@weavecode/weave 0.1.7 → 0.1.9

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 (431) hide show
  1. package/node_modules/@modelcontextprotocol/sdk/node_modules/@hono/node-server/dist/index.cjs +31 -6
  2. package/node_modules/@modelcontextprotocol/sdk/node_modules/@hono/node-server/dist/index.mjs +31 -6
  3. package/node_modules/@modelcontextprotocol/sdk/node_modules/@hono/node-server/package.json +1 -1
  4. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/dist/index.cjs +1 -1
  5. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/dist/index.mjs +1 -1
  6. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/node_modules/ip-address/README.md +39 -39
  7. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/node_modules/ip-address/dist/ipv6.js +1 -1
  8. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/node_modules/ip-address/package.json +1 -1
  9. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/package.json +1 -1
  10. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/adapter/bun/serve-static.js +1 -1
  11. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/adapter/bun/websocket.js +1 -1
  12. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/context.js +1 -1
  13. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/helper/css/common.js +3 -5
  14. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/hono-base.js +4 -10
  15. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/hono.js +0 -1
  16. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/middleware/basic-auth/index.js +1 -1
  17. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +1 -1
  18. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/middleware/csrf/index.js +2 -2
  19. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +2 -2
  20. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +2 -2
  21. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/middleware/mount/index.js +4 -4
  22. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +13 -11
  23. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/router/reg-exp-router/prepared-router.js +4 -4
  24. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +39 -51
  25. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +15 -5
  26. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/utils/jwt/jwt.js +2 -1
  27. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/utils/mount.js +35 -0
  28. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/utils/url.js +10 -0
  29. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/cjs/validator/validator.js +1 -1
  30. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/context.js +1 -1
  31. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/helper/css/common.js +3 -5
  32. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/hono-base.js +4 -10
  33. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/hono.js +0 -1
  34. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/basic-auth/index.js +1 -1
  35. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/bearer-auth/index.js +1 -1
  36. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/csrf/index.js +2 -2
  37. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/jwk/jwk.js +2 -2
  38. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/jwt/jwt.js +2 -2
  39. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/language/language.js +1 -1
  40. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/middleware/mount/index.js +4 -4
  41. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/router/reg-exp-router/node.js +13 -11
  42. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/router/reg-exp-router/prepared-router.js +5 -5
  43. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/router/reg-exp-router/router.js +40 -52
  44. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/router/reg-exp-router/trie.js +15 -5
  45. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/types/middleware/mount/index.d.ts +1 -0
  46. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -3
  47. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +1 -1
  48. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/types/utils/mount.d.ts +4 -0
  49. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/types/utils/url.d.ts +1 -0
  50. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/utils/jwt/jwt.js +2 -1
  51. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/utils/mount.js +34 -0
  52. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/utils/url.js +10 -1
  53. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/dist/validator/validator.js +1 -1
  54. package/node_modules/@modelcontextprotocol/sdk/node_modules/hono/package.json +1 -1
  55. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/webapi/jwks/remote.js +3 -1
  56. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/webapi/lib/deflate.js +2 -1
  57. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +2 -1
  58. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/package.json +1 -1
  59. package/node_modules/@weavecode/cli/dist/cli.js +4 -4
  60. package/node_modules/@weavecode/cli/dist/commands/chpass.js +1 -1
  61. package/node_modules/@weavecode/cli/dist/commands/daemon-auth.js +1 -1
  62. package/node_modules/@weavecode/cli/dist/commands/daemon.js +2 -2
  63. package/node_modules/@weavecode/cli/dist/commands/dashboard.js +2 -2
  64. package/node_modules/@weavecode/cli/dist/commands/doctor.js +1 -1
  65. package/node_modules/@weavecode/cli/dist/commands/init.js +1 -1
  66. package/node_modules/@weavecode/cli/dist/commands/scan.js +1 -1
  67. package/node_modules/@weavecode/cli/dist/commands/status.js +2 -2
  68. package/node_modules/@weavecode/cli/dist/commands/uninstall.js +1 -1
  69. package/node_modules/@weavecode/cli/dist/i18n/dict/en.js +3 -3
  70. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.d.ts +7 -0
  71. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.js +3 -3
  72. package/node_modules/@weavecode/cli/dist/i18n/index.js +1 -1
  73. package/node_modules/@weavecode/cli/dist/i18n/t.js +1 -1
  74. package/node_modules/@weavecode/cli/dist/init/claudemd-gen.js +5 -5
  75. package/node_modules/@weavecode/cli/dist/init/executor.js +3 -3
  76. package/node_modules/@weavecode/cli/dist/init/git-commit.js +2 -2
  77. package/node_modules/@weavecode/cli/dist/init/git-exclude.d.ts +20 -0
  78. package/node_modules/@weavecode/cli/dist/init/git-exclude.js +7 -0
  79. package/node_modules/@weavecode/cli/dist/init/harnesses/claude.js +7 -7
  80. package/node_modules/@weavecode/cli/dist/init/harnesses/codex.js +1 -1
  81. package/node_modules/@weavecode/cli/dist/init/instruction-files.d.ts +14 -0
  82. package/node_modules/@weavecode/cli/dist/init/instruction-files.js +1 -0
  83. package/node_modules/@weavecode/cli/dist/init/instruction-link.js +1 -1
  84. package/node_modules/@weavecode/cli/dist/init/plugin-templates.js +1 -1
  85. package/node_modules/@weavecode/cli/dist/init/prompt.js +4 -4
  86. package/node_modules/@weavecode/cli/dist/init/provision.js +1 -1
  87. package/node_modules/@weavecode/cli/dist/init/uninstall.js +6 -6
  88. package/node_modules/@weavecode/cli/dist/init/wiring.js +1 -1
  89. package/node_modules/@weavecode/cli/dist/parser.js +1 -1
  90. package/node_modules/@weavecode/cli/dist/statusline/ansi.js +1 -1
  91. package/node_modules/@weavecode/cli/package.json +5 -5
  92. package/node_modules/@weavecode/core/dist/harness-checks.js +1 -1
  93. package/node_modules/@weavecode/core/dist/harness.d.ts +115 -0
  94. package/node_modules/@weavecode/core/dist/harness.js +1 -1
  95. package/node_modules/@weavecode/core/dist/index.d.ts +1 -1
  96. package/node_modules/@weavecode/core/dist/index.js +1 -1
  97. package/node_modules/@weavecode/core/dist/platform.js +1 -1
  98. package/node_modules/@weavecode/core/dist/types.d.ts +4 -4
  99. package/node_modules/@weavecode/core/dist/types.js +1 -1
  100. package/node_modules/@weavecode/core/dist/utils/fs.js +1 -1
  101. package/node_modules/@weavecode/core/package.json +1 -1
  102. package/node_modules/@weavecode/harness/dist/claude/assets.js +3 -3
  103. package/node_modules/@weavecode/harness/dist/claude/capture.js +9 -9
  104. package/node_modules/@weavecode/harness/dist/claude/commands.d.ts +2 -0
  105. package/node_modules/@weavecode/harness/dist/claude/commands.js +3 -2
  106. package/node_modules/@weavecode/harness/dist/claude/config.js +2 -2
  107. package/node_modules/@weavecode/harness/dist/claude/detect.js +1 -1
  108. package/node_modules/@weavecode/harness/dist/claude/edits.js +1 -1
  109. package/node_modules/@weavecode/harness/dist/claude/event-source.js +1 -1
  110. package/node_modules/@weavecode/harness/dist/claude/images.js +2 -2
  111. package/node_modules/@weavecode/harness/dist/claude/index.js +1 -1
  112. package/node_modules/@weavecode/harness/dist/claude/liveness.js +1 -1
  113. package/node_modules/@weavecode/harness/dist/claude/mcp-gen.js +1 -1
  114. package/node_modules/@weavecode/harness/dist/claude/messaging.js +2 -2
  115. package/node_modules/@weavecode/harness/dist/claude/model-aliases.js +1 -1
  116. package/node_modules/@weavecode/harness/dist/claude/narration.js +1 -1
  117. package/node_modules/@weavecode/harness/dist/claude/peers.js +1 -1
  118. package/node_modules/@weavecode/harness/dist/claude/plan.js +1 -1
  119. package/node_modules/@weavecode/harness/dist/claude/plugins.js +4 -4
  120. package/node_modules/@weavecode/harness/dist/claude/prompt-inbox.js +1 -1
  121. package/node_modules/@weavecode/harness/dist/claude/prompt.js +3 -3
  122. package/node_modules/@weavecode/harness/dist/claude/questions.js +2 -2
  123. package/node_modules/@weavecode/harness/dist/claude/resume.js +1 -1
  124. package/node_modules/@weavecode/harness/dist/claude/rewind.js +1 -1
  125. package/node_modules/@weavecode/harness/dist/claude/settings-gen.js +1 -1
  126. package/node_modules/@weavecode/harness/dist/claude/side-question.js +2 -2
  127. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.d.ts +6 -0
  128. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.js +21 -14
  129. package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.js +1 -1
  130. package/node_modules/@weavecode/harness/dist/claude/statusline/surface.js +2 -2
  131. package/node_modules/@weavecode/harness/dist/claude/transcript-cursor.js +2 -2
  132. package/node_modules/@weavecode/harness/dist/claude/transcript.js +7 -7
  133. package/node_modules/@weavecode/harness/dist/codex/assets.js +1 -1
  134. package/node_modules/@weavecode/harness/dist/codex/capture.js +2 -2
  135. package/node_modules/@weavecode/harness/dist/codex/commands.js +4 -4
  136. package/node_modules/@weavecode/harness/dist/codex/config.js +2 -2
  137. package/node_modules/@weavecode/harness/dist/codex/detect.js +1 -1
  138. package/node_modules/@weavecode/harness/dist/codex/event-source.js +2 -2
  139. package/node_modules/@weavecode/harness/dist/codex/images.js +1 -1
  140. package/node_modules/@weavecode/harness/dist/codex/index.js +1 -1
  141. package/node_modules/@weavecode/harness/dist/codex/liveness.js +1 -1
  142. package/node_modules/@weavecode/harness/dist/codex/mcp.js +1 -1
  143. package/node_modules/@weavecode/harness/dist/codex/messaging.js +2 -2
  144. package/node_modules/@weavecode/harness/dist/codex/narration.js +1 -1
  145. package/node_modules/@weavecode/harness/dist/codex/plugin-state.js +1 -1
  146. package/node_modules/@weavecode/harness/dist/codex/plugins.js +3 -3
  147. package/node_modules/@weavecode/harness/dist/codex/provision.js +1 -1
  148. package/node_modules/@weavecode/harness/dist/codex/questions.js +2 -2
  149. package/node_modules/@weavecode/harness/dist/codex/rollout.js +5 -5
  150. package/node_modules/@weavecode/harness/dist/codex/side-question.js +4 -4
  151. package/node_modules/@weavecode/harness/dist/codex/titles.js +4 -4
  152. package/node_modules/@weavecode/harness/dist/codex/toml.js +13 -13
  153. package/node_modules/@weavecode/harness/dist/codex/transcript.js +2 -2
  154. package/node_modules/@weavecode/harness/dist/driver.d.ts +6 -0
  155. package/node_modules/@weavecode/harness/dist/driver.js +1 -1
  156. package/node_modules/@weavecode/harness/dist/init/generated.js +2 -2
  157. package/node_modules/@weavecode/harness/dist/init/pipeline.js +1 -1
  158. package/node_modules/@weavecode/harness/dist/registry-wiring.js +1 -1
  159. package/node_modules/@weavecode/harness/dist/shared/install-link.js +1 -1
  160. package/node_modules/@weavecode/harness/dist/shared/json-cap.js +1 -1
  161. package/node_modules/@weavecode/harness/package.json +2 -2
  162. package/node_modules/@weavecode/server/dist/config/dotted.js +1 -1
  163. package/node_modules/@weavecode/server/dist/config/profiles.js +2 -2
  164. package/node_modules/@weavecode/server/dist/daemon/ai-config.js +2 -2
  165. package/node_modules/@weavecode/server/dist/daemon/ai-context.js +6 -6
  166. package/node_modules/@weavecode/server/dist/daemon/ai-routes.js +4 -4
  167. package/node_modules/@weavecode/server/dist/daemon/api-log.js +3 -3
  168. package/node_modules/@weavecode/server/dist/daemon/auth-routes.js +1 -1
  169. package/node_modules/@weavecode/server/dist/daemon/auto-start.js +3 -3
  170. package/node_modules/@weavecode/server/dist/daemon/capture-routes.js +1 -1
  171. package/node_modules/@weavecode/server/dist/daemon/config-routes.js +1 -1
  172. package/node_modules/@weavecode/server/dist/daemon/cron.js +1 -1
  173. package/node_modules/@weavecode/server/dist/daemon/dashboard-auth.js +2 -2
  174. package/node_modules/@weavecode/server/dist/daemon/dashboard-pusher.js +1 -1
  175. package/node_modules/@weavecode/server/dist/daemon/decision-config.js +2 -2
  176. package/node_modules/@weavecode/server/dist/daemon/dev-entry.js +1 -1
  177. package/node_modules/@weavecode/server/dist/daemon/entry.js +2 -2
  178. package/node_modules/@weavecode/server/dist/daemon/fs-browser.js +1 -1
  179. package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.js +1 -1
  180. package/node_modules/@weavecode/server/dist/daemon/hooks-routes.js +1 -1
  181. package/node_modules/@weavecode/server/dist/daemon/http.js +1 -1
  182. package/node_modules/@weavecode/server/dist/daemon/liveness.js +1 -1
  183. package/node_modules/@weavecode/server/dist/daemon/mcp-orchestration.js +1 -1
  184. package/node_modules/@weavecode/server/dist/daemon/narration-watcher.js +2 -2
  185. package/node_modules/@weavecode/server/dist/daemon/opener/backend-ghostty.js +1 -1
  186. package/node_modules/@weavecode/server/dist/daemon/opener/backend-iterm.js +1 -1
  187. package/node_modules/@weavecode/server/dist/daemon/opener/backend-konsole.js +2 -2
  188. package/node_modules/@weavecode/server/dist/daemon/opener/backend-terminal-app.js +1 -1
  189. package/node_modules/@weavecode/server/dist/daemon/opener/backend-wezterm.js +1 -1
  190. package/node_modules/@weavecode/server/dist/daemon/opener/backend-wt.js +1 -1
  191. package/node_modules/@weavecode/server/dist/daemon/opener/backend.js +1 -1
  192. package/node_modules/@weavecode/server/dist/daemon/opener/pty-terminals.js +1 -1
  193. package/node_modules/@weavecode/server/dist/daemon/opener/shared.js +1 -1
  194. package/node_modules/@weavecode/server/dist/daemon/opener/wezterm-mux.js +1 -1
  195. package/node_modules/@weavecode/server/dist/daemon/opener/wt.js +3 -3
  196. package/node_modules/@weavecode/server/dist/daemon/opener.js +1 -1
  197. package/node_modules/@weavecode/server/dist/daemon/pet-seed.d.ts +4 -0
  198. package/node_modules/@weavecode/server/dist/daemon/pet-seed.js +2 -0
  199. package/node_modules/@weavecode/server/dist/daemon/pet-window.d.ts +17 -0
  200. package/node_modules/@weavecode/server/dist/daemon/pet-window.js +2 -0
  201. package/node_modules/@weavecode/server/dist/daemon/port.js +1 -1
  202. package/node_modules/@weavecode/server/dist/daemon/project-files.d.ts +1 -1
  203. package/node_modules/@weavecode/server/dist/daemon/project-files.js +2 -2
  204. package/node_modules/@weavecode/server/dist/daemon/project-harnesses.js +2 -2
  205. package/node_modules/@weavecode/server/dist/daemon/project-updates-refresher.js +1 -1
  206. package/node_modules/@weavecode/server/dist/daemon/prompt-images.js +1 -1
  207. package/node_modules/@weavecode/server/dist/daemon/routes/hooks-admin.js +1 -1
  208. package/node_modules/@weavecode/server/dist/daemon/routes/installations.d.ts +29 -0
  209. package/node_modules/@weavecode/server/dist/daemon/routes/installations.js +1 -0
  210. package/node_modules/@weavecode/server/dist/daemon/routes/market.js +1 -1
  211. package/node_modules/@weavecode/server/dist/daemon/routes/orchestration.js +1 -1
  212. package/node_modules/@weavecode/server/dist/daemon/routes/path-refs.js +1 -1
  213. package/node_modules/@weavecode/server/dist/daemon/routes/pets.d.ts +3 -0
  214. package/node_modules/@weavecode/server/dist/daemon/routes/pets.js +1 -0
  215. package/node_modules/@weavecode/server/dist/daemon/routes/project-files.js +1 -1
  216. package/node_modules/@weavecode/server/dist/daemon/routes/projects.js +1 -1
  217. package/node_modules/@weavecode/server/dist/daemon/routes/search.js +1 -1
  218. package/node_modules/@weavecode/server/dist/daemon/routes/session-control.js +1 -1
  219. package/node_modules/@weavecode/server/dist/daemon/routes/session-insights.js +1 -1
  220. package/node_modules/@weavecode/server/dist/daemon/routes/settings-statusline.js +1 -1
  221. package/node_modules/@weavecode/server/dist/daemon/routes/uploads.js +1 -1
  222. package/node_modules/@weavecode/server/dist/daemon/routes/visualizations.js +2 -1
  223. package/node_modules/@weavecode/server/dist/daemon/routes/watches.js +1 -1
  224. package/node_modules/@weavecode/server/dist/daemon/server.d.ts +4 -0
  225. package/node_modules/@weavecode/server/dist/daemon/server.js +8 -8
  226. package/node_modules/@weavecode/server/dist/daemon/session-open.js +1 -1
  227. package/node_modules/@weavecode/server/dist/daemon/session-probe.js +1 -1
  228. package/node_modules/@weavecode/server/dist/daemon/settings.d.ts +21 -0
  229. package/node_modules/@weavecode/server/dist/daemon/settings.js +2 -2
  230. package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.js +1 -1
  231. package/node_modules/@weavecode/server/dist/daemon/sse-hub.js +1 -1
  232. package/node_modules/@weavecode/server/dist/daemon/start.js +1 -1
  233. package/node_modules/@weavecode/server/dist/daemon/state.js +2 -2
  234. package/node_modules/@weavecode/server/dist/daemon/static.js +1 -1
  235. package/node_modules/@weavecode/server/dist/daemon/terminal-topology-refresher.js +1 -1
  236. package/node_modules/@weavecode/server/dist/daemon/tls.js +1 -1
  237. package/node_modules/@weavecode/server/dist/daemon/toolkit-routes.js +1 -1
  238. package/node_modules/@weavecode/server/dist/daemon/upload-sessions.js +1 -1
  239. package/node_modules/@weavecode/server/dist/db/db.d.ts +4 -1
  240. package/node_modules/@weavecode/server/dist/db/db.js +2 -2
  241. package/node_modules/@weavecode/server/dist/db/layout.d.ts +5 -0
  242. package/node_modules/@weavecode/server/dist/db/layout.js +2 -1
  243. package/node_modules/@weavecode/server/dist/db/paths.d.ts +9 -0
  244. package/node_modules/@weavecode/server/dist/db/paths.js +1 -1
  245. package/node_modules/@weavecode/server/dist/hooks/answer-keys.js +1 -1
  246. package/node_modules/@weavecode/server/dist/hooks/dashboard-rate.js +1 -1
  247. package/node_modules/@weavecode/server/dist/hooks/decision-judge.js +1 -1
  248. package/node_modules/@weavecode/server/dist/hooks/delivery/ack-gate.js +1 -1
  249. package/node_modules/@weavecode/server/dist/hooks/delivery/channels.js +1 -1
  250. package/node_modules/@weavecode/server/dist/hooks/delivery/keys.js +1 -1
  251. package/node_modules/@weavecode/server/dist/hooks/delivery/queue.js +1 -1
  252. package/node_modules/@weavecode/server/dist/hooks/delivery/relay.js +1 -1
  253. package/node_modules/@weavecode/server/dist/hooks/delivery/send.js +2 -2
  254. package/node_modules/@weavecode/server/dist/hooks/delivery/views.js +1 -1
  255. package/node_modules/@weavecode/server/dist/hooks/dispatcher.js +6 -6
  256. package/node_modules/@weavecode/server/dist/hooks/edge-extract.js +1 -1
  257. package/node_modules/@weavecode/server/dist/hooks/handlers/webhook.js +1 -1
  258. package/node_modules/@weavecode/server/dist/hooks/headless-runner.js +2 -2
  259. package/node_modules/@weavecode/server/dist/hooks/inbox-socket.js +2 -2
  260. package/node_modules/@weavecode/server/dist/hooks/inbox-worker.js +3 -3
  261. package/node_modules/@weavecode/server/dist/hooks/match.js +1 -1
  262. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.js +5 -5
  263. package/node_modules/@weavecode/server/dist/hooks/mirror.js +4 -4
  264. package/node_modules/@weavecode/server/dist/hooks/orchestration-engine.js +7 -7
  265. package/node_modules/@weavecode/server/dist/hooks/orchestration-graph.js +1 -1
  266. package/node_modules/@weavecode/server/dist/hooks/plugin-installer.js +3 -3
  267. package/node_modules/@weavecode/server/dist/hooks/plugin-loader.js +1 -1
  268. package/node_modules/@weavecode/server/dist/hooks/plugin-project-state.js +1 -1
  269. package/node_modules/@weavecode/server/dist/hooks/prompt-flusher.js +1 -1
  270. package/node_modules/@weavecode/server/dist/hooks/promptbox.js +8 -8
  271. package/node_modules/@weavecode/server/dist/hooks/push-notify.js +2 -2
  272. package/node_modules/@weavecode/server/dist/hooks/question-tracker.js +1 -1
  273. package/node_modules/@weavecode/server/dist/hooks/registry.js +1 -1
  274. package/node_modules/@weavecode/server/dist/hooks/resume-runner.js +1 -1
  275. package/node_modules/@weavecode/server/dist/hooks/session-purge.js +1 -1
  276. package/node_modules/@weavecode/server/dist/hooks/side-questions.js +1 -1
  277. package/node_modules/@weavecode/server/dist/hooks/synthetic-sessions.js +1 -1
  278. package/node_modules/@weavecode/server/dist/hooks/toolkit-scan.js +1 -1
  279. package/node_modules/@weavecode/server/dist/hooks/toolkit.js +5 -5
  280. package/node_modules/@weavecode/server/dist/hooks/watch-engine.js +1 -1
  281. package/node_modules/@weavecode/server/dist/index.d.ts +1 -1
  282. package/node_modules/@weavecode/server/dist/index.js +1 -1
  283. package/node_modules/@weavecode/server/dist/install/apply-update.js +1 -1
  284. package/node_modules/@weavecode/server/dist/install/capabilities.js +1 -1
  285. package/node_modules/@weavecode/server/dist/install/codex-toml.js +1 -1
  286. package/node_modules/@weavecode/server/dist/install/file-assets.js +1 -1
  287. package/node_modules/@weavecode/server/dist/install/fingerprint.js +1 -1
  288. package/node_modules/@weavecode/server/dist/install/global-config.js +1 -1
  289. package/node_modules/@weavecode/server/dist/install/global-file-assets.js +1 -1
  290. package/node_modules/@weavecode/server/dist/install/init-files.d.ts +0 -9
  291. package/node_modules/@weavecode/server/dist/install/init-files.js +2 -2
  292. package/node_modules/@weavecode/server/dist/install/install-ledger.js +1 -1
  293. package/node_modules/@weavecode/server/dist/install/installer.js +1 -1
  294. package/node_modules/@weavecode/server/dist/install/library-index.js +1 -1
  295. package/node_modules/@weavecode/server/dist/install/mcp-rows.js +1 -1
  296. package/node_modules/@weavecode/server/dist/install/plugin-install.js +1 -1
  297. package/node_modules/@weavecode/server/dist/install/plugin-rows.js +1 -1
  298. package/node_modules/@weavecode/server/dist/install/plugin-templates.js +1 -1
  299. package/node_modules/@weavecode/server/dist/install/project-config.js +1 -1
  300. package/node_modules/@weavecode/server/dist/install/reader.js +1 -1
  301. package/node_modules/@weavecode/server/dist/install/scan-cache.js +1 -1
  302. package/node_modules/@weavecode/server/dist/install/scanner.js +1 -1
  303. package/node_modules/@weavecode/server/dist/install/settings-mcp.js +2 -2
  304. package/node_modules/@weavecode/server/dist/install/updates.js +1 -1
  305. package/node_modules/@weavecode/server/dist/market/builtin.js +1 -1
  306. package/node_modules/@weavecode/server/dist/market/identity.js +1 -1
  307. package/node_modules/@weavecode/server/dist/market/import.js +2 -2
  308. package/node_modules/@weavecode/server/dist/market/refresh.js +1 -1
  309. package/node_modules/@weavecode/server/dist/market/resolve.js +1 -1
  310. package/node_modules/@weavecode/server/dist/market/updates.js +1 -1
  311. package/node_modules/@weavecode/server/dist/registry.js +2 -2
  312. package/node_modules/@weavecode/server/dist/repositories/hook-dispatches.js +4 -4
  313. package/node_modules/@weavecode/server/dist/repositories/hook-events.d.ts +6 -0
  314. package/node_modules/@weavecode/server/dist/repositories/hook-events.js +8 -5
  315. package/node_modules/@weavecode/server/dist/repositories/hook-rules.js +4 -4
  316. package/node_modules/@weavecode/server/dist/repositories/libraries.js +1 -1
  317. package/node_modules/@weavecode/server/dist/repositories/orchestrations.js +7 -7
  318. package/node_modules/@weavecode/server/dist/repositories/plugin-states.js +1 -1
  319. package/node_modules/@weavecode/server/dist/repositories/projects.js +1 -1
  320. package/node_modules/@weavecode/server/dist/repositories/push-subscriptions.js +2 -2
  321. package/node_modules/@weavecode/server/dist/repositories/session-watches.js +4 -4
  322. package/node_modules/@weavecode/server/dist/repositories/sessions.js +2 -2
  323. package/node_modules/@weavecode/server/dist/repositories/uploads.js +11 -11
  324. package/node_modules/@weavecode/server/dist/statusline/cache-sweeper.js +1 -1
  325. package/node_modules/@weavecode/server/dist/statusline/generator.js +1 -1
  326. package/node_modules/@weavecode/server/dist/statusline/manager.js +3 -3
  327. package/node_modules/@weavecode/server/dist/surfaces.js +1 -1
  328. package/node_modules/@weavecode/server/dist/watch/cache.js +1 -1
  329. package/node_modules/@weavecode/server/dist/watch/classifier.js +1 -1
  330. package/node_modules/@weavecode/server/dist/watch/plugin-mods.js +1 -1
  331. package/node_modules/@weavecode/server/dist/watch/sync-scheduler.js +1 -1
  332. package/node_modules/@weavecode/server/dist/watch/watch-service.js +1 -1
  333. package/node_modules/@weavecode/server/package.json +4 -4
  334. package/node_modules/@weavecode/server/web/dist/assets/abnfDiagram-WEYOIA3C-S1lL1V_n.js +1 -0
  335. package/node_modules/@weavecode/server/web/dist/assets/{arc-CRZTRyjb.js → arc-CoTNHyHV.js} +1 -1
  336. package/node_modules/@weavecode/server/web/dist/assets/architectureDiagram-ZHVYKT53-C6ttfnzW.js +36 -0
  337. package/node_modules/@weavecode/server/web/dist/assets/{blockDiagram-RJAE7QA3-BIH8mSIZ.js → blockDiagram-RJAE7QA3-CYr3Cpu9.js} +2 -2
  338. package/node_modules/@weavecode/server/web/dist/assets/{c4Diagram-XDPPPE54-5YoKWFOD.js → c4Diagram-XDPPPE54-BFS7PhLD.js} +2 -2
  339. package/node_modules/@weavecode/server/web/dist/assets/channel-DxQHp3Yc.js +1 -0
  340. package/node_modules/@weavecode/server/web/dist/assets/{chunk-2E7WHVMD-B653kMXt.js → chunk-2E7WHVMD-DJ7X_Shu.js} +1 -1
  341. package/node_modules/@weavecode/server/web/dist/assets/{chunk-2Q5K7J3B-f_A4_eDz.js → chunk-2Q5K7J3B-B8nr2rdz.js} +1 -1
  342. package/node_modules/@weavecode/server/web/dist/assets/{chunk-4EMNKR2T-v5j5qNGj.js → chunk-4EMNKR2T-DmnZmUMU.js} +1 -1
  343. package/node_modules/@weavecode/server/web/dist/assets/chunk-55XCU76K-Xz9a8dmp.js +1 -0
  344. package/node_modules/@weavecode/server/web/dist/assets/{chunk-5VM5RSS4-BeROhLXN.js → chunk-5VM5RSS4-Dg9mg7pf.js} +1 -1
  345. package/node_modules/@weavecode/server/web/dist/assets/{chunk-JWPE2WC7-D6dXb_jr.js → chunk-JWPE2WC7-CmjTZO8o.js} +1 -1
  346. package/node_modules/@weavecode/server/web/dist/assets/{chunk-VQ5W62QI-CnHdSEMi.js → chunk-VQ5W62QI-D3YDdJ3E.js} +1 -1
  347. package/node_modules/@weavecode/server/web/dist/assets/chunk-XXDRQBXY-s1OG9sqW.js +1 -0
  348. package/node_modules/@weavecode/server/web/dist/assets/chunk-ZCHMQSOR-C3hINQU3.js +2 -0
  349. package/node_modules/@weavecode/server/web/dist/assets/{classDiagram-v2-4MYWR3UA-DQe1b_k8.js → classDiagram-v2-4MYWR3UA-BTsbYrVl.js} +2 -2
  350. package/node_modules/@weavecode/server/web/dist/assets/cose-bilkent-JH36ORCC-C0W8Y-jI.js +1 -0
  351. package/node_modules/@weavecode/server/web/dist/assets/{cynefin-EF2NZ3EQ-OZotFKUu.js → cynefin-EF2NZ3EQ-stAu4Rh-.js} +1 -1
  352. package/node_modules/@weavecode/server/web/dist/assets/cynefinDiagram-WF24UDRP-CqYibiiv.js +62 -0
  353. package/node_modules/@weavecode/server/web/dist/assets/dagre-NK4GIZVX-qqcoJAqB.js +4 -0
  354. package/node_modules/@weavecode/server/web/dist/assets/diagram-CBK3MWTC-D0YTopb7.js +24 -0
  355. package/node_modules/@weavecode/server/web/dist/assets/diagram-I5XIIV2D-Csvw6aYr.js +24 -0
  356. package/node_modules/@weavecode/server/web/dist/assets/{diagram-ODIL2UCW-D9A6W8oB.js → diagram-ODIL2UCW-Dl0tYLkF.js} +2 -2
  357. package/node_modules/@weavecode/server/web/dist/assets/{diagram-PFBEJUXE-PeI-eyu2.js → diagram-PFBEJUXE-BCPSWZPe.js} +2 -2
  358. package/node_modules/@weavecode/server/web/dist/assets/diagram-RJA3I4VV-ETg-BrAJ.js +41 -0
  359. package/node_modules/@weavecode/server/web/dist/assets/{diagram-TYUNFSNR-D5KieEw5.js → diagram-TYUNFSNR-_M6ZxBG7.js} +2 -2
  360. package/node_modules/@weavecode/server/web/dist/assets/ebnfDiagram-V4P3TTPT-C-e_W7qx.js +1 -0
  361. package/node_modules/@weavecode/server/web/dist/assets/{elk-QONTBCTQ-D3xrVg0U.js → elk-QONTBCTQ-B5TBharB.js} +3 -3
  362. package/node_modules/@weavecode/server/web/dist/assets/{erDiagram-KYQIGQAA-y_V1cor7.js → erDiagram-KYQIGQAA-D08rj2pv.js} +2 -2
  363. package/node_modules/@weavecode/server/web/dist/assets/{flowDiagram-IUGLLWBU-BJ8Z0MkF.js → flowDiagram-IUGLLWBU-BALTxsKh.js} +1 -1
  364. package/node_modules/@weavecode/server/web/dist/assets/ganttDiagram-R7GPNTJY-Cts193jT.js +292 -0
  365. package/node_modules/@weavecode/server/web/dist/assets/gitGraphDiagram-PDYL7XHY-L9qYUX0v.js +106 -0
  366. package/node_modules/@weavecode/server/web/dist/assets/infoDiagram-MVDRKAX5-Bv47rgU0.js +2 -0
  367. package/node_modules/@weavecode/server/web/dist/assets/{ishikawaDiagram-HPEHV2GE-ChEnND76.js → ishikawaDiagram-HPEHV2GE-Da8_s6cs.js} +2 -2
  368. package/node_modules/@weavecode/server/web/dist/assets/{journeyDiagram-BOSHW7O7-DHcNtpYF.js → journeyDiagram-BOSHW7O7-Bj3CjZOf.js} +2 -2
  369. package/node_modules/@weavecode/server/web/dist/assets/{kanban-definition-FNSA4HC5-Dft7qpW5.js → kanban-definition-FNSA4HC5-D1hicmMu.js} +2 -2
  370. package/node_modules/@weavecode/server/web/dist/assets/linear-CWQg2iA1.js +1 -0
  371. package/node_modules/@weavecode/server/web/dist/assets/main-XLn3EZFh.css +11 -0
  372. package/node_modules/@weavecode/server/web/dist/assets/main-bsb0lQoL.js +1096 -0
  373. package/node_modules/@weavecode/server/web/dist/assets/{mermaid.core-D_Dp_Kl2.js → mermaid.core-C-wP0Zg2.js} +4 -4
  374. package/node_modules/@weavecode/server/web/dist/assets/{mindmap-definition-6OY57UG3-CM6OAd_-.js → mindmap-definition-6OY57UG3-Dk0VKqre.js} +2 -2
  375. package/node_modules/@weavecode/server/web/dist/assets/modulepreload-polyfill-B5Qt9EMX.js +1 -0
  376. package/node_modules/@weavecode/server/web/dist/assets/pegDiagram-ECIUJNBX-Bp1K_F9z.js +1 -0
  377. package/node_modules/@weavecode/server/web/dist/assets/pet-J6izA90s.js +1 -0
  378. package/node_modules/@weavecode/server/web/dist/assets/pieDiagram-GNMPY74T-BZ4rGy3t.js +39 -0
  379. package/node_modules/@weavecode/server/web/dist/assets/{quadrantDiagram-D57K4MC3-D8pQ_PU2.js → quadrantDiagram-D57K4MC3-B4N5KRsR.js} +4 -4
  380. package/node_modules/@weavecode/server/web/dist/assets/railroadDiagram-Y72NRBL7-C8nx8oHf.js +1 -0
  381. package/node_modules/@weavecode/server/web/dist/assets/{requirementDiagram-EQYSW5RA-C90DU5Ny.js → requirementDiagram-EQYSW5RA-VSH57d0d.js} +2 -2
  382. package/node_modules/@weavecode/server/web/dist/assets/{sankeyDiagram-SY24JHO5-B_poYGgJ.js → sankeyDiagram-SY24JHO5-jUb1hcya.js} +2 -2
  383. package/node_modules/@weavecode/server/web/dist/assets/{sequenceDiagram-BLEHNHQU-DDW7b2Dn.js → sequenceDiagram-BLEHNHQU-E2BioCNi.js} +2 -2
  384. package/node_modules/@weavecode/server/web/dist/assets/sizeCapture-INFHLROL-BdKrbRmI.js +1 -0
  385. package/node_modules/@weavecode/server/web/dist/assets/{stateDiagram-v2-A6IESKWZ-CkVMAyxG.js → stateDiagram-v2-A6IESKWZ-tFz7uNM7.js} +2 -2
  386. package/node_modules/@weavecode/server/web/dist/assets/swimlanes-B3ENRKFQ-B9BAPDWw.js +1 -0
  387. package/node_modules/@weavecode/server/web/dist/assets/swimlanesDiagram-AYQRV4AK-DQ2NPLl9.js +8 -0
  388. package/node_modules/@weavecode/server/web/dist/assets/{timeline-definition-JXYKGLGB-DnhkRGLb.js → timeline-definition-JXYKGLGB-CofNnFTK.js} +2 -2
  389. package/node_modules/@weavecode/server/web/dist/assets/{usecaseDiagram-WPOLBNNX-_TdIjC6Z.js → usecaseDiagram-WPOLBNNX-DOvjhJjJ.js} +2 -2
  390. package/node_modules/@weavecode/server/web/dist/assets/{vennDiagram-Q3Y3IDCW-FzgsymXB.js → vennDiagram-Q3Y3IDCW-BcFHWnhS.js} +2 -2
  391. package/node_modules/@weavecode/server/web/dist/assets/{wardleyDiagram-IVAIDKWA-BwqimStC.js → wardleyDiagram-IVAIDKWA-Bz2aqRAs.js} +2 -2
  392. package/node_modules/@weavecode/server/web/dist/assets/{xychartDiagram-4URNHDU4-CFjjVPIV.js → xychartDiagram-4URNHDU4-DpNF6Vsq.js} +2 -2
  393. package/node_modules/@weavecode/server/web/dist/index.html +3 -2
  394. package/node_modules/@weavecode/server/web/dist/pet.html +130 -0
  395. package/node_modules/@weavecode/templates/dist/index.d.ts +2 -0
  396. package/node_modules/@weavecode/templates/dist/index.js +1 -1
  397. package/node_modules/@weavecode/templates/dist/resources/pets/weave-spider/pet.json +7 -0
  398. package/node_modules/@weavecode/templates/dist/resources/pets/weave-spider/spritesheet.png +0 -0
  399. package/node_modules/@weavecode/templates/dist/resources/weave-market/.claude-plugin/marketplace.json +6 -0
  400. package/node_modules/@weavecode/templates/dist/resources/weave-market/plugins/core-skills/weave-pet-hatcher/.claude-plugin/plugin.json +8 -0
  401. package/node_modules/@weavecode/templates/dist/resources/weave-market/plugins/core-skills/weave-pet-hatcher/skills/weave-pet-hatcher/SKILL.md +121 -0
  402. package/node_modules/@weavecode/templates/dist/resources/weave-market/plugins/core-skills/weave-pet-hatcher/skills/weave-pet-hatcher/scripts/assemble-pet.mjs +308 -0
  403. package/node_modules/@weavecode/templates/dist/resources/weave-market/plugins/core-skills/weave-pet-hatcher/weave.json +4 -0
  404. package/node_modules/@weavecode/templates/package.json +2 -2
  405. package/package.json +11 -7
  406. package/scripts/preinstall.mjs +108 -0
  407. package/node_modules/@weavecode/server/web/dist/assets/abnfDiagram-WEYOIA3C-DDeQkTf5.js +0 -1
  408. package/node_modules/@weavecode/server/web/dist/assets/architectureDiagram-ZHVYKT53-Dj54DE3O.js +0 -36
  409. package/node_modules/@weavecode/server/web/dist/assets/channel-5VM93AJ8.js +0 -1
  410. package/node_modules/@weavecode/server/web/dist/assets/chunk-55XCU76K-CgEqeP7j.js +0 -1
  411. package/node_modules/@weavecode/server/web/dist/assets/chunk-XXDRQBXY-BkG_e6PE.js +0 -1
  412. package/node_modules/@weavecode/server/web/dist/assets/chunk-ZCHMQSOR-DxR-fxzs.js +0 -2
  413. package/node_modules/@weavecode/server/web/dist/assets/cose-bilkent-JH36ORCC-DThrA8S5.js +0 -1
  414. package/node_modules/@weavecode/server/web/dist/assets/cynefinDiagram-WF24UDRP-DJjq2hfT.js +0 -62
  415. package/node_modules/@weavecode/server/web/dist/assets/dagre-NK4GIZVX-C2vH93HT.js +0 -4
  416. package/node_modules/@weavecode/server/web/dist/assets/diagram-CBK3MWTC-DrF_ScM-.js +0 -24
  417. package/node_modules/@weavecode/server/web/dist/assets/diagram-I5XIIV2D-D1qoUJ4t.js +0 -24
  418. package/node_modules/@weavecode/server/web/dist/assets/diagram-RJA3I4VV-DITY0Rd8.js +0 -41
  419. package/node_modules/@weavecode/server/web/dist/assets/ebnfDiagram-V4P3TTPT-DDylK-gz.js +0 -1
  420. package/node_modules/@weavecode/server/web/dist/assets/ganttDiagram-R7GPNTJY-C0q-CS1T.js +0 -292
  421. package/node_modules/@weavecode/server/web/dist/assets/gitGraphDiagram-PDYL7XHY-BYTComiB.js +0 -106
  422. package/node_modules/@weavecode/server/web/dist/assets/index-BKxAxaVG.js +0 -1091
  423. package/node_modules/@weavecode/server/web/dist/assets/index-C0Sd4MH7.css +0 -11
  424. package/node_modules/@weavecode/server/web/dist/assets/infoDiagram-MVDRKAX5-DtixgjLP.js +0 -2
  425. package/node_modules/@weavecode/server/web/dist/assets/linear-DEo11t-X.js +0 -1
  426. package/node_modules/@weavecode/server/web/dist/assets/pegDiagram-ECIUJNBX-_QkY0h8Q.js +0 -1
  427. package/node_modules/@weavecode/server/web/dist/assets/pieDiagram-GNMPY74T-ZGr5vtDj.js +0 -39
  428. package/node_modules/@weavecode/server/web/dist/assets/railroadDiagram-Y72NRBL7-DUIo-WmA.js +0 -1
  429. package/node_modules/@weavecode/server/web/dist/assets/sizeCapture-INFHLROL-CmKoqX6K.js +0 -1
  430. package/node_modules/@weavecode/server/web/dist/assets/swimlanes-B3ENRKFQ-Dm_Omzky.js +0 -1
  431. package/node_modules/@weavecode/server/web/dist/assets/swimlanesDiagram-AYQRV4AK-Bjj8ZA4d.js +0 -8
@@ -1,3 +1,3 @@
1
- import{appendOutbox as m}from"../mirror.js";export function resolveChannel(e,t,s,o,n){if(s==="prompt")return n.prompt||n.queuedPrompt||t()?"prompt":{reason:n.unsupportedReasons?.prompt??`Harness "${n.harness}" has no native prompt channel and the configured terminal cannot inject text`};if(s==="inject"||s==="headless")return(s==="inject"?n.inject:n.headless)?s:{reason:unsupportedReason(n,s)};const i=e.turnOpenOf(o)?"inject":"headless";return(i==="inject"?n.inject:n.headless)?i:{reason:unsupportedReason(n,i)}}export function unsupportedReason(e,t){return e.unsupportedReasons?.[t]??`Harness "${e.harness}" does not support the ${t} messaging channel`}export function renderEnvelope(e,t,s){return s.renderEnvelope?.(e,t)??`[weave message] from=${t.fromLabel} at=${t.at} mode=${t.mode}
1
+ import{appendOutbox as p}from"../mirror.js";export function resolveChannel(e,t,n,o,s){if(n==="prompt")return s.prompt||s.queuedPrompt||t()?"prompt":{reason:s.unsupportedReasons?.prompt??`Harness "${s.harness}" has no native prompt channel and the configured terminal cannot inject text`};if(n==="inject"||n==="headless")return(n==="inject"?s.inject:s.headless)?n:{reason:unsupportedReason(s,n)};const i=e.turnOpenOf(o)?"inject":"headless";return(i==="inject"?s.inject:s.headless)?i:{reason:unsupportedReason(s,i)}}export function unsupportedReason(e,t){return e.unsupportedReasons?.[t]??`Harness "${e.harness}" does not support the ${t} messaging channel`}export function renderEnvelope(e,t,n){return n.renderEnvelope?.(e,t)??`[weave message] from=${t.fromLabel} at=${t.at} mode=${t.mode}
2
2
 
3
- ${e}`}export function deliverInject(e,t,s,o,n,a){const i=renderEnvelope(a,o,n);m(e.hooksDir,s,[{sessionId:t.sessionId,text:i}]);const r={...t,status:"injected",settledAt:new Date().toISOString()};return e.memory.set(r.id,r),e.bus.emit({kind:"message.delivered",messageId:r.id,sessionId:t.sessionId,at:new Date().toISOString()}),{ok:!0,messageId:r.id,status:"injected",message:r}}export function deliverHeadless(e,t,s,o,n,a){const i=n.headless,r={...t,status:"running"};e.memory.set(r.id,r);const u={settled:!1};try{e.headless.run({sessionId:r.sessionId,cwd:s,prompt:renderEnvelope(a,o,n),spec:i,bin:e.bin??process.env.WEAVE_CLAUDE_BIN??i.bin,timeoutMs:e.timeoutMs,onSettle:l=>p(e,r,u,l)})||p(e,r,u,{status:"failed",output:null,error:"Session already has a running run"})}catch(d){p(e,r,u,{status:"failed",output:null,error:d instanceof Error?d.message:String(d)})}return{ok:!0,messageId:r.id,status:"running",message:e.memory.get(r.id)}}function p(e,t,s,o){if(s.settled)return;s.settled=!0;const n=e.memory.get(t.id);if(n)try{e.memory.set(t.id,{...n,status:o.status,output:o.output,error:o.error,settledAt:new Date().toISOString()}),e.bus.emit({kind:"message.finished",messageId:t.id,sessionId:t.sessionId,status:o.status,at:new Date().toISOString()})}catch{}}
3
+ ${e}`}export function deliverInject(e,t,n,o,s,i){const d=renderEnvelope(i,o,s);p(e.hooksDir,n,[{sessionId:t.sessionId,text:d}]);const r={...t,status:"injected",settledAt:new Date().toISOString()};return e.memory.set(r.id,r),e.bus.emit({kind:"message.delivered",messageId:r.id,sessionId:t.sessionId,at:new Date().toISOString()}),{ok:!0,messageId:r.id,status:"injected",message:r}}export function deliverHeadless(e,t,n,o,s,i){const d=s.headless,r={...t,status:"running"};e.memory.set(r.id,r);const u={settled:!1};try{e.headless.run({sessionId:r.sessionId,cwd:n,prompt:renderEnvelope(i,o,s),spec:d,bin:e.bin??process.env.WEAVE_CLAUDE_BIN??d.bin,timeoutMs:e.timeoutMs,onSettle:a=>m(e,r,u,a)})||m(e,r,u,{status:"failed",output:null,error:"Session already has a running run"})}catch(a){m(e,r,u,{status:"failed",output:null,error:a instanceof Error?a.message:String(a)})}return{ok:!0,messageId:r.id,status:"running",message:e.memory.get(r.id)}}function m(e,t,n,o){if(n.settled)return;n.settled=!0;const s=e.memory.get(t.id);if(s)try{e.memory.set(t.id,{...s,status:o.status,output:o.output,error:o.error,settledAt:new Date().toISOString()}),e.bus.emit({kind:"message.finished",messageId:t.id,sessionId:t.sessionId,status:o.status,at:new Date().toISOString()})}catch{}}
@@ -1 +1 @@
1
- import{combinedLivenessOf as m}from"../../daemon/liveness.js";import{readAllPromptbox as p,readPromptbox as g}from"../promptbox.js";import{sessionJudgedDead as h}from"./queue.js";function x(n,e){return{id:e.id,sessionId:n,text:e.text,...e.attachments&&e.attachments.length>0?{attachments:e.attachments}:{},mode:"prompt",oneWay:e.oneWay===!0,...e.forceTerminal===!0?{forceTerminal:!0}:{},channel:"prompt",status:"pending",at:e.at}}export function listVisible(n,e,o,i,s,a=()=>!1){const f=Date.now(),u=[],c=n.sessions.getBySessionId(i);if(c?.projectId!=null){const t=n.projects.getById(c.projectId);if(t)for(const r of g(e,t.token))r.sessionId===i&&u.push(x(i,r))}for(const t of o.values()){if(t.sessionId!==i)continue;if(t.stalled===!0||t.status==="delivering"||t.status==="running"){u.push(t);continue}if(t.status==="failed"||t.status==="timeout"){u.push(t);continue}const r=t.settledAt?Date.parse(t.settledAt):NaN,l=t.status==="delivered"&&t.acked!==!0,d=t.status==="delivered"&&t.acked===!0&&t.ackedEventId==null&&a(t.text);(l||d||!Number.isNaN(r)&&f-r<s)&&u.push(t)}return u.sort((t,r)=>t.at<r.at?-1:1)}export function listInflightCounts(n,e){const o=new Map,i=s=>{o.set(s,(o.get(s)??0)+1)};for(const s of p(n))i(s.sessionId);for(const s of e.values())(s.status==="delivering"||s.status==="running")&&i(s.sessionId);return o}export async function sessionTerminals(n,e,o,i,s){const a={};if(s&&s.size>0){for(const c of s)a[c]=null;s.clear()}if(!e?.locateScreens||o.size===0)return a;const f={},u=n.livenessOf?.()??m();for(const[c,t]of o){if(h(n.sessions,c,u)){o.delete(c),i(),a[c]=null;continue}f[c]=t}return{...a,...await e.locateScreens(f)}}
1
+ import{combinedLivenessOf as p}from"../../daemon/liveness.js";import{readAllPromptbox as m,readPromptbox as g}from"../promptbox.js";import{sessionJudgedDead as h}from"./queue.js";function I(n,e){return{id:e.id,sessionId:n,text:e.text,...e.attachments&&e.attachments.length>0?{attachments:e.attachments}:{},mode:"prompt",oneWay:e.oneWay===!0,...e.forceTerminal===!0?{forceTerminal:!0}:{},channel:"prompt",status:"pending",at:e.at}}export function listVisible(n,e,o,a,s,u=()=>!1){const l=Date.now(),r=[],i=n.sessions.getBySessionId(a);if(i?.projectId!=null){const t=n.projects.getById(i.projectId);if(t)for(const c of g(e,t.token))c.sessionId===a&&r.push(I(a,c))}for(const t of o.values()){if(t.sessionId!==a)continue;if(t.stalled===!0||t.status==="delivering"||t.status==="running"){r.push(t);continue}if(t.status==="failed"||t.status==="timeout"){r.push(t);continue}const c=t.settledAt?Date.parse(t.settledAt):NaN,d=t.status==="delivered"&&t.acked!==!0,f=t.status==="delivered"&&t.acked===!0&&t.ackedEventId==null&&u(t.text);(d||f||!Number.isNaN(c)&&l-c<s)&&r.push(t)}return r.sort((t,c)=>t.at<c.at?-1:1)}export function listInflightCounts(n,e){const o=new Map,a=s=>{o.set(s,(o.get(s)??0)+1)};for(const s of m(n))a(s.sessionId);for(const s of e.values())(s.status==="delivering"||s.status==="running")&&a(s.sessionId);return o}export async function sessionTerminals(n,e,o,a,s){const u={};if(s&&s.size>0){for(const i of s)u[i]=null;s.clear()}if(!e?.locateScreens||o.size===0)return u;const l={},r=n.livenessOf?.()??p();for(const[i,t]of o){if(h(n.sessions,i,r)){o.delete(i),a(),u[i]=null;continue}l[i]=t}return{...u,...await e.locateScreens(l)}}
@@ -1,11 +1,11 @@
1
- import{INJECTABLE_EVENTS as b,TITLE_EFFECTIVE_EVENTS as S,hookSourceHarness as w}from"@weavecode/core";import{createPluginLogger as P}from"./plugin-loader.js";import{readHooksEnabled as k}from"../daemon/settings.js";import{effective as v,pluginsStateOf as T}from"./plugin-project-state.js";import{postJson as D,runWebhook as j}from"./handlers/webhook.js";import{DISPATCH_MARKER as g,extractFilePath as c,isCrossProjectPath as W,matchesAnyGlob as C,projectLabel as O,renderTemplate as _,templateVars as E}from"./match.js";function N(h){if(!h)return null;const e=h.match(/\{[\s\S]*\}/);if(e)try{const n=JSON.parse(e[0]);if(typeof n.label=="string"&&n.label.trim())return n.label.trim()}catch{}const s=/label\s*[:=]\s*"?([A-Za-z0-9_-]+)/i.exec(h);return s?s[1]:null}const I=6e4,A=10*6e4,$=4,L=4,M=3e3;export class HookDispatcher{deps;runningWebhooks=new Set;lastClassifyAt=new Map;recentPool=new Map;activeWebhooks=0;pendingWebhooks=[];activePlugins=0;pendingPlugins=[];constructor(e){this.deps=e}async onIngest(e){try{if(!k())return{};this.deps.plugins?.notifyEvent?.(e.payload.eventType,e.payload,e.sessionId);const s={};if(e.sessionId){const t=H(e.payload);let i=null;if(t===null&&this.deps.sessionTitleOf)try{i=await this.deps.sessionTitleOf(e.sessionId)}catch{}const r=t??i;r!==null&&this.deps.sessions.applyReportedTitle(e.sessionId,r)&&this.deps.bus.emit({kind:"session.renamed",sessionId:e.sessionId,title:r,at:new Date().toISOString()});const d=this.deps.sessions.getBySessionId(e.sessionId)?.pendingTitle;if(d){s.sessionTitle=d;const p=S[e.source].includes(e.payload.eventType);(e.deliveredTitle===d&&p||t!==null)&&this.deps.sessions.confirmPendingTitle(e.sessionId,d)&&this.deps.bus.emit({kind:"session.renamed",sessionId:e.sessionId,title:d,at:new Date().toISOString()})}}const n=this.match(e);if(n.length===0)return s;const a=[],o=[],l=Date.now(),u=l+M;for(const t of n)if(this.deps.bus.emit({kind:"rule.matched",ruleId:t.id,eventId:e.eventId,sessionId:e.sessionId,at:new Date(l).toISOString()}),t.handler.type!=="log"){if(t.handler.type==="manual"||t.handler.type==="inject"){if(this.dedupe(t,e,l))continue;const i=_(t.handler.template,E({event:e.payload.eventType,tool:e.payload.toolName,filePath:c(e.payload.toolInput),cwd:e.payload.cwd,session:e.sessionId,project:O(e.project.path),rule:t.name}))+`
2
- ${g}`,r=t.handler.type==="inject"?"injected":"pending";this.createDispatch(t,e,{prompt:i,status:r,mode:t.handler.type==="inject"?"auto":"manual"}),t.handler.type==="inject"&&(t.filter.crossProjectOnly?o.push(i):a.push(i));continue}if(t.handler.type==="webhook"){if(this.runningWebhooks.has(t.id))continue;this.runningWebhooks.add(t.id);const i=this.createDispatch(t,e,{prompt:`webhook ${t.handler.url}`,status:"running",mode:"auto"});this.deps.bus.emit({kind:"dispatch.started",ruleId:t.id,dispatchId:i.id,sessionId:e.sessionId,at:new Date().toISOString()}),this.runWebhookAsync(t,this.summary(t,e),i.id);continue}if(t.handler.type==="classify"){const i=this.turnText(e);if(!i)continue;const r=t.handler,d=i.toLowerCase();if(r.prefilter&&r.prefilter.length>0&&!r.prefilter.some(m=>d.includes(m.toLowerCase())))continue;const p=`classify:${t.id}`,f=r.cooldownMs??A;if(l-(this.lastClassifyAt.get(p)??0)<f||this.runningWebhooks.has(p))continue;this.lastClassifyAt.set(p,l),this.runningWebhooks.add(p);const y=this.createDispatch(t,e,{prompt:`classify \u2192 ${r.routes.map(m=>m.label).join("/")}`,status:"running",mode:"auto"});this.deps.bus.emit({kind:"dispatch.started",ruleId:t.id,dispatchId:y.id,sessionId:e.sessionId,at:new Date().toISOString()}),this.runClassifyAsync(t,e,y.id,i);continue}if(t.handler.type==="plugin"){const i=this.deps.plugins;if(!i)continue;const r=i.getHandler(t.handler.pluginId,t.handler.handlerType);if(!r||this.dedupe(t,e,l))continue;if(!this.injectable(e)){this.runPluginDetached(t,e,i,r);continue}const d=u-Date.now();if(d<=0)continue;const p=await this.runPluginHandler(t,e,i,r,d);if(!p)continue;p.prompt&&this.createDispatch(t,e,{prompt:p.prompt+`
3
- ${g}`,status:"pending",mode:"auto"}),p.additionalContext&&o.push(p.additionalContext)}}return(a.length>0||o.length>0)&&(s.additionalContext=[...a,...o].join(`
1
+ import{INJECTABLE_EVENTS as b,TITLE_EFFECTIVE_EVENTS as S,hookSourceHarness as w}from"@weavecode/core";import{createPluginLogger as P}from"./plugin-loader.js";import{readHooksEnabled as k}from"../daemon/settings.js";import{effective as v,pluginsStateOf as j}from"./plugin-project-state.js";import{postJson as D,runWebhook as T}from"./handlers/webhook.js";import{DISPATCH_MARKER as I,extractFilePath as h,isCrossProjectPath as W,matchesAnyGlob as C,projectLabel as O,renderTemplate as E,templateVars as A}from"./match.js";function $(c){if(!c)return null;const e=c.match(/\{[\s\S]*\}/);if(e)try{const i=JSON.parse(e[0]);if(typeof i.label=="string"&&i.label.trim())return i.label.trim()}catch{}const s=/label\s*[:=]\s*"?([A-Za-z0-9_-]+)/i.exec(c);return s?s[1]:null}const y=6e4,N=10*6e4,x=4,H=4,L=3e3;export class HookDispatcher{deps;runningWebhooks=new Set;lastClassifyAt=new Map;recentPool=new Map;activeWebhooks=0;pendingWebhooks=[];activePlugins=0;pendingPlugins=[];constructor(e){this.deps=e}async onIngest(e){try{if(!k())return{};this.deps.plugins?.notifyEvent?.(e.payload.eventType,e.payload,e.sessionId);const s={};if(e.sessionId){const t=_(e.payload);let n=null;if(t===null&&this.deps.sessionTitleOf)try{n=await this.deps.sessionTitleOf(e.sessionId)}catch{}const r=t??n;r!==null&&this.deps.sessions.applyReportedTitle(e.sessionId,r)&&this.deps.bus.emit({kind:"session.renamed",sessionId:e.sessionId,title:r,at:new Date().toISOString()});const d=this.deps.sessions.getBySessionId(e.sessionId)?.pendingTitle;if(d){s.sessionTitle=d;const p=S[e.source].includes(e.payload.eventType);(e.deliveredTitle===d&&p||t!==null)&&this.deps.sessions.confirmPendingTitle(e.sessionId,d)&&this.deps.bus.emit({kind:"session.renamed",sessionId:e.sessionId,title:d,at:new Date().toISOString()})}}const i=this.match(e);if(i.length===0)return s;const a=[],o=[],l=Date.now(),u=l+L;for(const t of i)if(this.deps.bus.emit({kind:"rule.matched",ruleId:t.id,eventId:e.eventId,sessionId:e.sessionId,at:new Date(l).toISOString()}),t.handler.type!=="log"){if(t.handler.type==="manual"||t.handler.type==="inject"){if(this.dedupe(t,e,l))continue;const n=E(t.handler.template,A({event:e.payload.eventType,tool:e.payload.toolName,filePath:h(e.payload.toolInput),cwd:e.payload.cwd,session:e.sessionId,project:O(e.project.path),rule:t.name}))+`
2
+ ${I}`,r=t.handler.type==="inject"?"injected":"pending";this.createDispatch(t,e,{prompt:n,status:r,mode:t.handler.type==="inject"?"auto":"manual"}),t.handler.type==="inject"&&(t.filter.crossProjectOnly?o.push(n):a.push(n));continue}if(t.handler.type==="webhook"){if(this.runningWebhooks.has(t.id))continue;this.runningWebhooks.add(t.id);const n=this.createDispatch(t,e,{prompt:`webhook ${t.handler.url}`,status:"running",mode:"auto"});this.deps.bus.emit({kind:"dispatch.started",ruleId:t.id,dispatchId:n.id,sessionId:e.sessionId,at:new Date().toISOString()}),this.runWebhookAsync(t,this.summary(t,e),n.id);continue}if(t.handler.type==="classify"){const n=this.turnText(e);if(!n)continue;const r=t.handler,d=n.toLowerCase();if(r.prefilter&&r.prefilter.length>0&&!r.prefilter.some(m=>d.includes(m.toLowerCase())))continue;const p=`classify:${t.id}`,f=r.cooldownMs??N;if(l-(this.lastClassifyAt.get(p)??0)<f||this.runningWebhooks.has(p))continue;this.lastClassifyAt.set(p,l),this.runningWebhooks.add(p);const g=this.createDispatch(t,e,{prompt:`classify \u2192 ${r.routes.map(m=>m.label).join("/")}`,status:"running",mode:"auto"});this.deps.bus.emit({kind:"dispatch.started",ruleId:t.id,dispatchId:g.id,sessionId:e.sessionId,at:new Date().toISOString()}),this.runClassifyAsync(t,e,g.id,n);continue}if(t.handler.type==="plugin"){const n=this.deps.plugins;if(!n)continue;const r=n.getHandler(t.handler.pluginId,t.handler.handlerType);if(!r||this.dedupe(t,e,l))continue;if(!this.injectable(e)){this.runPluginDetached(t,e,n,r);continue}const d=u-Date.now();if(d<=0)continue;const p=await this.runPluginHandler(t,e,n,r,d);if(!p)continue;p.prompt&&this.createDispatch(t,e,{prompt:p.prompt+`
3
+ ${I}`,status:"pending",mode:"auto"}),p.additionalContext&&o.push(p.additionalContext)}}return(a.length>0||o.length>0)&&(s.additionalContext=[...a,...o].join(`
4
4
 
5
5
  `)),o.length>0&&(s.deferred=o.join(`
6
6
 
7
- `)),s}catch{return{}}}createDispatch(e,s,n){const a=this.deps.dispatches.insert({ruleId:e.id,eventId:s.eventId,sessionId:s.sessionId,...n});return this.deps.bus.emit({kind:"dispatch.created",ruleId:e.id,dispatchId:a.id,status:n.status,mode:a.mode,sessionId:s.sessionId,at:a.startedAt}),a}dedupe(e,s,n){const a=`${e.id}:${s.sessionId??""}`,o=this.recentPool.get(a);return o!==void 0&&n-o<I?!0:(this.recentPool.set(a,n),this.trimDedupe(n),!1)}match(e){const s=this.deps.pluginStates,n=s?T(e.project.meta).enabled:null;let a=null;return this.deps.registry.candidates(e.payload.eventType,s?{includeDisabled:!0}:void 0).filter(o=>{const l=w(o.source);if(l!==null&&l!=="any"&&l!==e.source||o.projectId!==null&&o.projectId!==e.project.id)return!1;if(o.pluginId!==null){if(!o.enabled&&n?.[o.pluginId]!==!0||s&&!v(e.project,o.pluginId,{states:s}))return!1}else if(!o.enabled)return!1;const u=o.filter;if(u.toolNames?.length&&(!e.payload.toolName||!u.toolNames.includes(e.payload.toolName)))return!1;if(u.pathGlobs?.length){const t=c(e.payload.toolInput);if(!t||!C(t,u.pathGlobs))return!1}if(u.crossProjectOnly){const t=c(e.payload.toolInput)??e.payload.cwd;if(!t||(a??=this.deps.projects.list().map(i=>i.path).filter(i=>i!==e.project.path),!W(t,e.project.path,a)))return!1}return!0})}injectable(e){const s=e.payload.eventType;return s!==null&&b[e.source].includes(s)}async runPluginHandler(e,s,n,a,o){if(e.handler.type!=="plugin")return;const{pluginId:l}=e.handler,u=P(l),t=o===void 0?e.timeoutMs:Math.min(e.timeoutMs,o);let i;try{const r=(async()=>{await this.acquirePluginSlot();try{return await a.execute({event:s.payload,rule:e,config:n.getConfig(l),logger:u})}finally{this.releasePluginSlot()}})(),d=await Promise.race([r,new Promise((p,f)=>{i=setTimeout(()=>f(new Error("plugin handler timeout")),t)})]);return d&&typeof d=="object"?d:void 0}catch{return}finally{i&&clearTimeout(i)}}async runPluginDetached(e,s,n,a){try{const o=await this.runPluginHandler(e,s,n,a);if(!o?.prompt)return;this.createDispatch(e,s,{prompt:o.prompt+`
8
- ${g}`,status:"pending",mode:"auto"})}catch{}}summary(e,s){return{ruleId:e.id,ruleName:e.name,projectId:s.project.id,sessionId:s.sessionId,event:s.payload.eventType,toolName:s.payload.toolName,filePath:c(s.payload.toolInput),cwd:s.payload.cwd,at:new Date().toISOString()}}turnText(e){const n=e.payload.raw?.last_assistant_message;return typeof n=="string"?n.trim():""}async runClassifyAsync(e,s,n,a){const o=e.handler;if(o.type!=="classify")return;const l=`classify:${e.id}`;try{const u=await this.deps.classify?.({sessionId:s.sessionId??"",prompt:`${o.prompt}
7
+ `)),s}catch{return{}}}createDispatch(e,s,i){const a=this.deps.dispatches.insert({ruleId:e.id,eventId:s.eventId,sessionId:s.sessionId,...i});return this.deps.bus.emit({kind:"dispatch.created",ruleId:e.id,dispatchId:a.id,status:i.status,mode:a.mode,sessionId:s.sessionId,at:a.startedAt}),a}dedupe(e,s,i){const a=`${e.id}:${s.sessionId??""}`,o=this.recentPool.get(a);return o!==void 0&&i-o<y?!0:(this.recentPool.set(a,i),this.trimDedupe(i),!1)}match(e){const s=this.deps.pluginStates,i=s?j(e.project.meta).enabled:null;let a=null;return this.deps.registry.candidates(e.payload.eventType,s?{includeDisabled:!0}:void 0).filter(o=>{const l=w(o.source);if(l!==null&&l!=="any"&&l!==e.source||o.projectId!==null&&o.projectId!==e.project.id)return!1;if(o.pluginId!==null){if(!o.enabled&&i?.[o.pluginId]!==!0||s&&!v(e.project,o.pluginId,{states:s}))return!1}else if(!o.enabled)return!1;const u=o.filter;if(u.toolNames?.length&&(!e.payload.toolName||!u.toolNames.includes(e.payload.toolName)))return!1;if(u.pathGlobs?.length){const t=h(e.payload.toolInput);if(!t||!C(t,u.pathGlobs))return!1}if(u.crossProjectOnly){const t=h(e.payload.toolInput)??e.payload.cwd;if(!t||(a??=this.deps.projects.list().map(n=>n.path).filter(n=>n!==e.project.path),!W(t,e.project.path,a)))return!1}return!0})}injectable(e){const s=e.payload.eventType;return s!==null&&b[e.source].includes(s)}async runPluginHandler(e,s,i,a,o){if(e.handler.type!=="plugin")return;const{pluginId:l}=e.handler,u=P(l),t=o===void 0?e.timeoutMs:Math.min(e.timeoutMs,o);let n;try{const r=(async()=>{await this.acquirePluginSlot();try{return await a.execute({event:s.payload,rule:e,config:i.getConfig(l),logger:u})}finally{this.releasePluginSlot()}})(),d=await Promise.race([r,new Promise((p,f)=>{n=setTimeout(()=>f(new Error("plugin handler timeout")),t)})]);return d&&typeof d=="object"?d:void 0}catch{return}finally{n&&clearTimeout(n)}}async runPluginDetached(e,s,i,a){try{const o=await this.runPluginHandler(e,s,i,a);if(!o?.prompt)return;this.createDispatch(e,s,{prompt:o.prompt+`
8
+ ${I}`,status:"pending",mode:"auto"})}catch{}}summary(e,s){return{ruleId:e.id,ruleName:e.name,projectId:s.project.id,sessionId:s.sessionId,event:s.payload.eventType,toolName:s.payload.toolName,filePath:h(s.payload.toolInput),cwd:s.payload.cwd,at:new Date().toISOString()}}turnText(e){const s=e.payload.raw?.last_assistant_message;return typeof s=="string"?s.trim():""}async runClassifyAsync(e,s,i,a){const o=e.handler;if(o.type!=="classify")return;const l=`classify:${e.id}`;try{const u=await this.deps.classify?.({sessionId:s.sessionId??"",prompt:`${o.prompt}
9
9
 
10
10
  \u672C\u8F6E\u6700\u7EC8\u8F93\u51FA\uFF1A
11
- ${a.slice(0,8e3)}`}),t=N(u),i=t?o.routes.find(d=>d.label.toLowerCase()===t.toLowerCase()):void 0;if(!i){this.deps.dispatches.updateStatus(n,{status:"succeeded",output:t?`label=${t} (no route)`:"no label",finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:n,status:"succeeded",sessionId:s.sessionId,at:new Date().toISOString()});return}const r=await D(i.url,i.secret,{...this.summary(e,s),label:t,verdict:u?.slice(0,500)},this.deps.fetchImpl);this.deps.dispatches.updateStatus(n,{status:r.status,output:`label=${t} ${r.output??""}`.trim(),error:r.error??null,finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:n,status:r.status,sessionId:s.sessionId,at:new Date().toISOString()})}catch(u){this.deps.dispatches.updateStatus(n,{status:"failed",error:String(u),finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:n,status:"failed",sessionId:s.sessionId,at:new Date().toISOString()})}finally{this.runningWebhooks.delete(l)}}async runWebhookAsync(e,s,n){await this.acquireWebhookSlot();try{const a=await j(e,s,this.deps.fetchImpl);this.deps.dispatches.updateStatus(n,{status:a.status,output:a.output??null,error:a.error??null,finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:n,status:a.status,sessionId:s.sessionId,at:new Date().toISOString()})}catch(a){this.deps.dispatches.updateStatus(n,{status:"failed",error:String(a),finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:n,status:"failed",sessionId:s.sessionId,at:new Date().toISOString()})}finally{this.runningWebhooks.delete(e.id),this.releaseWebhookSlot()}}acquireWebhookSlot(){return this.activeWebhooks<$?(this.activeWebhooks++,Promise.resolve()):new Promise(e=>{this.pendingWebhooks.push(()=>{this.activeWebhooks++,e()})})}releaseWebhookSlot(){this.activeWebhooks--;const e=this.pendingWebhooks.shift();e&&e()}acquirePluginSlot(){return this.activePlugins<L?(this.activePlugins++,Promise.resolve()):new Promise(e=>{this.pendingPlugins.push(()=>{this.activePlugins++,e()})})}releasePluginSlot(){this.activePlugins--;const e=this.pendingPlugins.shift();e&&e()}trimDedupe(e){if(!(this.recentPool.size<1e3))for(const[s,n]of this.recentPool)e-n>=I&&this.recentPool.delete(s)}}function H(h){const e=h.raw;if(typeof e!="object"||e===null)return null;const s=e.session_title;return typeof s=="string"&&s.length>0?s:null}
11
+ ${a.slice(0,8e3)}`}),t=$(u),n=t?o.routes.find(d=>d.label.toLowerCase()===t.toLowerCase()):void 0;if(!n){this.deps.dispatches.updateStatus(i,{status:"succeeded",output:t?`label=${t} (no route)`:"no label",finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:i,status:"succeeded",sessionId:s.sessionId,at:new Date().toISOString()});return}const r=await D(n.url,n.secret,{...this.summary(e,s),label:t,verdict:u?.slice(0,500)},this.deps.fetchImpl);this.deps.dispatches.updateStatus(i,{status:r.status,output:`label=${t} ${r.output??""}`.trim(),error:r.error??null,finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:i,status:r.status,sessionId:s.sessionId,at:new Date().toISOString()})}catch(u){this.deps.dispatches.updateStatus(i,{status:"failed",error:String(u),finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:i,status:"failed",sessionId:s.sessionId,at:new Date().toISOString()})}finally{this.runningWebhooks.delete(l)}}async runWebhookAsync(e,s,i){await this.acquireWebhookSlot();try{const a=await T(e,s,this.deps.fetchImpl);this.deps.dispatches.updateStatus(i,{status:a.status,output:a.output??null,error:a.error??null,finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:i,status:a.status,sessionId:s.sessionId,at:new Date().toISOString()})}catch(a){this.deps.dispatches.updateStatus(i,{status:"failed",error:String(a),finished:!0}),this.deps.bus.emit({kind:"dispatch.finished",ruleId:e.id,dispatchId:i,status:"failed",sessionId:s.sessionId,at:new Date().toISOString()})}finally{this.runningWebhooks.delete(e.id),this.releaseWebhookSlot()}}acquireWebhookSlot(){return this.activeWebhooks<x?(this.activeWebhooks++,Promise.resolve()):new Promise(e=>{this.pendingWebhooks.push(()=>{this.activeWebhooks++,e()})})}releaseWebhookSlot(){this.activeWebhooks--;const e=this.pendingWebhooks.shift();e&&e()}acquirePluginSlot(){return this.activePlugins<H?(this.activePlugins++,Promise.resolve()):new Promise(e=>{this.pendingPlugins.push(()=>{this.activePlugins++,e()})})}releasePluginSlot(){this.activePlugins--;const e=this.pendingPlugins.shift();e&&e()}trimDedupe(e){if(!(this.recentPool.size<1e3))for(const[s,i]of this.recentPool)e-i>=y&&this.recentPool.delete(s)}}function _(c){const e=c.raw;if(typeof e!="object"||e===null)return null;const s=e.session_title;return typeof s=="string"&&s.length>0?s:null}
@@ -1 +1 @@
1
- import{isSyntheticSession as g,registerSyntheticSession as d,syntheticSessionIds as x}from"./synthetic-sessions.js";const y=8e3;export function createEdgeExtract(t){const h=t.concurrency??4;let o=0;return{supports(s){const r=t.harnessOf(s);return r?t.surfaceFor(r).headless?.extractArgs!=null:!1},run({sessionId:s,prompt:r}){if(o>=h)return Promise.resolve(null);const c=t.harnessOf(s),i=t.sessions.getBySessionId(s);if(!c||!i?.projectId)return Promise.resolve(null);const u=t.projects.getById(i.projectId),n=t.surfaceFor(c).headless,a=process.env.WEAVE_CLAUDE_BIN??n?.bin;if(!u||!n?.extractArgs||!a)return Promise.resolve(null);const m=d();return t.onSyntheticChange(),o++,new Promise(S=>{let l=!1;const f=e=>{l||(l=!0,o--,S(e))};t.headless.run({sessionId:m,cwd:u.path,prompt:r,spec:n,bin:a,argsOf:e=>n.extractArgs(s,e),timeoutMs:t.timeoutMs??12e4,onSettle:e=>{f(e.status==="succeeded"&&e.output?.trim()?e.output.trim().slice(0,y):null)}})||f(null)})}}}export{g as isSyntheticSession,x as syntheticSessionIds};
1
+ import{isSyntheticSession as S,registerSyntheticSession as f,syntheticSessionIds as g}from"./synthetic-sessions.js";const y=8e3;export function createEdgeExtract(e){const d=e.concurrency??4;let o=0;return{supports(t){const r=e.harnessOf(t);return r?e.surfaceFor(r).headless?.extractArgs!=null:!1},run({sessionId:t,prompt:r}){if(o>=d)return Promise.resolve(null);const i=e.harnessOf(t),c=e.sessions.getBySessionId(t);if(!i||!c?.projectId)return Promise.resolve(null);const u=e.projects.getById(c.projectId),n=e.surfaceFor(i).headless,l=process.env.WEAVE_CLAUDE_BIN??n?.bin;if(!u||!n?.extractArgs||!l)return Promise.resolve(null);const h=f();return e.onSyntheticChange(),o++,new Promise(m=>{let a=!1;const p=s=>{a||(a=!0,o--,m(s))};e.headless.run({sessionId:h,cwd:u.path,prompt:r,spec:n,bin:l,argsOf:s=>n.extractArgs(t,s),timeoutMs:e.timeoutMs??12e4,onSettle:s=>{p(s.status==="succeeded"&&s.output?.trim()?s.output.trim().slice(0,y):null)}})||p(null)})}}}export{S as isSyntheticSession,g as syntheticSessionIds};
@@ -1 +1 @@
1
- import{createHmac as l}from"node:crypto";const c=5e3,f=4096;export async function postJson(e,s,n,o=fetch){let r;try{r=new URL(e)}catch{return{ok:!1,status:"failed",error:`invalid url: ${e}`}}if(r.protocol!=="http:"&&r.protocol!=="https:")return{ok:!1,status:"failed",error:`unsupported protocol: ${r.protocol}`};const u=JSON.stringify(n),i={"content-type":"application/json","user-agent":"weave-daemon"};s&&(i["x-weave-signature"]=`sha256=${l("sha256",s).update(u).digest("hex")}`);try{const t=await o(r,{method:"POST",headers:i,body:u,signal:AbortSignal.timeout(c)}),a=(await t.text()).slice(0,f);return t.ok?{ok:!0,status:"succeeded",output:a||`HTTP ${t.status}`}:{ok:!1,status:"failed",error:`HTTP ${t.status}`,output:a}}catch(t){const a=t instanceof Error&&(t.name==="TimeoutError"||t.name==="AbortError");return{ok:!1,status:a?"timeout":"failed",error:a?`webhook timed out after ${c}ms`:String(t)}}}export async function runWebhook(e,s,n=fetch){const o=e.handler;return o.type!=="webhook"?{ok:!1,status:"failed",error:`rule ${e.id} is not a webhook handler`}:postJson(o.url,o.secret,s,n)}
1
+ import{createHmac as l}from"node:crypto";const c=5e3,d=4096;export async function postJson(e,s,n,o=fetch){let r;try{r=new URL(e)}catch{return{ok:!1,status:"failed",error:`invalid url: ${e}`}}if(r.protocol!=="http:"&&r.protocol!=="https:")return{ok:!1,status:"failed",error:`unsupported protocol: ${r.protocol}`};const u=JSON.stringify(n),i={"content-type":"application/json","user-agent":"weave-daemon"};s&&(i["x-weave-signature"]=`sha256=${l("sha256",s).update(u).digest("hex")}`);try{const t=await o(r,{method:"POST",headers:i,body:u,signal:AbortSignal.timeout(c)}),a=(await t.text()).slice(0,d);return t.ok?{ok:!0,status:"succeeded",output:a||`HTTP ${t.status}`}:{ok:!1,status:"failed",error:`HTTP ${t.status}`,output:a}}catch(t){const a=t instanceof Error&&(t.name==="TimeoutError"||t.name==="AbortError");return{ok:!1,status:a?"timeout":"failed",error:a?`webhook timed out after ${c}ms`:String(t)}}}export async function runWebhook(e,s,n=fetch){const o=e.handler;return o.type!=="webhook"?{ok:!1,status:"failed",error:`rule ${e.id} is not a webhook handler`}:postJson(o.url,o.secret,s,n)}
@@ -1,2 +1,2 @@
1
- import{spawn as k}from"node:child_process";const A=3e5,T=32*1024,_=2e3;export class HeadlessRunner{spawnImpl;killImpl;active=new Set;livePids=new Map;constructor(t={}){this.spawnImpl=t.spawnImpl??k,this.killImpl=t.killImpl??defaultKill}isBusy(t){return this.active.has(t)}killAll(){let t=0;for(const e of this.livePids.values())try{this.killImpl(e),t++}catch{}return this.livePids.clear(),t}run(t){if(this.active.has(t.sessionId))return!1;this.active.add(t.sessionId);const e={settled:!1};try{this.watch(t,e)}catch(l){this.settle(t,e,{status:"failed",output:null,error:y(l)})}return!0}watch(t,e){const l=t.timeoutMs??A,a=t.spec.promptStdin===!0,m=t.argsOf?t.argsOf(t.sessionId):t.spec.args(t.sessionId,a?"":t.prompt),h=a&&process.platform==="win32"&&!/[\s"\\/]/.test(t.bin),E=(t.detached??!0)&&!h,p={cwd:t.cwd,detached:E,windowsHide:!0,shell:h,stdio:[a?"pipe":"ignore","pipe","pipe"],env:(()=>{const s={...process.env,WEAVE_HOOK_DISPATCH:"1"};return delete s.CLAUDE_CODE_CHILD_SESSION,s})()},r=h?this.spawnImpl([t.bin,...m].join(" "),[],p):this.spawnImpl(t.bin,m,p);if(r.pid!==void 0&&this.livePids.set(t.sessionId,r.pid),a){r.stdin?.on("error",()=>{});try{r.stdin?.write(t.prompt),r.stdin?.end()}catch{}}const I=[],u=[];let w=0,S=0;const g=(s,n,o)=>{if(n>=T)return n;const c=T-n;return s.push(o.length>c?o.subarray(0,c):o),n+Math.min(o.length,c)},f=()=>Buffer.concat([...I,...u]).toString("utf8"),M=()=>{const s=Buffer.concat(u).toString("utf8");return s.length>_?s.slice(-_):s};r.stdout?.on("data",s=>{w=g(I,w,s)}),r.stderr?.on("data",s=>{S=g(u,S,s)});const d=l>0?setTimeout(()=>{if(r.pid!==void 0)try{this.killImpl(r.pid)}catch{}this.settle(t,e,{status:"timeout",output:f(),error:`Timeout after ${l}ms`})},l):null;r.on("error",s=>{d!==null&&clearTimeout(d),this.settle(t,e,{status:"failed",output:f(),error:y(s)})}),r.on("exit",(s,n)=>{d!==null&&clearTimeout(d);const o=f();if(s===0){this.settle(t,e,{status:"succeeded",output:o,error:null});return}const c=s!==null?`exit ${s}`:`exit signal ${n??"unknown"}`,v=M();this.settle(t,e,{status:"failed",output:o,error:v?`${c}
2
- ${v}`:c})})}settle(t,e,l){if(!e.settled){e.settled=!0,this.active.delete(t.sessionId),this.livePids.delete(t.sessionId);try{t.onSettle(l)}catch{}}}}export function defaultKill(i){if(process.platform==="win32"){try{k("taskkill",["/PID",String(i),"/T","/F"],{windowsHide:!0,stdio:"ignore"}).on("error",()=>{})}catch{}return}try{process.kill(-i,"SIGKILL")}catch{try{process.kill(i,"SIGKILL")}catch{}}}function y(i){return i instanceof Error?i.message:String(i)}
1
+ import{spawn as k}from"node:child_process";const T=3e5,y=32*1024,P=2e3;export class HeadlessRunner{spawnImpl;killImpl;active=new Set;livePids=new Map;constructor(t={}){this.spawnImpl=t.spawnImpl??k,this.killImpl=t.killImpl??defaultKill}isBusy(t){return this.active.has(t)}killAll(){let t=0;for(const e of this.livePids.values())try{this.killImpl(e),t++}catch{}return this.livePids.clear(),t}run(t){if(this.active.has(t.sessionId))return!1;this.active.add(t.sessionId);const e={settled:!1};try{this.watch(t,e)}catch(r){this.settle(t,e,{status:"failed",output:null,error:E(r)})}return!0}watch(t,e){const r=t.timeoutMs??T,a=t.spec.promptStdin===!0,f=t.argsOf?t.argsOf(t.sessionId):t.spec.args(t.sessionId,a?"":t.prompt),d=a&&process.platform==="win32"&&!/[\s"\\/]/.test(t.bin),H=(t.detached??!0)&&!d,m={cwd:t.cwd,detached:H,windowsHide:!0,shell:d,stdio:[a?"pipe":"ignore","pipe","pipe"],env:(()=>{const s={...process.env,WEAVE_HOOK_DISPATCH:"1"};return delete s.CLAUDE_CODE_CHILD_SESSION,s})()},i=d?this.spawnImpl([t.bin,...f].join(" "),[],m):this.spawnImpl(t.bin,f,m);if(i.pid!==void 0&&this.livePids.set(t.sessionId,i.pid),a){i.stdin?.on("error",()=>{});try{i.stdin?.write(t.prompt),i.stdin?.end()}catch{}}const I=[],p=[];let w=0,v=0;const g=(s,o,l)=>{if(o>=y)return o;const c=y-o;return s.push(l.length>c?l.subarray(0,c):l),o+Math.min(l.length,c)},h=()=>Buffer.concat([...I,...p]).toString("utf8"),L=()=>{const s=Buffer.concat(p).toString("utf8");return s.length>P?s.slice(-P):s};i.stdout?.on("data",s=>{w=g(I,w,s)}),i.stderr?.on("data",s=>{v=g(p,v,s)});const u=r>0?setTimeout(()=>{if(i.pid!==void 0)try{this.killImpl(i.pid)}catch{}this.settle(t,e,{status:"timeout",output:h(),error:`Timeout after ${r}ms`})},r):null;i.on("error",s=>{u!==null&&clearTimeout(u),this.settle(t,e,{status:"failed",output:h(),error:E(s)})}),i.on("exit",(s,o)=>{u!==null&&clearTimeout(u);const l=h();if(s===0){this.settle(t,e,{status:"succeeded",output:l,error:null});return}const c=s!==null?`exit ${s}`:`exit signal ${o??"unknown"}`,S=L();this.settle(t,e,{status:"failed",output:l,error:S?`${c}
2
+ ${S}`:c})})}settle(t,e,r){if(!e.settled){e.settled=!0,this.active.delete(t.sessionId),this.livePids.delete(t.sessionId);try{t.onSettle(r)}catch{}}}}export function defaultKill(n){if(process.platform==="win32"){try{k("taskkill",["/PID",String(n),"/T","/F"],{windowsHide:!0,stdio:"ignore"}).on("error",()=>{})}catch{}return}try{process.kill(-n,"SIGKILL")}catch{try{process.kill(n,"SIGKILL")}catch{}}}function E(n){return n instanceof Error?n.message:String(n)}
@@ -1,2 +1,2 @@
1
- import a from"node:net";import{chmodSync as y,mkdirSync as d,readFileSync as k,renameSync as S,writeFileSync as b}from"node:fs";import{dirname as x}from"node:path";export function inboxSocketPathOf(s){return s.startsWith("uds:")?s.slice(4):s}export function deliverViaInboxSocket(s,t,e,o={}){const c=o.platform??process.platform,n=o.timeoutMs??3e3,l=inboxSocketPathOf(t.socket);return s.requiresAuthOn(c)&&!t.token?Promise.reject(new Error(`inbox delivery requires a token on platform "${c}"`)):new Promise((p,m)=>{const h=s.frame({token:t.token,text:e});let f=!1;const u=i=>{f||(f=!0,r.destroy(),m(i))},r=a.connect(l);r.setTimeout(n),r.on("timeout",()=>u(new Error(`inbox socket timeout after ${n}ms`))),r.on("error",i=>u(i)),r.on("connect",()=>{r.write(h,i=>{i?u(i):r.end()})}),r.on("close",()=>{f||(f=!0,p())})})}export const INBOX_TARGETS_FILE="inbox-targets.json";function O(s){try{const t=JSON.parse(k(s,"utf8"));if(typeof t!="object"||t===null)return{};const e={};for(const[o,c]of Object.entries(t)){const n=c;typeof n?.socket!="string"||!n.socket||(e[o]={socket:n.socket,token:typeof n.token=="string"&&n.token?n.token:null})}return e}catch{return{}}}export class InboxSocketRegistry{file;bySession;persisted;constructor(t){this.file=t;const e=t?O(t):{};this.bySession=new Map(Object.entries(e)),this.persisted=JSON.stringify(e)}set(t,e){const o=this.bySession.get(t);if(!e?.socket){o&&(this.bySession.delete(t),this.persist());return}o&&o.socket===e.socket&&o.token===e.token||(this.bySession.set(t,e),this.persist())}get(t){return this.bySession.get(t)}forget(t){this.bySession.delete(t)&&this.persist()}persist(){if(!this.file)return;const t=JSON.stringify(Object.fromEntries(this.bySession));if(t!==this.persisted)try{d(x(this.file),{recursive:!0});const e=`${this.file}.tmp`;b(e,`${t}
2
- `,{encoding:"utf8",mode:384}),S(e,this.file),y(this.file,384),this.persisted=t}catch{}}}
1
+ import k from"node:net";import{chmodSync as l,mkdirSync as y,readFileSync as S,renameSync as b,writeFileSync as d}from"node:fs";import{dirname as g}from"node:path";export function inboxSocketPathOf(s){return s.startsWith("uds:")?s.slice(4):s}export function deliverViaInboxSocket(s,t,e,o={}){const c=o.platform??process.platform,i=o.timeoutMs??3e3,p=inboxSocketPathOf(t.socket);return s.requiresAuthOn(c)&&!t.token?Promise.reject(new Error(`inbox delivery requires a token on platform "${c}"`)):new Promise((u,m)=>{const a=s.frame({token:t.token,text:e});let f=!1;const h=r=>{f||(f=!0,n.destroy(),m(r))},n=k.connect(p);n.setTimeout(i),n.on("timeout",()=>h(new Error(`inbox socket timeout after ${i}ms`))),n.on("error",r=>h(r)),n.on("connect",()=>{n.write(a,r=>{r?h(r):n.end()})}),n.on("close",()=>{f||(f=!0,u())})})}export const INBOX_TARGETS_FILE="inbox-targets.json";function x(s){try{const t=JSON.parse(S(s,"utf8"));if(typeof t!="object"||t===null)return{};const e={};for(const[o,c]of Object.entries(t)){const i=c;typeof i?.socket!="string"||!i.socket||(e[o]={socket:i.socket,token:typeof i.token=="string"&&i.token?i.token:null})}return e}catch{return{}}}export class InboxSocketRegistry{file;bySession;persisted;constructor(t){this.file=t;const e=t?x(t):{};this.bySession=new Map(Object.entries(e)),this.persisted=JSON.stringify(e)}set(t,e){const o=this.bySession.get(t);if(!e?.socket){o&&(this.bySession.delete(t),this.persist());return}o&&o.socket===e.socket&&o.token===e.token||(this.bySession.set(t,e),this.persist())}get(t){return this.bySession.get(t)}forget(t){this.bySession.delete(t)&&this.persist()}persist(){if(!this.file)return;const t=JSON.stringify(Object.fromEntries(this.bySession));if(t!==this.persisted)try{y(g(this.file),{recursive:!0});const e=`${this.file}.tmp`;d(e,`${t}
2
+ `,{encoding:"utf8",mode:384}),b(e,this.file),l(this.file,384),this.persisted=t}catch{}}}
@@ -1,4 +1,4 @@
1
- import{existsSync as v,readdirSync as I,readFileSync as w,renameSync as g,unlinkSync as S,writeFileSync as T}from"node:fs";import{join as f}from"node:path";import{isMainThread as E,parentPort as h,Worker as x,workerData as k}from"node:worker_threads";import{createRuntimeRegistry as M,effortLevelOf as O,frameFacts as P,permissionModeOf as D,startedModelOf as A,switchedModelOf as L}from"@weavecode/harness";import{HARNESS_LIFECYCLE as W,isHarness as m}from"@weavecode/core";import{getLogsDir as _}from"../db/paths.js";import{projectHarnessIds as C}from"../daemon/project-harnesses.js";import{openDatabase as F}from"../db/db.js";import{HookEventRepository as R}from"../repositories/hook-events.js";import{ProjectRepository as N}from"../repositories/projects.js";import{SessionRepository as B}from"../repositories/sessions.js";import{appendOutbox as X}from"./mirror.js";import{isSyntheticSession as H,replaceSyntheticSessions as U}from"./synthetic-sessions.js";export const INBOX_TICK_MS=200,INBOX_CLAIM_SUFFIX=".claim.jsonl",INBOX_FAILED_SUFFIX=".failed.jsonl";const $=5;export function drainInboxOnce(n,o=new Map){const e=f(n.hooksDir,"inbox");let i;try{i=I(e)}catch{return[]}const s=[];for(const c of i)c.endsWith(INBOX_FAILED_SUFFIX)||c.endsWith(".tmp")||c.endsWith(INBOX_CLAIM_SUFFIX)||c.endsWith(".jsonl")&&s.push(c);const r=[];for(const c of i)c.endsWith(INBOX_CLAIM_SUFFIX)&&r.push(...j(n,f(e,c),o));for(const c of s){const p=f(e,`${c.slice(0,-6)}${INBOX_CLAIM_SUFFIX}`);if(!v(p)){try{g(f(e,c),p)}catch{continue}r.push(...j(n,p,o))}}return r}function j(n,o,e){let i;try{i=w(o,"utf8").split(`
2
- `)}catch{return[]}const s=new Map;for(const u of i){const a=u.trim();if(!a)continue;const t=z(a);if(!t)continue;const l=s.get(t.token)??{lines:[],inputs:[],delivered:[],terminals:[],messaging:[]};if(l.lines.push(a),l.project||(l.project=n.projects.getByToken(t.token),s.set(t.token,l)),!l.project||l.project.archivedAt)continue;const y=t.report&&typeof t.report=="object"?n.adapters.find(d=>d.source===t.report.source):void 0;if(!(!y||!t.report.eventType))try{const d=y.normalize(t.report,l.project.id);if(H(d.sessionId))continue;l.inputs.push(d),l.delivered.push(t.report.deliveredTitle??null),l.terminals.push(t.terminalEnv),l.messaging.push(t.messaging)}catch{}}const r=[],c=[];for(const u of s.values())if(!(!u.project||u.inputs.length===0))try{const a=b(n,u.project,u.inputs);for(let t=0;t<u.inputs.length;t++)r.push({eventId:a[t],projectId:u.project.id,input:u.inputs[t],deliveredTitle:u.delivered[t]??null,messaging:u.messaging[t]??null,terminalEnv:u.terminals[t]??null})}catch{c.push(...u.lines)}if(c.length===0){e.delete(o);try{S(o)}catch{}return r}const p=(e.get(o)??0)+1;if(e.set(o,p),p>=$){e.delete(o);try{g(o,o.replace(INBOX_CLAIM_SUFFIX,INBOX_FAILED_SUFFIX))}catch{}return r}try{T(o,c.join(`
1
+ import{existsSync as I,readdirSync as v,readFileSync as j,renameSync as h,unlinkSync as b,writeFileSync as E}from"node:fs";import{join as d}from"node:path";import{isMainThread as T,parentPort as m,Worker as P,workerData as k}from"node:worker_threads";import{createRuntimeRegistry as x,effortLevelOf as M,frameFacts as O,permissionModeOf as D,startedModelOf as W,switchedModelOf as A}from"@weavecode/harness";import{HARNESS_LIFECYCLE as F,isHarness as g}from"@weavecode/core";import{getLogsDir as R}from"../db/paths.js";import{projectHarnessIds as L}from"../daemon/project-harnesses.js";import{openDatabase as _}from"../db/db.js";import{HookEventRepository as C}from"../repositories/hook-events.js";import{ProjectRepository as N}from"../repositories/projects.js";import{SessionRepository as B}from"../repositories/sessions.js";import{appendOutbox as X}from"./mirror.js";import{isSyntheticSession as H,replaceSyntheticSessions as U}from"./synthetic-sessions.js";export const INBOX_TICK_MS=200,INBOX_CLAIM_SUFFIX=".claim.jsonl",INBOX_FAILED_SUFFIX=".failed.jsonl";const $=5;export function drainInboxOnce(s,t=new Map){const e=d(s.hooksDir,"inbox");let i;try{i=v(e)}catch{return[]}const o=[];for(const c of i)c.endsWith(INBOX_FAILED_SUFFIX)||c.endsWith(".tmp")||c.endsWith(INBOX_CLAIM_SUFFIX)||c.endsWith(".jsonl")&&o.push(c);const r=[];for(const c of i)c.endsWith(INBOX_CLAIM_SUFFIX)&&r.push(...w(s,d(e,c),t));for(const c of o){const p=d(e,`${c.slice(0,-6)}${INBOX_CLAIM_SUFFIX}`);if(!I(p)){try{h(d(e,c),p)}catch{continue}r.push(...w(s,p,t))}}return r}function w(s,t,e){let i;try{i=j(t,"utf8").split(`
2
+ `)}catch{return[]}const o=new Map;for(const a of i){const u=a.trim();if(!u)continue;const n=z(u);if(!n)continue;const l=o.get(n.token)??{lines:[],inputs:[],delivered:[],terminals:[],messaging:[]};if(l.lines.push(u),l.project||(l.project=s.projects.getByToken(n.token),o.set(n.token,l)),!l.project||l.project.archivedAt)continue;const y=n.report&&typeof n.report=="object"?s.adapters.find(f=>f.source===n.report.source):void 0;if(!(!y||!n.report.eventType))try{const f=y.normalize(n.report,l.project.id);if(H(f.sessionId))continue;l.inputs.push(f),l.delivered.push(n.report.deliveredTitle??null),l.terminals.push(n.terminalEnv),l.messaging.push(n.messaging)}catch{}}const r=[],c=[];for(const a of o.values())if(!(!a.project||a.inputs.length===0))try{const u=S(s,a.project,a.inputs);for(let n=0;n<a.inputs.length;n++)r.push({eventId:u[n],projectId:a.project.id,input:a.inputs[n],deliveredTitle:a.delivered[n]??null,messaging:a.messaging[n]??null,terminalEnv:a.terminals[n]??null})}catch{c.push(...a.lines)}if(c.length===0){e.delete(t);try{b(t)}catch{}return r}const p=(e.get(t)??0)+1;if(e.set(t,p),p>=$){e.delete(t);try{h(t,t.replace(INBOX_CLAIM_SUFFIX,INBOX_FAILED_SUFFIX))}catch{}return r}try{E(t,c.join(`
3
3
  `)+`
4
- `)}catch{}return r}function b(n,o,e){const i=n.hookEvents.insertMany(e),s=new Map;for(const r of e){if(!r.sessionId)continue;const c=s.get(r.sessionId),p=r.createdAt;(!s.has(r.sessionId)||p&&(!c||p>c))&&s.set(r.sessionId,p)}for(const[r,c]of s)n.hookSessions.touch(r,o.id,c);return i}export const LOGS_TICK_MS=1e3;export async function pollLogsOnce(n){if(n.sources.length===0)return[];const o=new Map;for(const i of n.projects.list()){const s=await C(i.path);n.sources.some(r=>s.includes(r.harness))&&o.set(i.path,i)}if(o.size===0)return[];const e=[];for(const i of n.sources){const s=n.adapters.find(p=>p.source===i.harness);if(!s)continue;let r;try{r=await i.poll({stateDir:f(n.logsDir,i.harness),cwdWhitelist:[...o.keys()]})}catch{continue}const c=new Map;for(const p of r){const u=p.report.cwd?o.get(p.report.cwd):void 0;if(u)try{const a=s.normalize(p.report,u.id);a.createdAt=p.at;const t=c.get(u);t?t.push(a):c.set(u,[a])}catch{}}for(const[p,u]of c)try{const a=b(n,p,u);for(let t=0;t<u.length;t++)e.push({eventId:a[t],projectId:p.id,input:u[t]})}catch(a){console.error("[weave] log ingest failed:",a)}}return e}function z(n){try{const o=JSON.parse(n);if(!o||typeof o.token!="string"||!o.token||!o.report||typeof o.report!="object")return null;const e=o.messaging?.socket,i=typeof e=="string"&&e?{socket:e,token:typeof o.messaging?.token=="string"?o.messaging.token:null}:null;return{token:o.token,report:o.report,messaging:i,terminalEnv:K(o.terminal)}}catch{return null}}function K(n){if(typeof n!="object"||n===null)return null;const o={};for(const[e,i]of Object.entries(n))typeof i=="string"&&i&&(o[e]=i);return Object.keys(o).length>0?o:null}export function unparkFailedInbox(n){const o=f(n,"inbox");let e;try{e=I(o)}catch{return 0}let i=0;for(const s of e){if(!s.endsWith(INBOX_FAILED_SUFFIX))continue;const r=f(o,`${s.slice(0,-INBOX_FAILED_SUFFIX.length)}${INBOX_CLAIM_SUFFIX}`);if(!v(r))try{g(f(o,s),r),i++}catch{}}return i}export function handleIngestedRecords(n,o){for(const e of o){const i=n.projects.getById(e.projectId);if(!i)continue;n.dashboardRate?.record();let s=null;try{s=JSON.parse(e.input.payload)}catch{s=null}if(n.bus?.emit({kind:"event.ingested",eventId:e.eventId,projectId:i.id,sessionId:e.input.sessionId??null,eventType:e.input.eventType,...P(s),at:e.input.createdAt??new Date().toISOString()}),e.input.sessionId){n.questionTracker?.onEvent({sessionId:e.input.sessionId,source:e.input.source,payload:s}),n.sideQuestions?.onEvent({sessionId:e.input.sessionId,source:e.input.source,eventType:e.input.eventType,payload:s,eventId:e.eventId}),n.watchEngine?.onEvent({sessionId:e.input.sessionId,eventType:e.input.eventType,source:e.input.source}),n.orchestrationEngine?.onEvent({sessionId:e.input.sessionId,eventType:e.input.eventType,source:e.input.source}),e.messaging&&n.messagePipe?.noteInboxSocket?.(e.input.sessionId,e.messaging),n.sessionProbe?.noteTranscript(e.input.sessionId,s?.transcriptPath??null);const r=L(e.input.eventType,s?.raw);r&&n.sessionProbe?.noteModel?.(e.input.sessionId,r.to);const c=A(e.input.eventType,s?.raw);c&&n.sessionProbe?.sessionStarted?.(e.input.sessionId,c.model);const p=O(s?.raw);p&&n.sessionProbe?.noteEffort?.(e.input.sessionId,p);const u=D(s?.raw);if(u&&n.sessionProbe?.noteMode?.(e.input.sessionId,u),e.terminalEnv||m(e.input.source)){const t=e.input.source;m(t)&&n.paneEnvSource?.(t)==="launch"?n.messagePipe?.claimPendingOrigin?.(e.input.sessionId,t,e.projectId):e.terminalEnv&&n.messagePipe?.notePaneEnv?.(e.input.sessionId,e.terminalEnv)}if(e.input.eventType==="UserPromptSubmit"){const t=n.messagePipe?.onSessionPrompt?.(e.input.sessionId,J(s)??"",e.eventId)??[];t.length>0&&n.uploads?.linkToEvent(e.eventId,t)}else n.messagePipe?.noteActivity?.(e.input.sessionId);const a=m(e.input.source)?W[e.input.source].sessionExit:null;a!==null&&e.input.eventType===a&&(n.messagePipe?.sessionExited?.(e.input.sessionId),n.terminalTopologyKick?.()),n.messagePipe?.hasQueued(e.input.sessionId)&&n.messagePipe.flushSession(e.input.sessionId)}n.dispatcher&&n.dispatcher.onIngest({eventId:e.eventId,project:i,sessionId:e.input.sessionId??null,payload:s??{eventType:e.input.eventType,toolName:null,toolInput:null,toolResponse:null,cwd:null,transcriptPath:null,raw:null},source:e.input.source,deliveredTitle:e.deliveredTitle??null}).then(r=>{r.deferred&&e.input.sessionId&&X(n.hooksDir,i.token,[{sessionId:e.input.sessionId,text:r.deferred}])}).catch(()=>{})}}function J(n){const o=n?.raw;return typeof o?.prompt=="string"&&o.prompt?o.prompt:null}export function startInboxWorker(n){return new x(new URL(import.meta.url),{workerData:{inboxWorker:!0,...n}})}if(!E&&k?.inboxWorker===!0){const n=k,o=F(n.dbPath),e=M(),i=new N(o),s={hooksDir:n.hooksDir,projects:i,hookEvents:new R(o),hookSessions:new B(o),adapters:e.byCapability("events").flatMap(t=>t.events??[])};h?.on("message",t=>{t?.type==="synthetic-sessions"&&Array.isArray(t.ids)&&U(t.ids)});const r=new Map,c=()=>{try{const t=drainInboxOnce(s,r);t.length>0&&h?.postMessage(t)}catch(t){console.error("[weave] inbox drain failed:",t)}};c(),setInterval(c,n.tickMs??INBOX_TICK_MS);const p={projects:i,hookEvents:s.hookEvents,hookSessions:s.hookSessions,adapters:s.adapters,logsDir:_(),sources:e.byCapability("logs").flatMap(t=>t.logs??[])};let u=!1;const a=()=>{u||(u=!0,pollLogsOnce(p).then(t=>{t.length>0&&h?.postMessage(t)}).catch(t=>console.error("[weave] log poll failed:",t)).finally(()=>{u=!1}))};p.sources.length>0&&(a(),setInterval(a,LOGS_TICK_MS))}
4
+ `)}catch{}return r}function S(s,t,e){const i=s.hookEvents.insertMany(e),o=new Map;for(const r of e){if(!r.sessionId)continue;const c=o.get(r.sessionId),p=r.createdAt;(!o.has(r.sessionId)||p&&(!c||p>c))&&o.set(r.sessionId,p)}for(const[r,c]of o)s.hookSessions.touch(r,t.id,c);return i}export const LOGS_TICK_MS=1e3;export async function pollLogsOnce(s){if(s.sources.length===0)return[];const t=new Map;for(const i of s.projects.list()){const o=await L(i.path);s.sources.some(r=>o.includes(r.harness))&&t.set(i.path,i)}if(t.size===0)return[];const e=[];for(const i of s.sources){const o=s.adapters.find(p=>p.source===i.harness);if(!o)continue;let r;try{r=await i.poll({stateDir:d(s.logsDir,i.harness),cwdWhitelist:[...t.keys()]})}catch{continue}const c=new Map;for(const p of r){const a=p.report.cwd?t.get(p.report.cwd):void 0;if(a)try{const u=o.normalize(p.report,a.id);u.createdAt=p.at;const n=c.get(a);n?n.push(u):c.set(a,[u])}catch{}}for(const[p,a]of c)try{const u=S(s,p,a);for(let n=0;n<a.length;n++)e.push({eventId:u[n],projectId:p.id,input:a[n]})}catch(u){console.error("[weave] log ingest failed:",u)}}return e}function z(s){try{const t=JSON.parse(s);if(!t||typeof t.token!="string"||!t.token||!t.report||typeof t.report!="object")return null;const e=t.messaging?.socket,i=typeof e=="string"&&e?{socket:e,token:typeof t.messaging?.token=="string"?t.messaging.token:null}:null;return{token:t.token,report:t.report,messaging:i,terminalEnv:K(t.terminal)}}catch{return null}}function K(s){if(typeof s!="object"||s===null)return null;const t={};for(const[e,i]of Object.entries(s))typeof i=="string"&&i&&(t[e]=i);return Object.keys(t).length>0?t:null}export function unparkFailedInbox(s){const t=d(s,"inbox");let e;try{e=v(t)}catch{return 0}let i=0;for(const o of e){if(!o.endsWith(INBOX_FAILED_SUFFIX))continue;const r=d(t,`${o.slice(0,-INBOX_FAILED_SUFFIX.length)}${INBOX_CLAIM_SUFFIX}`);if(!I(r))try{h(d(t,o),r),i++}catch{}}return i}export function handleIngestedRecords(s,t){for(const e of t){const i=s.projects.getById(e.projectId);if(!i)continue;s.dashboardRate?.record();let o=null;try{o=JSON.parse(e.input.payload)}catch{o=null}if(s.bus?.emit({kind:"event.ingested",eventId:e.eventId,projectId:i.id,sessionId:e.input.sessionId??null,eventType:e.input.eventType,...O(o),at:e.input.createdAt??new Date().toISOString()}),e.input.sessionId){s.questionTracker?.onEvent({sessionId:e.input.sessionId,source:e.input.source,payload:o}),s.sideQuestions?.onEvent({sessionId:e.input.sessionId,source:e.input.source,eventType:e.input.eventType,payload:o,eventId:e.eventId}),s.watchEngine?.onEvent({sessionId:e.input.sessionId,eventType:e.input.eventType,source:e.input.source}),s.orchestrationEngine?.onEvent({sessionId:e.input.sessionId,eventType:e.input.eventType,source:e.input.source}),e.messaging&&s.messagePipe?.noteInboxSocket?.(e.input.sessionId,e.messaging),s.sessionProbe?.noteTranscript(e.input.sessionId,o?.transcriptPath??null);const r=A(e.input.eventType,o?.raw);r&&s.sessionProbe?.noteModel?.(e.input.sessionId,r.to);const c=W(e.input.eventType,o?.raw);c&&s.sessionProbe?.sessionStarted?.(e.input.sessionId,c.model);const p=M(o?.raw);p&&s.sessionProbe?.noteEffort?.(e.input.sessionId,p);const a=D(o?.raw);if(a&&s.sessionProbe?.noteMode?.(e.input.sessionId,a),e.terminalEnv||g(e.input.source)){const n=e.input.source;g(n)&&s.paneEnvSource?.(n)==="launch"?s.messagePipe?.claimPendingOrigin?.(e.input.sessionId,n,e.projectId):e.terminalEnv&&s.messagePipe?.notePaneEnv?.(e.input.sessionId,e.terminalEnv)}if(e.input.eventType==="UserPromptSubmit"){const n=s.messagePipe?.onSessionPrompt?.(e.input.sessionId,J(o)??"",e.eventId)??[];n.length>0&&s.uploads?.linkToEvent(e.eventId,n)}else s.messagePipe?.noteActivity?.(e.input.sessionId);const u=g(e.input.source)?F[e.input.source].sessionExit:null;u!==null&&e.input.eventType===u&&(s.messagePipe?.sessionExited?.(e.input.sessionId),s.terminalTopologyKick?.()),s.messagePipe?.hasQueued(e.input.sessionId)&&s.messagePipe.flushSession(e.input.sessionId)}s.dispatcher&&s.dispatcher.onIngest({eventId:e.eventId,project:i,sessionId:e.input.sessionId??null,payload:o??{eventType:e.input.eventType,toolName:null,toolInput:null,toolResponse:null,cwd:null,transcriptPath:null,raw:null},source:e.input.source,deliveredTitle:e.deliveredTitle??null}).then(r=>{r.deferred&&e.input.sessionId&&X(s.hooksDir,i.token,[{sessionId:e.input.sessionId,text:r.deferred}])}).catch(()=>{})}}function J(s){const t=s?.raw;return typeof t?.prompt=="string"&&t.prompt?t.prompt:null}export function startInboxWorker(s){return new P(new URL(import.meta.url),{workerData:{inboxWorker:!0,...s}})}if(!T&&k?.inboxWorker===!0){const s=k,t=_(s.dbPath),e=x(),i=new N(t),o={hooksDir:s.hooksDir,projects:i,hookEvents:new C(t),hookSessions:new B(t),adapters:e.byCapability("events").flatMap(n=>n.events??[])};m?.on("message",n=>{n?.type==="synthetic-sessions"&&Array.isArray(n.ids)&&U(n.ids)});const r=new Map,c=()=>{try{const n=drainInboxOnce(o,r);n.length>0&&m?.postMessage(n)}catch(n){console.error("[weave] inbox drain failed:",n)}};c(),setInterval(c,s.tickMs??INBOX_TICK_MS);const p={projects:i,hookEvents:o.hookEvents,hookSessions:o.hookSessions,adapters:o.adapters,logsDir:R(),sources:e.byCapability("logs").flatMap(n=>n.logs??[])};let a=!1;const u=()=>{a||(a=!0,pollLogsOnce(p).then(n=>{n.length>0&&m?.postMessage(n)}).catch(n=>console.error("[weave] log poll failed:",n)).finally(()=>{a=!1}))};p.sources.length>0&&(u(),setInterval(u,LOGS_TICK_MS))}
@@ -1 +1 @@
1
- import f from"node:path";export const DISPATCH_MARKER="<!--weave-dispatch-->";const s=e=>process.platform==="win32"?e.toLowerCase():e;export function normalizePosix(e){return e.replace(/\\/g,"/")}function a(e){return/[.+^${}()|[\]\\]/.test(e)?`\\${e}`:e}const l=new Map;export function globToRegex(e){const r=l.get(e);if(r)return r;let t="";for(let n=0;n<e.length;n++){const c=e[n];c==="*"?e[n+1]==="*"?(n++,e[n+1]==="/"?(n++,t+="(?:.*/)?"):t+=".*"):t+="[^/]*":c==="?"?t+="[^/]":t+=a(c)}const o=new RegExp(`^${t}$`,"i");return l.set(e,o),o}export function matchGlob(e,r){return globToRegex(r).test(normalizePosix(e))}export function extractFilePath(e){if(typeof e!="object"||e===null)return null;const r=e;for(const t of["file_path","notebook_path","filePath"]){const o=r[t];if(typeof o=="string"&&o.length>0)return o}return null}function i(e,r){const t=s(normalizePosix(e)),o=s(normalizePosix(r).replace(/\/+$/,""));return t===o||t.startsWith(`${o}/`)}export function isCrossProjectPath(e,r,t){const o=s(normalizePosix(r));return i(e,r)?!1:t.some(n=>s(normalizePosix(n))!==o&&i(e,n))}export function renderTemplate(e,r){return e.replace(/\{(\w+)\}/g,(t,o)=>{const n=r[o];return n==null?t:String(n)})}export function templateVars(e){return{event:e.event,tool:e.tool,file:e.filePath?normalizePosix(e.filePath):null,cwd:e.cwd?normalizePosix(e.cwd):null,session:e.session,project:e.project,rule:e.rule}}export function matchesAnyGlob(e,r){return r.some(t=>matchGlob(e,t))}export function projectLabel(e){return f.basename(normalizePosix(e).replace(/\/+$/,""))||e}
1
+ import i from"node:path";export const DISPATCH_MARKER="<!--weave-dispatch-->";const c=e=>process.platform==="win32"?e.toLowerCase():e;export function normalizePosix(e){return e.replace(/\\/g,"/")}function p(e){return/[.+^${}()|[\]\\]/.test(e)?`\\${e}`:e}const s=new Map;export function globToRegex(e){const t=s.get(e);if(t)return t;let n="";for(let r=0;r<e.length;r++){const l=e[r];l==="*"?e[r+1]==="*"?(r++,e[r+1]==="/"?(r++,n+="(?:.*/)?"):n+=".*"):n+="[^/]*":l==="?"?n+="[^/]":n+=p(l)}const o=new RegExp(`^${n}$`,"i");return s.set(e,o),o}export function matchGlob(e,t){return globToRegex(t).test(normalizePosix(e))}export function extractFilePath(e){if(typeof e!="object"||e===null)return null;const t=e;for(const n of["file_path","notebook_path","filePath"]){const o=t[n];if(typeof o=="string"&&o.length>0)return o}return null}function u(e,t){const n=c(normalizePosix(e)),o=c(normalizePosix(t).replace(/\/+$/,""));return n===o||n.startsWith(`${o}/`)}export function isCrossProjectPath(e,t,n){const o=c(normalizePosix(t));return u(e,t)?!1:n.some(r=>c(normalizePosix(r))!==o&&u(e,r))}export function renderTemplate(e,t){return e.replace(/\{(\w+)\}/g,(n,o)=>{const r=t[o];return r==null?n:String(r)})}export function templateVars(e){return{event:e.event,tool:e.tool,file:e.filePath?normalizePosix(e.filePath):null,cwd:e.cwd?normalizePosix(e.cwd):null,session:e.session,project:e.project,rule:e.rule}}export function matchesAnyGlob(e,t){return t.some(n=>matchGlob(e,n))}export function projectLabel(e){return i.basename(normalizePosix(e).replace(/\/+$/,""))||e}
@@ -1,5 +1,5 @@
1
- import{randomUUID as K}from"node:crypto";import{chmodSync as v,mkdirSync as w,readFileSync as E,renameSync as O,writeFileSync as I}from"node:fs";import{dirname as T,join as g}from"node:path";import{isHarness as S}from"@weavecode/core";import{commandModeAt as q,turnOutputOf as B}from"@weavecode/harness";import{isImageMediaType as b}from"../repositories/uploads.js";import{commandsSurfaceFor as L,messagingSurfaceFor as F}from"../surfaces.js";import{pidAliveOf as Q}from"../daemon/claude-liveness.js";import{combinedLivenessOf as A}from"../daemon/liveness.js";import{HeadlessRunner as G}from"./headless-runner.js";import{hooksRoot as W}from"./mirror.js";import{INBOX_TARGETS_FILE as J,InboxSocketRegistry as U,deliverViaInboxSocket as V}from"./inbox-socket.js";import{AckGate as H}from"./delivery/ack-gate.js";import{ANSWER_ATOM as D,ANSWER_ATOM_GAP_MS as Y,ANSWER_TEXT as $,CTRL_B as X,CTRL_C_KITTY as z,ENTER as Z,ESC as ee,ESC_KITTY as te,MODEL_NAME as se,SHIFT_TAB as ie,effortCommand as ne,effortLevelsOf as re,locateTerminalPane as ae,modelCommand as oe,sessionKeysTarget as le}from"./delivery/keys.js";import{hasImages as ue,settleDelivered as P,spawnOnce as he,wireTextOf as de}from"./delivery/channels.js";import{deliverHeadless as ce,deliverInject as me,resolveChannel as fe}from"./delivery/send.js";import{appendQueueEntry as pe,failOffline as ge,failQueuedOffline as ke,sessionJudgedDead as Se}from"./delivery/queue.js";import{runRelay as ye}from"./delivery/relay.js";import{listInflightCounts as ve,listVisible as we,sessionTerminals as Ee}from"./delivery/views.js";import{readPromptbox as k,removePromptboxBySession as Oe,removePromptboxEntries as p,scanQueuedSessionIds as Ie}from"./promptbox.js";const Te=1e3,be=6e4,Ae=3e4,De=9e4,Pe=5e3,_e=8,Me=12e4,Ce=1200,xe=/interrupted/i,je=/· done/,Re=12,Ne=[te,ee,X],Ke=600,qe=250,Be=5,Le=200,_="pane-envs.json",M="pending-origins.json",C=15*6e4;function Fe(f){try{const e=JSON.parse(E(f,"utf8"));if(!Array.isArray(e))return[];const t=[];for(const s of e){if(typeof s!="object"||s===null)continue;const i=s;if(!S(i.harness)||typeof i.projectId!="number"||typeof i.env!="object"||i.env===null||typeof i.at!="string")continue;const n={};for(const[r,l]of Object.entries(i.env))typeof l=="string"&&l&&(n[r]=l);Object.keys(n).length!==0&&t.push({harness:i.harness,projectId:i.projectId,env:n,at:i.at})}return t.filter(s=>Date.now()-Date.parse(s.at)<C)}catch{return[]}}function Qe(f){try{const e=JSON.parse(E(f,"utf8"));if(typeof e!="object"||e===null)return{};const t={};for(const[s,i]of Object.entries(e)){if(typeof i!="object"||i===null)continue;const n={};for(const[r,l]of Object.entries(i))typeof l=="string"&&l&&(n[r]=l);Object.keys(n).length>0&&(t[s]=n)}return t}catch{return{}}}const m=f=>new Promise(e=>setTimeout(e,f)),x=300,j=1500,Ge=1e3,We=12e4;export class MessageDeliverer{deps;headless;hooksDir;sideAnswerStore;memory=new Map;queuedSessions;inboxSockets;paneEnvs;revokedPaneEnvs=new Set;paneEnvConflicts=new Map;persistedPaneEnvs;pendingOrigins;persistedPendingOrigins;flushScheduled=new Set;flushing=new Map;stalled=new Map;awaitingAck=new Map;dismissedIds=new Set;gate;constructor(e){this.deps=e,this.headless=e.headless??new G({spawnImpl:e.spawnImpl,killImpl:e.killImpl}),this.hooksDir=e.hooksDir??W(),this.sideAnswerStore=e.sideAnswerStore,this.inboxSockets=new U(g(this.hooksDir,J));const t=Qe(g(this.hooksDir,_));this.paneEnvs=new Map(Object.entries(t)),this.persistedPaneEnvs=JSON.stringify(t),this.pendingOrigins=Fe(g(this.hooksDir,M)),this.persistedPendingOrigins=JSON.stringify(this.pendingOrigins),this.queuedSessions=Ie(this.hooksDir),this.gate=new H({awaitingAck:this.awaitingAck,stalled:this.stalled,memory:this.memory},{ackStallMs:e.ackStallMs??Ae,ackStallReleaseMs:e.ackStallReleaseMs??De,commandAckGraceMs:e.commandAckGraceMs??Pe},{emit:s=>this.bus(s)},(s,i)=>this.commandModeOf(s,i)!==null),this.gate.onAutoRelease=s=>this.scheduleFlushContinuation(s)}async send(e){const t=this.deps.sessions.getBySessionId(e.toSessionId);if(!t)return{ok:!1,status:404,error:"Session not found"};const s=this.surfaceOfSession(e.toSessionId);if(!s)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};const i=e.attachments?[...e.attachments]:[];if(i.length>0){if(!s.attach)return{ok:!1,status:409,error:"This harness cannot receive attachments"};if(i.some(d=>b(d.mediaType))&&!s.attach.kinds.includes("image"))return{ok:!1,status:409,error:"This harness cannot receive images"};if(i.some(d=>!b(d.mediaType))&&!s.attach.kinds.includes("file"))return{ok:!1,status:409,error:"This harness cannot receive file attachments"}}const n=this.resolveChannel(e.mode,e.toSessionId,s);if(typeof n!="string")return{ok:!1,status:409,error:n.reason};if(t.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const r=this.deps.projects.getById(t.projectId);if(!r)return{ok:!1,status:404,error:"Project not found"};if(n==="headless"&&this.headless.isBusy(e.toSessionId))return{ok:!1,status:409,error:"Session already has a running run"};const l={fromLabel:e.fromKind==="session"?`session ${e.fromSessionId??"unknown"}`:e.fromKind,at:new Date().toISOString(),mode:n},a={id:K(),sessionId:e.toSessionId,text:e.text,...i.length>0?{attachments:i}:{},mode:e.mode,oneWay:e.oneWay===!0,...e.forceTerminal===!0?{forceTerminal:!0}:{},channel:n,status:"pending",at:l.at},o=n==="prompt"&&this.blockedAhead(a.sessionId);o&&(this.queueEntry(a,r.token),this.scheduleFlushContinuation(a.sessionId));const h=o?{ok:!0,messageId:a.id,status:"pending",message:a}:n==="inject"?this.deliverInject(a,r.token,l,s):n==="headless"?this.deliverHeadless(a,r.path,l,s):await this.deliverPrompt(a,t,r,s);return this.bus({kind:"message.created",messageId:a.id,sessionId:a.sessionId,mode:a.mode,at:a.at}),h}getMessage(e){return this.memory.get(e)??null}listVisible(e){return we(this.deps,this.hooksDir,this.memory,e,this.deps.visibleTtlMs??be,t=>this.commandModeOf(e,t)!==null)}hasQueued(e){return this.queuedSessions.has(e)}listInflightCounts(){return ve(this.hooksDir,this.memory)}hasTerminalChannel(){return this.terminalChannel()!=null}scheduleFlushContinuation(e){if(this.flushScheduled.has(e))return;this.flushScheduled.add(e),setTimeout(()=>{this.flushScheduled.delete(e),this.flushSession(e)},this.deps.flushGapMs??Te).unref?.()}ackGateOpen(e){return this.gate.open(e)}blockedAhead(e){return this.ackGateOpen(e)?this.queuedSessions.has(e)||this.flushing.has(e):!0}noteInboxSocket(e,t){this.inboxSockets.set(e,t)}async awaitInboxReady(e,t){const s=this.surfaceOfSession(e)?.promptInbox??null;if(!s)return!1;const i=()=>{const r=this.inboxSockets.get(e);return!!r&&(!s.requiresAuthOn(process.platform)||!!r.token)},n=Date.now()+t;for(;!i();){if(Date.now()>=n)return!1;await new Promise(r=>setTimeout(r,qe))}return!0}lastTurnOutput(e){const t=this.deps.hookEvents.listBySession(e,Be);for(let s=t.length-1;s>=0;s--){const i=t[s];let n=null;try{n=JSON.parse(i.payload)}catch{continue}const r=B(n?.raw);if(r)return{output:r,at:i.createdAt}}return null}notePaneEnv(e,t){const s=JSON.stringify(t);for(const[i,n]of this.paneEnvs){if(i===e||JSON.stringify(n)!==s)continue;const r=this.deps.sessions.getBySessionId(i)?.lastSeenAt;if((r?Date.now()-Date.parse(r):Number.POSITIVE_INFINITY)<Me){this.paneEnvs.delete(e),this.paneEnvConflicts.set(e,i),this.persistPaneEnvs();return}this.paneEnvs.delete(i),this.revokedPaneEnvs.add(i)}this.paneEnvConflicts.delete(e),this.paneEnvs.set(e,t),this.revokedPaneEnvs.delete(e),this.persistPaneEnvs()}paneEnvConflictOf(e){return this.paneEnvConflicts.get(e)??null}notePendingOrigin(e){this.pendingOrigins=this.pendingOrigins.filter(t=>Date.now()-Date.parse(t.at)<C),this.pendingOrigins.push({...e,at:new Date().toISOString()}),this.persistPendingOrigins()}claimPendingOrigin(e,t,s){const i=this.pendingOrigins.findIndex(r=>r.harness===t&&r.projectId===s);if(i<0)return;const[n]=this.pendingOrigins.splice(i,1);this.notePaneEnv(e,n.env),this.persistPendingOrigins()}persistPaneEnvs(){const e=JSON.stringify(Object.fromEntries(this.paneEnvs));if(e===this.persistedPaneEnvs)return;const t=g(this.hooksDir,_);try{w(T(t),{recursive:!0});const s=`${t}.tmp`;I(s,`${e}
2
- `,{encoding:"utf8",mode:384}),O(s,t),v(t,384),this.persistedPaneEnvs=e}catch{}}persistPendingOrigins(){const e=JSON.stringify(this.pendingOrigins);if(e===this.persistedPendingOrigins)return;const t=g(this.hooksDir,M);try{w(T(t),{recursive:!0});const s=`${t}.tmp`;I(s,`${e}
3
- `,{encoding:"utf8",mode:384}),O(s,t),v(t,384),this.persistedPendingOrigins=e}catch{}}sessionExited(e){this.inboxSockets.forget(e),this.paneEnvs.delete(e),this.revokedPaneEnvs.add(e),this.persistPaneEnvs(),this.awaitingAck.delete(e);const t=this.deps.sessions.getBySessionId(e),s=t?.projectId==null?null:this.deps.projects.getById(t.projectId);if(!s)return;const i=k(this.hooksDir,s.token).filter(n=>n.sessionId===e);this.failQueuedOffline(e,i.filter(n=>!this.dismissedIds.has(n.id)))}async sessionTerminals(){return Ee(this.deps,this.terminalChannel(),this.paneEnvs,()=>this.persistPaneEnvs(),this.revokedPaneEnvs)}diagnose(e){const t=this.surfaceOfSession(e),s=this.deps.sessions.livenessRowOf(e),i=s?Q(s,this.deps.livenessOf?.()??A()):null,n=t?.promptInbox??null,r=this.inboxSockets.get(e),l=n?r?n.requiresAuthOn(process.platform)&&!r.token?{status:"unavailable",reason:"auth-token-required"}:{status:"ready",reason:null}:{status:"unavailable",reason:"no-credentials"}:{status:"unavailable",reason:"no-mechanism"},a=t?.queuedPrompt?{status:"ready",reason:null}:{status:"unavailable",reason:"no-mechanism"},o=t?.prompt?.resolve(e)??null,h=t?.prompt?o?{status:"ready",reason:null}:{status:"unavailable",reason:"no-peer-name"}:{status:"unavailable",reason:"no-mechanism"},d=this.paneEnvs.has(e),u=this.terminalChannel()?d?{status:"ready",reason:null}:this.paneEnvConflicts.has(e)?{status:"unavailable",reason:"pane-env-conflict"}:{status:"unknown",reason:"no-pane-env"}:{status:"unavailable",reason:"no-backend"},c=this.deps.sessions.getBySessionId(e),y=c?.projectId==null?null:this.deps.projects.getById(c.projectId),R=y?k(this.hooksDir,y.token).filter(N=>N.sessionId===e).length:0;return{alive:i,inbox:l,queue:a,relay:h,terminal:u,paneEnvReported:d,pending:R,stalled:this.gate.isStalled(e)}}onSessionPrompt(e,t,s){const i=this.gate.acknowledge(e,t,s,{ackMinText:_e});return this.gate.hitLast&&this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e),i}noteActivity(e){this.gate.noteActivity(e)&&this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e)}armAckWatch(e){this.gate.armAckWatch(e)}armCommandAck(e){this.gate.armCommandAck(e,t=>{this.queuedSessions.has(t)&&this.scheduleFlushContinuation(t)})}stall(e,t,s){this.gate.stall(e,t,s)}async answer(e,t){if(t.length===0||!D.test(t[0])||t.some(i=>!D.test(i)&&!$.test(i)))return{ok:!1,status:400,error:"Invalid answer keys"};const s=await this.sessionKeysTarget(e);if(!s.ok)return s;try{for(const[i,n]of t.entries())i>0&&await m(Y),await s.terminal.sendKeys(s.paneId,n);return{ok:!0}}catch(i){return{ok:!1,status:409,error:i instanceof Error?i.message:String(i)}}}async interrupt(e){const t=await this.sessionKeysTarget(e,"\u51FB\u952E");if(!t.ok)return t;const s=async()=>{try{return await t.terminal.getPaneText?.(t.paneId)??null}catch{return null}},i=l=>{const a=l.split(`
4
- `).slice(-Re).join(`
5
- `);return xe.test(a)||je.test(a)},n=this.deps.interruptVerifyMs??Ce;let r=!1;for(const[l,a]of Ne.entries()){try{await t.terminal.sendKeys(t.paneId,a)}catch(h){if(l===0)return{ok:!1,status:409,error:h instanceof Error?h.message:String(h)};break}await m(l===0?n:Ke);const o=await s();if(o===null)return{ok:!0,paneId:t.paneId,sent:!0,confirmed:!0};if(i(o)){r=!0;break}}return{ok:!0,paneId:t.paneId,sent:!0,confirmed:r}}async closePane(e){const t=await this.sessionKeysTarget(e,"\u5173\u95ED");if(!t.ok)return t;try{return await t.terminal.closePane(t.paneId),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async cycleMode(e){const t=await this.sessionKeysTarget(e);if(!t.ok)return t;try{return await t.terminal.sendKeys(t.paneId,ie),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async pressEnter(e){const t=await this.sessionKeysTarget(e);if(!t.ok)return t;try{return await t.terminal.sendKeys(t.paneId,Z),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async pressCtrlC(e){const t=await this.sessionKeysTarget(e);if(!t.ok)return t;try{return await t.terminal.sendKeys(t.paneId,z),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async setModel(e,t){const s=t.trim();if(!se.test(s))return{ok:!1,status:400,error:"\u6A21\u578B\u540D\u5F62\u6001\u4E0D\u5408\u6CD5\uFF08\u4E0D\u542B\u7A7A\u767D\u4E0E\u63A7\u5236\u5B57\u7B26\uFF0C\u2264128 \u5B57\u7B26\uFF09"};const i=await this.sessionKeysTarget(e);if(!i.ok)return i;try{return await i.terminal.sendText(i.paneId,oe(s)),{ok:!0}}catch(n){return{ok:!1,status:409,error:n instanceof Error?n.message:String(n)}}}async setEffort(e,t){const s=t.trim(),i=await this.sessionKeysTarget(e);if(!i.ok)return i;const n=re(this.deps.hookEvents,e);if(!n.includes(s))return{ok:!1,status:400,error:`\u601D\u8003\u5F3A\u5EA6\u6863\u4F4D\u4E0D\u5408\u6CD5\uFF08\u53EF\u9009\uFF1A${n.join(" / ")||"\u65E0"}\uFF09`};try{return await i.terminal.sendText(i.paneId,ne(s)),{ok:!0}}catch(r){return{ok:!1,status:409,error:r instanceof Error?r.message:String(r)}}}async askSideQuestion(e,t){if(!this.deps.sessions.getBySessionId(e))return{ok:!1,status:404,error:"Session not found"};const s=this.surfaceOfSession(e),i=s?.sideQuestion??null;if(!i)return{ok:!1,status:409,error:s?.unsupportedReasons?.sideQuestion??"\u8FD9\u4E2A harness \u6CA1\u6709\u65C1\u8DEF\u4F1A\u8BDD\u901A\u9053"};const n=i.validate(t);if(!n.ok)return{ok:!1,status:400,error:n.error};if(i.prerequisite&&this.deps.sessions.hasRunOf(e)!==!0)return{ok:!1,status:409,error:i.prerequisite};const r=await this.sessionKeysTarget(e,"\u53D1\u8D77\u65C1\u8DEF\u4F1A\u8BDD");if(!r.ok)return r;try{return i.screen?(this.pollScreenAnswer(e,r.terminal,r.paneId,i,n.text),{ok:!0,question:n.text}):(await r.terminal.sendText(r.paneId,i.command(n.text)),{ok:!0,question:n.text})}catch(l){return{ok:!1,status:409,error:l instanceof Error?l.message:String(l)}}}pollScreenAnswer(e,t,s,i,n){const r=i.screen,l=async()=>await t.getPaneText?.(s).catch(()=>null)??null;(async()=>{const a=await l()??"";await t.sendKeys(s,r.openKeys).catch(()=>{}),await m(x),await t.sendKeys(s,r.submitKey).catch(()=>{}),await m(j);let o=await l();if((!o||!r.panelOpen(o))&&(await t.sendKeys(s,r.submitKey).catch(()=>{}),await m(j),o=await l(),!o||!r.panelOpen(o)))return;await t.sendText(s,n).catch(()=>{}),await m(x),await t.sendKeys(s,r.submitKey).catch(()=>{});const h=Date.now()+We;let d=null;for(;Date.now()<h;){if(await m(Ge),o=await l(),o==null)return;const c=r.extractAnswer(n,a,o);if(c.done){d=c.answer;break}}d!=null&&(this.sideAnswerStore?.answerById(e,n,d),this.bus({kind:"sideQuestion.answered",sessionId:e,at:new Date().toISOString()}));const u=await l();u&&r.panelOpen(u)&&await t.sendKeys(s,r.closeKey).catch(()=>{})})()}async sessionKeysTarget(e,t="\u51FB\u952E"){return le(this.keysDeps(),e,t)}busSafe(){return{emit:e=>this.bus(e)}}channelDeps(){return{setMemory:e=>this.memory.set(e.id,e),deleteMemory:e=>this.memory.delete(e),awaitAck:(e,t)=>this.gate.await(e,t),commandModeOf:(e,t)=>this.commandModeOf(t,e),armAckWatch:e=>this.gate.armAckWatch(e),armCommandAck:e=>this.gate.armCommandAck(e,t=>{this.queuedSessions.has(t)&&this.scheduleFlushContinuation(t)}),emit:e=>this.bus(e),...this.deps.spawnImpl?{spawnImpl:this.deps.spawnImpl}:{}}}wireTextOfEntry(e){return de(e,t=>this.surfaceOfSession(t))}hasImagesEntry(e){return ue(e)}keysDeps(){return{sessions:this.deps.sessions,projects:this.deps.projects,hookEvents:this.deps.hookEvents,terminalChannel:()=>this.terminalChannel(),terminalPaneFor:(e,t)=>this.terminalPaneFor(e,t),paneEnvConflictOf:e=>this.paneEnvConflictOf(e)}}async resend(e){const t=this.memory.get(e);if(!t)return{ok:!1,status:404,error:"Message not found"};if(!(t.channel==="prompt"&&(t.status==="failed"||t.status==="delivered"&&!t.acked)))return{ok:!1,status:409,error:"Message is not resendable"};const i=this.deps.sessions.getBySessionId(t.sessionId);if(!i)return{ok:!1,status:404,error:"Session not found"};if(i.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const n=this.deps.projects.getById(i.projectId);if(!n)return{ok:!1,status:404,error:"Project not found"};const r=this.surfaceOfSession(t.sessionId);if(!r)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};if(this.gate.clearStall(t.sessionId,e),t.status==="delivered"&&t.via==="terminal"&&this.terminalChannel()){const o=await this.terminalPaneFor(i,n);if(o)return this.memory.set(e,{...t,status:"delivering",stalled:!1,settledAt:null}),this.runTerminalSubmit(e,o.id),this.bus({kind:"message.created",messageId:e,sessionId:t.sessionId,mode:t.mode,at:new Date().toISOString()}),{ok:!0,messageId:e,status:"delivering",message:this.memory.get(e)}}this.memory.delete(e);const l={...t,status:"pending",stalled:!1,acked:!1,settledAt:null,reason:void 0,error:null},a=await this.deliverPrompt(l,i,n,r);return this.bus({kind:"message.created",messageId:l.id,sessionId:l.sessionId,mode:l.mode,at:new Date().toISOString()}),a}dismiss(e,t){const s=this.memory.get(t);if(s){if(s.status==="running")return{ok:!1,status:409,error:"Message is still in flight"};s.status==="delivering"&&this.gate.headOf(s.sessionId)===t&&this.flushing.delete(s.sessionId),this.memory.delete(t),this.gate.clearStall(s.sessionId,t),this.gate.release(s.sessionId,t),this.queuedSessions.has(s.sessionId)&&this.scheduleFlushContinuation(s.sessionId)}else if(!this.dropQueued(e,t))return{ok:!1,status:404,error:"Message not found"};return this.bus({kind:"message.dismissed",messageId:t,sessionId:e,at:new Date().toISOString()}),{ok:!0}}dropQueued(e,t){const s=this.deps.sessions.getBySessionId(e);if(s?.projectId==null)return!1;const i=this.deps.projects.getById(s.projectId);if(!i)return!1;const n=k(this.hooksDir,i.token);if(!n.some(r=>r.id===t&&r.sessionId===e))return!1;for(p(this.hooksDir,i.token,new Set([t])),this.dismissedIds.add(t);this.dismissedIds.size>Le;){const r=this.dismissedIds.values().next().value;if(r===void 0)break;this.dismissedIds.delete(r)}return n.some(r=>r.sessionId===e&&r.id!==t)||this.queuedSessions.delete(e),!0}async flushSession(e){if(this.stalled.has(e)||this.flushing.has(e)||!this.queuedSessions.has(e))return;const t=Symbol(e);this.flushing.set(e,t);try{await this.flushSessionInner(e)}catch{}finally{this.flushing.get(e)===t&&this.flushing.delete(e)}}async flushSessionInner(e){if(!this.ackGateOpen(e))return;const t=this.deps.sessions.getBySessionId(e);if(t?.projectId==null)return;const s=this.deps.projects.getById(t.projectId);if(!s)return;const i=k(this.hooksDir,s.token).filter(a=>a.sessionId===e);if(i.length===0){this.queuedSessions.delete(e);return}if(this.sessionJudgedDead(e)){this.failQueuedOffline(e,i.filter(a=>!this.dismissedIds.has(a.id)));return}const n=this.surfaceOfSession(e),r=n?.prompt??null,l=this.modalBlocked(e);for(const a of i){if(this.stalled.has(e))return;if(this.dismissedIds.has(a.id))continue;const o={id:a.id,sessionId:e,text:a.text,...a.attachments&&a.attachments.length>0?{attachments:a.attachments}:{},mode:"prompt",oneWay:a.oneWay===!0,...a.forceTerminal===!0?{forceTerminal:!0}:{},via:"socket",channel:"prompt",status:"pending",at:a.at};if(!l&&this.hasImagesEntry(o)&&this.terminalChannel()){const u=await this.terminalPaneFor(t,s);if(u){p(this.hooksDir,s.token,new Set([a.id])),this.memory.set(o.id,{...o,via:"terminal",status:"delivering"}),this.awaitingAck.set(e,o.id),await this.runTerminalPrompt(o.id,u.id,this.wireTextOfEntry(o)),this.settleQueueIndex(e,s.token);return}}if(n&&await this.tryQueuedDeliver(o,n)){p(this.hooksDir,s.token,new Set([a.id])),this.settleQueueIndex(e,s.token);return}if(n&&await this.tryInboxDeliver(o,n)){p(this.hooksDir,s.token,new Set([a.id])),this.settleQueueIndex(e,s.token);return}const h=r?.resolve(e)??null,d=o.forceTerminal===!0?["terminal"]:o.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const u of d){if(u==="relay"&&r&&h){if(this.memory.set(o.id,{...o,status:"delivering",via:"relay"}),!this.runRelay(o,r,h.name)){this.memory.delete(o.id);return}this.awaitingAck.set(e,o.id),p(this.hooksDir,s.token,new Set([a.id]));break}if(u==="terminal"&&!l&&this.terminalChannel()){const c=await this.terminalPaneFor(t,s);if(c){p(this.hooksDir,s.token,new Set([a.id])),this.memory.set(o.id,{...o,via:"terminal",status:"delivering"}),this.awaitingAck.set(e,o.id),await this.runTerminalPrompt(o.id,c.id,this.wireTextOfEntry(o));break}}}if(this.awaitingAck.get(e)===o.id){this.settleQueueIndex(e,s.token);return}return}this.settleQueueIndex(e,s.token)}settleQueueIndex(e,t){k(this.hooksDir,t).every(s=>s.sessionId!==e)&&this.queuedSessions.delete(e)}async flushAll(){for(const e of[...this.queuedSessions])await this.flushSession(e)}forgetSession(e){let t=0;for(const[s,i]of this.memory)i.sessionId===e&&(this.memory.delete(s),t++);return t+=Oe(this.hooksDir,e),this.queuedSessions.delete(e),this.gate.clearStall(e),this.gate.release(e),this.inboxSockets.forget(e),this.paneEnvs.delete(e),this.paneEnvConflicts.delete(e),this.revokedPaneEnvs.add(e),this.persistPaneEnvs(),t}forgetProject(e){const t=this.pendingOrigins.length;this.pendingOrigins=this.pendingOrigins.filter(i=>i.projectId!==e),this.pendingOrigins.length!==t&&this.persistPendingOrigins();let s=0;for(const[i,n]of this.memory)this.deps.sessions.getBySessionId(n.sessionId)?.projectId===e&&(this.memory.delete(i),this.inboxSockets.forget(n.sessionId),this.paneEnvs.delete(n.sessionId),this.gate.clearStall(n.sessionId),this.gate.release(n.sessionId),s++);for(const i of[...this.queuedSessions])this.deps.sessions.getBySessionId(i)?.projectId===e&&(s+=this.forgetSession(i));return this.persistPaneEnvs(),s}surfaceOfSession(e){const t=this.deps.hookEvents.listBySession(e,1),s=t[t.length-1]?.source;return S(s)?this.deps.surfaceFor?.(s)??F(s):null}commandModeOf(e,t){const s=this.deps.hookEvents.listBySession(e,1),i=s[s.length-1]?.source;if(!S(i))return null;const n=this.deps.commandsFor?.(i)??L(i);return n?q(t,n.inputModes):null}resolveChannel(e,t,s){return fe(this.deps.sessions,()=>this.terminalChannel()!=null,e,t,s)}async terminalPaneFor(e,t){return this.paneEnvConflicts.has(e.sessionId)?null:ae(this.terminalChannel(),this.paneEnvs,e,t)}terminalChannel(){const e=this.deps.terminal;return e&&e.available()?e:null}modalBlocked(e){return this.deps.blockedByQuestion?.(e)===!0}async deliverPrompt(e,t,s,i){if(this.sessionJudgedDead(e.sessionId))return{ok:!0,messageId:e.id,status:"failed",message:this.failOffline(e)};const n=this.modalBlocked(e.sessionId);if(!n&&this.hasImagesEntry(e)&&this.terminalChannel()){const u=await this.terminalPaneFor(t,s);if(u)return this.memory.set(e.id,{...e,status:"delivering",via:"terminal"}),this.awaitingAck.set(e.sessionId,e.id),this.runTerminalPrompt(e.id,u.id,this.wireTextOfEntry(e)),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)}}const r=await this.tryQueuedDeliver(e,i);if(r)return{ok:!0,messageId:e.id,status:"delivered",message:r};const l=await this.tryInboxDeliver(e,i);if(l)return{ok:!0,messageId:e.id,status:"delivered",message:l};const a=i.prompt,o=a?.resolve(e.sessionId)??null,h=e.forceTerminal===!0?["terminal"]:e.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const u of h){if(u==="relay"&&a&&o){if(this.memory.set(e.id,{...e,status:"delivering",via:"relay"}),this.runRelay(e,a,o.name))return this.awaitingAck.set(e.sessionId,e.id),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)};this.memory.delete(e.id)}if(u==="terminal"&&!n&&this.terminalChannel()){const c=await this.terminalPaneFor(t,s);if(c)return this.memory.set(e.id,{...e,status:"delivering",via:"terminal"}),this.awaitingAck.set(e.sessionId,e.id),this.runTerminalPrompt(e.id,c.id,this.wireTextOfEntry(e)),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)}}}if(this.sessionJudgedDead(e.sessionId))return{ok:!0,messageId:e.id,status:"failed",message:this.failOffline(e)};this.queueEntry(e,s.token);const d={...e,status:"pending"};return{ok:!0,messageId:e.id,status:"pending",message:d}}async tryQueuedDeliver(e,t){const s=t.queuedPrompt;if(!s||this.hasImagesEntry(e))return null;const{bin:i,args:n}=s.command(e.sessionId,this.wireTextOfEntry(e));this.memory.set(e.id,{...e,status:"delivering",via:"queue"});try{await he(i,n,s.timeoutMs??3e4,this.deps.spawnImpl)}catch{return this.memory.delete(e.id),null}return P(this.channelDeps(),{...e,via:"queue"},"queue")}async tryInboxDeliver(e,t){const s=t.promptInbox,i=this.inboxSockets.get(e.sessionId);if(!s||!i||e.forceTerminal===!0)return null;this.memory.set(e.id,{...e,status:"delivering",via:"socket"});try{await V(s,i,this.wireTextOfEntry(e))}catch{return this.memory.delete(e.id),null}return P(this.channelDeps(),e,"socket")}queueEntry(e,t){pe(this.hooksDir,t,e),this.queuedSessions.add(e.sessionId)}sessionJudgedDead(e,t=this.deps.livenessOf?.()??A()){return Se(this.deps.sessions,e,t)}failOffline(e){return ge(this.memory,t=>this.bus(t),e)}failQueuedOffline(e,t){ke(this.deps,this.hooksDir,this.memory,s=>this.bus(s),e,t),this.queuedSessions.delete(e)}relayDeps(){return{headless:this.headless,getMemory:e=>this.memory.get(e)??null,setMemory:e=>this.memory.set(e.id,e),awaitAck:(e,t)=>this.gate.await(e,t),stall:(e,t,s)=>this.gate.stall(e,t,s),commandModeOf:(e,t)=>this.commandModeOf(t,e),armAckWatch:e=>this.gate.armAckWatch(e),armCommandAck:(e,t)=>this.gate.armCommandAck(e,s=>{t(s),this.queuedSessions.has(s)&&this.scheduleFlushContinuation(s)}),onReleased:e=>{this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e)},emit:e=>this.bus(e),...this.deps.bin?{bin:this.deps.bin}:{},...this.deps.relayTimeoutMs===void 0?{}:{relayTimeoutMs:this.deps.relayTimeoutMs}}}runRelay(e,t,s){return ye(this.relayDeps(),e,t,s,this.wireTextOfEntry(e))}async runTerminalPrompt(e,t,s){await this.runTerminal(e,i=>i.sendText(t,s))}async runTerminalSubmit(e,t){await this.runTerminal(e,s=>s.sendKeys(t,"\r"))}async runTerminal(e,t){const s=this.memory.get(e);if(!s)return;const i=this.terminalChannel();if(i)try{await t(i);const n=this.memory.get(e);if(!n)return;const r={...n,status:"delivered",settledAt:new Date().toISOString()};this.memory.set(e,r),this.commandModeOf(n.sessionId,n.text)?this.armCommandAck(e):this.armAckWatch(r),this.bus({kind:"message.delivered",messageId:e,sessionId:s.sessionId,at:new Date().toISOString()})}catch(n){const r=this.memory.get(e);if(!r)return;if(r.acked===!0){const a=new Date().toISOString();this.memory.set(e,{...r,status:"delivered",settledAt:a}),this.bus({kind:"message.delivered",messageId:e,sessionId:r.sessionId,at:a});return}const l={...r,status:"failed",error:n instanceof Error?n.message:String(n),settledAt:new Date().toISOString()};this.memory.set(e,l),this.stall(s.sessionId,l,"failed"),this.bus({kind:"message.finished",messageId:e,sessionId:s.sessionId,status:"failed",at:new Date().toISOString()})}}deliverInject(e,t,s,i){return me({hooksDir:this.hooksDir,memory:this.memory,bus:this.busSafe()},e,t,s,i,this.wireTextOfEntry(e))}deliverHeadless(e,t,s,i){return ce({memory:this.memory,headless:this.headless,bus:this.busSafe(),...this.deps.bin?{bin:this.deps.bin}:{},...this.deps.timeoutMs===void 0?{}:{timeoutMs:this.deps.timeoutMs}},e,t,s,i,this.wireTextOfEntry(e))}bus(e){try{this.deps.bus?.emit(e)}catch{}}}
1
+ import{randomUUID as K}from"node:crypto";import{chmodSync as S,mkdirSync as v,readFileSync as w,renameSync as O,writeFileSync as E}from"node:fs";import{dirname as b,join as g}from"node:path";import{isHarness as k}from"@weavecode/core";import{commandModeAt as Q,turnOutputOf as R}from"@weavecode/harness";import{isImageMediaType as D}from"../repositories/uploads.js";import{commandsSurfaceFor as N,messagingSurfaceFor as W}from"../surfaces.js";import{pidAliveOf as J}from"../daemon/claude-liveness.js";import{combinedLivenessOf as T}from"../daemon/liveness.js";import{HeadlessRunner as _}from"./headless-runner.js";import{hooksRoot as G}from"./mirror.js";import{INBOX_TARGETS_FILE as L,InboxSocketRegistry as H,deliverViaInboxSocket as $}from"./inbox-socket.js";import{AckGate as V}from"./delivery/ack-gate.js";import{ANSWER_ATOM as A,ANSWER_ATOM_GAP_MS as Y,ANSWER_TEXT as U,CTRL_B as X,CTRL_C_KITTY as z,ENTER as Z,ESC as ee,ESC_KITTY as se,MODEL_NAME as te,SHIFT_TAB as ie,effortCommand as ne,effortLevelsOf as re,locateTerminalPane as ae,modelCommand as oe,sessionKeysTarget as de}from"./delivery/keys.js";import{hasImages as ue,settleDelivered as P,spawnOnce as he,wireTextOf as le}from"./delivery/channels.js";import{deliverHeadless as ce,deliverInject as me,resolveChannel as fe}from"./delivery/send.js";import{appendQueueEntry as pe,failOffline as ge,failQueuedOffline as ye,sessionJudgedDead as ke}from"./delivery/queue.js";import{runRelay as Ie}from"./delivery/relay.js";import{listInflightCounts as Se,listVisible as ve,sessionTerminals as we}from"./delivery/views.js";import{readPromptbox as y,removePromptboxBySession as Oe,removePromptboxEntries as p,scanQueuedSessionIds as Ee}from"./promptbox.js";const be=1e3,De=6e4,Te=3e4,Ae=9e4,Pe=5e3,xe=8,Ce=12e4,je=1200,Me=/interrupted/i,Fe=/· done/,Be=12,qe=[se,ee,X],Ke=600,Qe=250,Re=5,Ne=200,x="pane-envs.json",C="pending-origins.json",j=15*6e4;function We(f){try{const e=JSON.parse(w(f,"utf8"));if(!Array.isArray(e))return[];const s=[];for(const t of e){if(typeof t!="object"||t===null)continue;const i=t;if(!k(i.harness)||typeof i.projectId!="number"||typeof i.env!="object"||i.env===null||typeof i.at!="string")continue;const n={};for(const[r,d]of Object.entries(i.env))typeof d=="string"&&d&&(n[r]=d);Object.keys(n).length!==0&&s.push({harness:i.harness,projectId:i.projectId,env:n,at:i.at})}return s.filter(t=>Date.now()-Date.parse(t.at)<j)}catch{return[]}}function Je(f){try{const e=JSON.parse(w(f,"utf8"));if(typeof e!="object"||e===null)return{};const s={};for(const[t,i]of Object.entries(e)){if(typeof i!="object"||i===null)continue;const n={};for(const[r,d]of Object.entries(i))typeof d=="string"&&d&&(n[r]=d);Object.keys(n).length>0&&(s[t]=n)}return s}catch{return{}}}const m=f=>new Promise(e=>setTimeout(e,f)),M=300,F=1500,_e=1e3,Ge=12e4;export class MessageDeliverer{deps;headless;hooksDir;sideAnswerStore;memory=new Map;queuedSessions;inboxSockets;paneEnvs;revokedPaneEnvs=new Set;paneEnvConflicts=new Map;persistedPaneEnvs;pendingOrigins;persistedPendingOrigins;flushScheduled=new Set;flushing=new Map;stalled=new Map;awaitingAck=new Map;dismissedIds=new Set;gate;constructor(e){this.deps=e,this.headless=e.headless??new _({spawnImpl:e.spawnImpl,killImpl:e.killImpl}),this.hooksDir=e.hooksDir??G(),this.sideAnswerStore=e.sideAnswerStore,this.inboxSockets=new H(g(this.hooksDir,L));const s=Je(g(this.hooksDir,x));this.paneEnvs=new Map(Object.entries(s)),this.persistedPaneEnvs=JSON.stringify(s),this.pendingOrigins=We(g(this.hooksDir,C)),this.persistedPendingOrigins=JSON.stringify(this.pendingOrigins),this.queuedSessions=Ee(this.hooksDir),this.gate=new V({awaitingAck:this.awaitingAck,stalled:this.stalled,memory:this.memory},{ackStallMs:e.ackStallMs??Te,ackStallReleaseMs:e.ackStallReleaseMs??Ae,commandAckGraceMs:e.commandAckGraceMs??Pe},{emit:t=>this.bus(t)},(t,i)=>this.commandModeOf(t,i)!==null),this.gate.onAutoRelease=t=>this.scheduleFlushContinuation(t)}async send(e){const s=this.deps.sessions.getBySessionId(e.toSessionId);if(!s)return{ok:!1,status:404,error:"Session not found"};const t=this.surfaceOfSession(e.toSessionId);if(!t)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};const i=e.attachments?[...e.attachments]:[];if(i.length>0){if(!t.attach)return{ok:!1,status:409,error:"This harness cannot receive attachments"};if(i.some(l=>D(l.mediaType))&&!t.attach.kinds.includes("image"))return{ok:!1,status:409,error:"This harness cannot receive images"};if(i.some(l=>!D(l.mediaType))&&!t.attach.kinds.includes("file"))return{ok:!1,status:409,error:"This harness cannot receive file attachments"}}const n=this.resolveChannel(e.mode,e.toSessionId,t);if(typeof n!="string")return{ok:!1,status:409,error:n.reason};if(s.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const r=this.deps.projects.getById(s.projectId);if(!r)return{ok:!1,status:404,error:"Project not found"};if(n==="headless"&&this.headless.isBusy(e.toSessionId))return{ok:!1,status:409,error:"Session already has a running run"};const d={fromLabel:e.fromKind==="session"?`session ${e.fromSessionId??"unknown"}`:e.fromKind,at:new Date().toISOString(),mode:n},a={id:K(),sessionId:e.toSessionId,text:e.text,...i.length>0?{attachments:i}:{},mode:e.mode,oneWay:e.oneWay===!0,...e.forceTerminal===!0?{forceTerminal:!0}:{},channel:n,status:"pending",at:d.at},o=n==="prompt"&&this.blockedAhead(a.sessionId);o&&(this.queueEntry(a,r.token),this.scheduleFlushContinuation(a.sessionId));const h=o?{ok:!0,messageId:a.id,status:"pending",message:a}:n==="inject"?this.deliverInject(a,r.token,d,t):n==="headless"?this.deliverHeadless(a,r.path,d,t):await this.deliverPrompt(a,s,r,t);return this.bus({kind:"message.created",messageId:a.id,sessionId:a.sessionId,mode:a.mode,at:a.at}),h}getMessage(e){return this.memory.get(e)??null}listVisible(e){return ve(this.deps,this.hooksDir,this.memory,e,this.deps.visibleTtlMs??De,s=>this.commandModeOf(e,s)!==null)}hasQueued(e){return this.queuedSessions.has(e)}listInflightCounts(){return Se(this.hooksDir,this.memory)}hasTerminalChannel(){return this.terminalChannel()!=null}scheduleFlushContinuation(e){this.flushScheduled.has(e)||(this.flushScheduled.add(e),setTimeout(()=>{this.flushScheduled.delete(e),this.flushSession(e)},this.deps.flushGapMs??be).unref?.())}ackGateOpen(e){return this.gate.open(e)}blockedAhead(e){return this.ackGateOpen(e)?this.queuedSessions.has(e)||this.flushing.has(e):!0}noteInboxSocket(e,s){this.inboxSockets.set(e,s)}async awaitInboxReady(e,s){const t=this.surfaceOfSession(e)?.promptInbox??null;if(!t)return!1;const i=()=>{const r=this.inboxSockets.get(e);return!!r&&(!t.requiresAuthOn(process.platform)||!!r.token)},n=Date.now()+s;for(;!i();){if(Date.now()>=n)return!1;await new Promise(r=>setTimeout(r,Qe))}return!0}lastTurnOutput(e){const s=this.deps.hookEvents.listBySession(e,Re);for(let t=s.length-1;t>=0;t--){const i=s[t];let n=null;try{n=JSON.parse(i.payload)}catch{continue}const r=R(n?.raw);if(r)return{output:r,at:i.createdAt}}return null}notePaneEnv(e,s){const t=JSON.stringify(s);for(const[i,n]of this.paneEnvs){if(i===e||JSON.stringify(n)!==t)continue;const r=this.deps.sessions.getBySessionId(i)?.lastSeenAt;if((r?Date.now()-Date.parse(r):Number.POSITIVE_INFINITY)<Ce){this.paneEnvs.delete(e),this.paneEnvConflicts.set(e,i),this.persistPaneEnvs();return}this.paneEnvs.delete(i),this.revokedPaneEnvs.add(i)}this.paneEnvConflicts.delete(e),this.paneEnvs.set(e,s),this.revokedPaneEnvs.delete(e),this.persistPaneEnvs()}paneEnvConflictOf(e){return this.paneEnvConflicts.get(e)??null}notePendingOrigin(e){this.pendingOrigins=this.pendingOrigins.filter(s=>Date.now()-Date.parse(s.at)<j),this.pendingOrigins.push({...e,at:new Date().toISOString()}),this.persistPendingOrigins()}claimPendingOrigin(e,s,t){const i=this.pendingOrigins.findIndex(r=>r.harness===s&&r.projectId===t);if(i<0)return;const[n]=this.pendingOrigins.splice(i,1);this.notePaneEnv(e,n.env),this.persistPendingOrigins()}persistPaneEnvs(){const e=JSON.stringify(Object.fromEntries(this.paneEnvs));if(e===this.persistedPaneEnvs)return;const s=g(this.hooksDir,x);try{v(b(s),{recursive:!0});const t=`${s}.tmp`;E(t,`${e}
2
+ `,{encoding:"utf8",mode:384}),O(t,s),S(s,384),this.persistedPaneEnvs=e}catch{}}persistPendingOrigins(){const e=JSON.stringify(this.pendingOrigins);if(e===this.persistedPendingOrigins)return;const s=g(this.hooksDir,C);try{v(b(s),{recursive:!0});const t=`${s}.tmp`;E(t,`${e}
3
+ `,{encoding:"utf8",mode:384}),O(t,s),S(s,384),this.persistedPendingOrigins=e}catch{}}sessionExited(e){this.inboxSockets.forget(e),this.paneEnvs.delete(e),this.revokedPaneEnvs.add(e),this.persistPaneEnvs(),this.awaitingAck.delete(e);const s=this.deps.sessions.getBySessionId(e),t=s?.projectId==null?null:this.deps.projects.getById(s.projectId);if(!t)return;const i=y(this.hooksDir,t.token).filter(n=>n.sessionId===e);this.failQueuedOffline(e,i.filter(n=>!this.dismissedIds.has(n.id)))}async sessionTerminals(){return we(this.deps,this.terminalChannel(),this.paneEnvs,()=>this.persistPaneEnvs(),this.revokedPaneEnvs)}diagnose(e){const s=this.surfaceOfSession(e),t=this.deps.sessions.livenessRowOf(e),i=t?J(t,this.deps.livenessOf?.()??T()):null,n=s?.promptInbox??null,r=this.inboxSockets.get(e),d=n?r?n.requiresAuthOn(process.platform)&&!r.token?{status:"unavailable",reason:"auth-token-required"}:{status:"ready",reason:null}:{status:"unavailable",reason:"no-credentials"}:{status:"unavailable",reason:"no-mechanism"},a=s?.queuedPrompt?{status:"ready",reason:null}:{status:"unavailable",reason:"no-mechanism"},o=s?.prompt?.resolve(e)??null,h=s?.prompt?o?{status:"ready",reason:null}:{status:"unavailable",reason:"no-peer-name"}:{status:"unavailable",reason:"no-mechanism"},l=this.paneEnvs.has(e),u=this.terminalChannel()?l?{status:"ready",reason:null}:this.paneEnvConflicts.has(e)?{status:"unavailable",reason:"pane-env-conflict"}:{status:"unknown",reason:"no-pane-env"}:{status:"unavailable",reason:"no-backend"},c=this.deps.sessions.getBySessionId(e),I=c?.projectId==null?null:this.deps.projects.getById(c.projectId),B=I?y(this.hooksDir,I.token).filter(q=>q.sessionId===e).length:0;return{alive:i,inbox:d,queue:a,relay:h,terminal:u,paneEnvReported:l,pending:B,stalled:this.gate.isStalled(e)}}onSessionPrompt(e,s,t){const i=this.gate.acknowledge(e,s,t,{ackMinText:xe});return this.gate.hitLast&&this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e),i}noteActivity(e){this.gate.noteActivity(e)&&this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e)}armAckWatch(e){this.gate.armAckWatch(e)}armCommandAck(e){this.gate.armCommandAck(e,s=>{this.queuedSessions.has(s)&&this.scheduleFlushContinuation(s)})}stall(e,s,t){this.gate.stall(e,s,t)}async answer(e,s){if(s.length===0||!A.test(s[0])||s.some(i=>!A.test(i)&&!U.test(i)))return{ok:!1,status:400,error:"Invalid answer keys"};const t=await this.sessionKeysTarget(e);if(!t.ok)return t;try{for(const[i,n]of s.entries())i>0&&await m(Y),await t.terminal.sendKeys(t.paneId,n);return{ok:!0}}catch(i){return{ok:!1,status:409,error:i instanceof Error?i.message:String(i)}}}async interrupt(e){const s=await this.sessionKeysTarget(e,"\u51FB\u952E");if(!s.ok)return s;const t=async()=>{try{return await s.terminal.getPaneText?.(s.paneId)??null}catch{return null}},i=d=>{const a=d.split(`
4
+ `).slice(-Be).join(`
5
+ `);return Me.test(a)||Fe.test(a)},n=this.deps.interruptVerifyMs??je;let r=!1;for(const[d,a]of qe.entries()){try{await s.terminal.sendKeys(s.paneId,a)}catch(h){if(d===0)return{ok:!1,status:409,error:h instanceof Error?h.message:String(h)};break}await m(d===0?n:Ke);const o=await t();if(o===null)return{ok:!0,paneId:s.paneId,sent:!0,confirmed:!0};if(i(o)){r=!0;break}}return{ok:!0,paneId:s.paneId,sent:!0,confirmed:r}}async closePane(e){const s=await this.sessionKeysTarget(e,"\u5173\u95ED");if(!s.ok)return s;try{return await s.terminal.closePane(s.paneId),{ok:!0}}catch(t){return{ok:!1,status:409,error:t instanceof Error?t.message:String(t)}}}async cycleMode(e){const s=await this.sessionKeysTarget(e);if(!s.ok)return s;try{return await s.terminal.sendKeys(s.paneId,ie),{ok:!0}}catch(t){return{ok:!1,status:409,error:t instanceof Error?t.message:String(t)}}}async pressEnter(e){const s=await this.sessionKeysTarget(e);if(!s.ok)return s;try{return await s.terminal.sendKeys(s.paneId,Z),{ok:!0}}catch(t){return{ok:!1,status:409,error:t instanceof Error?t.message:String(t)}}}async pressCtrlC(e){const s=await this.sessionKeysTarget(e);if(!s.ok)return s;try{return await s.terminal.sendKeys(s.paneId,z),{ok:!0}}catch(t){return{ok:!1,status:409,error:t instanceof Error?t.message:String(t)}}}async setModel(e,s){const t=s.trim();if(!te.test(t))return{ok:!1,status:400,error:"\u6A21\u578B\u540D\u5F62\u6001\u4E0D\u5408\u6CD5\uFF08\u4E0D\u542B\u7A7A\u767D\u4E0E\u63A7\u5236\u5B57\u7B26\uFF0C\u2264128 \u5B57\u7B26\uFF09"};const i=await this.sessionKeysTarget(e);if(!i.ok)return i;try{return await i.terminal.sendText(i.paneId,oe(t)),{ok:!0}}catch(n){return{ok:!1,status:409,error:n instanceof Error?n.message:String(n)}}}async setEffort(e,s){const t=s.trim(),i=await this.sessionKeysTarget(e);if(!i.ok)return i;const n=re(this.deps.hookEvents,e);if(!n.includes(t))return{ok:!1,status:400,error:`\u601D\u8003\u5F3A\u5EA6\u6863\u4F4D\u4E0D\u5408\u6CD5\uFF08\u53EF\u9009\uFF1A${n.join(" / ")||"\u65E0"}\uFF09`};try{return await i.terminal.sendText(i.paneId,ne(t)),{ok:!0}}catch(r){return{ok:!1,status:409,error:r instanceof Error?r.message:String(r)}}}async askSideQuestion(e,s){if(!this.deps.sessions.getBySessionId(e))return{ok:!1,status:404,error:"Session not found"};const t=this.surfaceOfSession(e),i=t?.sideQuestion??null;if(!i)return{ok:!1,status:409,error:t?.unsupportedReasons?.sideQuestion??"\u8FD9\u4E2A harness \u6CA1\u6709\u65C1\u8DEF\u4F1A\u8BDD\u901A\u9053"};const n=i.validate(s);if(!n.ok)return{ok:!1,status:400,error:n.error};if(i.prerequisite&&this.deps.sessions.hasRunOf(e)!==!0)return{ok:!1,status:409,error:i.prerequisite};const r=await this.sessionKeysTarget(e,"\u53D1\u8D77\u65C1\u8DEF\u4F1A\u8BDD");if(!r.ok)return r;try{return i.screen?(this.pollScreenAnswer(e,r.terminal,r.paneId,i,n.text),{ok:!0,question:n.text}):(await r.terminal.sendText(r.paneId,i.command(n.text)),{ok:!0,question:n.text})}catch(d){return{ok:!1,status:409,error:d instanceof Error?d.message:String(d)}}}pollScreenAnswer(e,s,t,i,n){const r=i.screen,d=async()=>await s.getPaneText?.(t).catch(()=>null)??null;(async()=>{const a=await d()??"";await s.sendKeys(t,r.openKeys).catch(()=>{}),await m(M),await s.sendKeys(t,r.submitKey).catch(()=>{}),await m(F);let o=await d();if((!o||!r.panelOpen(o))&&(await s.sendKeys(t,r.submitKey).catch(()=>{}),await m(F),o=await d(),!o||!r.panelOpen(o)))return;await s.sendText(t,n).catch(()=>{}),await m(M),await s.sendKeys(t,r.submitKey).catch(()=>{});const h=Date.now()+Ge;let l=null;for(;Date.now()<h;){if(await m(_e),o=await d(),o==null)return;const c=r.extractAnswer(n,a,o);if(c.done){l=c.answer;break}}l!=null&&(this.sideAnswerStore?.answerById(e,n,l),this.bus({kind:"sideQuestion.answered",sessionId:e,at:new Date().toISOString()}));const u=await d();u&&r.panelOpen(u)&&await s.sendKeys(t,r.closeKey).catch(()=>{})})()}async sessionKeysTarget(e,s="\u51FB\u952E"){return de(this.keysDeps(),e,s)}busSafe(){return{emit:e=>this.bus(e)}}channelDeps(){return{setMemory:e=>this.memory.set(e.id,e),deleteMemory:e=>this.memory.delete(e),awaitAck:(e,s)=>this.gate.await(e,s),commandModeOf:(e,s)=>this.commandModeOf(s,e),armAckWatch:e=>this.gate.armAckWatch(e),armCommandAck:e=>this.gate.armCommandAck(e,s=>{this.queuedSessions.has(s)&&this.scheduleFlushContinuation(s)}),emit:e=>this.bus(e),...this.deps.spawnImpl?{spawnImpl:this.deps.spawnImpl}:{}}}wireTextOfEntry(e){return le(e,s=>this.surfaceOfSession(s))}hasImagesEntry(e){return ue(e)}keysDeps(){return{sessions:this.deps.sessions,projects:this.deps.projects,hookEvents:this.deps.hookEvents,terminalChannel:()=>this.terminalChannel(),terminalPaneFor:(e,s)=>this.terminalPaneFor(e,s),paneEnvConflictOf:e=>this.paneEnvConflictOf(e)}}async resend(e){const s=this.memory.get(e);if(!s)return{ok:!1,status:404,error:"Message not found"};if(!(s.channel==="prompt"&&(s.status==="failed"||s.status==="delivered"&&!s.acked)))return{ok:!1,status:409,error:"Message is not resendable"};const t=this.deps.sessions.getBySessionId(s.sessionId);if(!t)return{ok:!1,status:404,error:"Session not found"};if(t.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const i=this.deps.projects.getById(t.projectId);if(!i)return{ok:!1,status:404,error:"Project not found"};const n=this.surfaceOfSession(s.sessionId);if(!n)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};if(this.gate.clearStall(s.sessionId,e),s.status==="delivered"&&s.via==="terminal"&&this.terminalChannel()){const a=await this.terminalPaneFor(t,i);if(a)return this.memory.set(e,{...s,status:"delivering",stalled:!1,settledAt:null}),this.runTerminalSubmit(e,a.id),this.bus({kind:"message.created",messageId:e,sessionId:s.sessionId,mode:s.mode,at:new Date().toISOString()}),{ok:!0,messageId:e,status:"delivering",message:this.memory.get(e)}}this.memory.delete(e);const r={...s,status:"pending",stalled:!1,acked:!1,settledAt:null,reason:void 0,error:null},d=await this.deliverPrompt(r,t,i,n);return this.bus({kind:"message.created",messageId:r.id,sessionId:r.sessionId,mode:r.mode,at:new Date().toISOString()}),d}dismiss(e,s){const t=this.memory.get(s);if(t){if(t.status==="running")return{ok:!1,status:409,error:"Message is still in flight"};t.status==="delivering"&&this.gate.headOf(t.sessionId)===s&&this.flushing.delete(t.sessionId),this.memory.delete(s),this.gate.clearStall(t.sessionId,s),this.gate.release(t.sessionId,s),this.queuedSessions.has(t.sessionId)&&this.scheduleFlushContinuation(t.sessionId)}else if(!this.dropQueued(e,s))return{ok:!1,status:404,error:"Message not found"};return this.bus({kind:"message.dismissed",messageId:s,sessionId:e,at:new Date().toISOString()}),{ok:!0}}dropQueued(e,s){const t=this.deps.sessions.getBySessionId(e);if(t?.projectId==null)return!1;const i=this.deps.projects.getById(t.projectId);if(!i)return!1;const n=y(this.hooksDir,i.token);if(!n.some(r=>r.id===s&&r.sessionId===e))return!1;for(p(this.hooksDir,i.token,new Set([s])),this.dismissedIds.add(s);this.dismissedIds.size>Ne;){const r=this.dismissedIds.values().next().value;if(r===void 0)break;this.dismissedIds.delete(r)}return n.some(r=>r.sessionId===e&&r.id!==s)||this.queuedSessions.delete(e),!0}async flushSession(e){if(this.stalled.has(e)||this.flushing.has(e)||!this.queuedSessions.has(e))return;const s=Symbol(e);this.flushing.set(e,s);try{await this.flushSessionInner(e)}catch{}finally{this.flushing.get(e)===s&&this.flushing.delete(e)}}async flushSessionInner(e){if(!this.ackGateOpen(e))return;const s=this.deps.sessions.getBySessionId(e);if(s?.projectId==null)return;const t=this.deps.projects.getById(s.projectId);if(!t)return;const i=y(this.hooksDir,t.token).filter(a=>a.sessionId===e);if(i.length===0){this.queuedSessions.delete(e);return}if(this.sessionJudgedDead(e)){this.failQueuedOffline(e,i.filter(a=>!this.dismissedIds.has(a.id)));return}const n=this.surfaceOfSession(e),r=n?.prompt??null,d=this.modalBlocked(e);for(const a of i){if(this.stalled.has(e))return;if(this.dismissedIds.has(a.id))continue;const o={id:a.id,sessionId:e,text:a.text,...a.attachments&&a.attachments.length>0?{attachments:a.attachments}:{},mode:"prompt",oneWay:a.oneWay===!0,...a.forceTerminal===!0?{forceTerminal:!0}:{},via:"socket",channel:"prompt",status:"pending",at:a.at};if(!d&&this.hasImagesEntry(o)&&this.terminalChannel()){const u=await this.terminalPaneFor(s,t);if(u){p(this.hooksDir,t.token,new Set([a.id])),this.memory.set(o.id,{...o,via:"terminal",status:"delivering"}),this.awaitingAck.set(e,o.id),await this.runTerminalPrompt(o.id,u.id,this.wireTextOfEntry(o)),this.settleQueueIndex(e,t.token);return}}if(n&&await this.tryQueuedDeliver(o,n)){p(this.hooksDir,t.token,new Set([a.id])),this.settleQueueIndex(e,t.token);return}if(n&&await this.tryInboxDeliver(o,n)){p(this.hooksDir,t.token,new Set([a.id])),this.settleQueueIndex(e,t.token);return}const h=r?.resolve(e)??null,l=o.forceTerminal===!0?["terminal"]:o.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const u of l){if(u==="relay"&&r&&h){if(this.memory.set(o.id,{...o,status:"delivering",via:"relay"}),!this.runRelay(o,r,h.name)){this.memory.delete(o.id);return}this.awaitingAck.set(e,o.id),p(this.hooksDir,t.token,new Set([a.id]));break}if(u==="terminal"&&!d&&this.terminalChannel()){const c=await this.terminalPaneFor(s,t);if(c){p(this.hooksDir,t.token,new Set([a.id])),this.memory.set(o.id,{...o,via:"terminal",status:"delivering"}),this.awaitingAck.set(e,o.id),await this.runTerminalPrompt(o.id,c.id,this.wireTextOfEntry(o));break}}}if(this.awaitingAck.get(e)===o.id){this.settleQueueIndex(e,t.token);return}return}this.settleQueueIndex(e,t.token)}settleQueueIndex(e,s){y(this.hooksDir,s).every(t=>t.sessionId!==e)&&this.queuedSessions.delete(e)}async flushAll(){for(const e of[...this.queuedSessions])await this.flushSession(e)}forgetSession(e){let s=0;for(const[t,i]of this.memory)i.sessionId===e&&(this.memory.delete(t),s++);return s+=Oe(this.hooksDir,e),this.queuedSessions.delete(e),this.gate.clearStall(e),this.gate.release(e),this.inboxSockets.forget(e),this.paneEnvs.delete(e),this.paneEnvConflicts.delete(e),this.revokedPaneEnvs.add(e),this.persistPaneEnvs(),s}forgetProject(e){const s=this.pendingOrigins.length;this.pendingOrigins=this.pendingOrigins.filter(i=>i.projectId!==e),this.pendingOrigins.length!==s&&this.persistPendingOrigins();let t=0;for(const[i,n]of this.memory)this.deps.sessions.getBySessionId(n.sessionId)?.projectId===e&&(this.memory.delete(i),this.inboxSockets.forget(n.sessionId),this.paneEnvs.delete(n.sessionId),this.gate.clearStall(n.sessionId),this.gate.release(n.sessionId),t++);for(const i of[...this.queuedSessions])this.deps.sessions.getBySessionId(i)?.projectId===e&&(t+=this.forgetSession(i));return this.persistPaneEnvs(),t}surfaceOfSession(e){const s=this.deps.hookEvents.listBySession(e,1),t=s[s.length-1]?.source;return k(t)?this.deps.surfaceFor?.(t)??W(t):null}commandModeOf(e,s){const t=this.deps.hookEvents.listBySession(e,1),i=t[t.length-1]?.source;if(!k(i))return null;const n=this.deps.commandsFor?.(i)??N(i);return n?Q(s,n.inputModes):null}resolveChannel(e,s,t){return fe(this.deps.sessions,()=>this.terminalChannel()!=null,e,s,t)}async terminalPaneFor(e,s){return this.paneEnvConflicts.has(e.sessionId)?null:ae(this.terminalChannel(),this.paneEnvs,e,s)}terminalChannel(){const e=this.deps.terminal;return e&&e.available()?e:null}modalBlocked(e){return this.deps.blockedByQuestion?.(e)===!0}async deliverPrompt(e,s,t,i){if(this.sessionJudgedDead(e.sessionId))return{ok:!0,messageId:e.id,status:"failed",message:this.failOffline(e)};const n=this.modalBlocked(e.sessionId);if(!n&&this.hasImagesEntry(e)&&this.terminalChannel()){const u=await this.terminalPaneFor(s,t);if(u)return this.memory.set(e.id,{...e,status:"delivering",via:"terminal"}),this.awaitingAck.set(e.sessionId,e.id),this.runTerminalPrompt(e.id,u.id,this.wireTextOfEntry(e)),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)}}const r=await this.tryQueuedDeliver(e,i);if(r)return{ok:!0,messageId:e.id,status:"delivered",message:r};const d=await this.tryInboxDeliver(e,i);if(d)return{ok:!0,messageId:e.id,status:"delivered",message:d};const a=i.prompt,o=a?.resolve(e.sessionId)??null,h=e.forceTerminal===!0?["terminal"]:e.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const u of h){if(u==="relay"&&a&&o){if(this.memory.set(e.id,{...e,status:"delivering",via:"relay"}),this.runRelay(e,a,o.name))return this.awaitingAck.set(e.sessionId,e.id),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)};this.memory.delete(e.id)}if(u==="terminal"&&!n&&this.terminalChannel()){const c=await this.terminalPaneFor(s,t);if(c)return this.memory.set(e.id,{...e,status:"delivering",via:"terminal"}),this.awaitingAck.set(e.sessionId,e.id),this.runTerminalPrompt(e.id,c.id,this.wireTextOfEntry(e)),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)}}}if(this.sessionJudgedDead(e.sessionId))return{ok:!0,messageId:e.id,status:"failed",message:this.failOffline(e)};this.queueEntry(e,t.token);const l={...e,status:"pending"};return{ok:!0,messageId:e.id,status:"pending",message:l}}async tryQueuedDeliver(e,s){const t=s.queuedPrompt;if(!t||this.hasImagesEntry(e))return null;const{bin:i,args:n}=t.command(e.sessionId,this.wireTextOfEntry(e));this.memory.set(e.id,{...e,status:"delivering",via:"queue"});try{await he(i,n,t.timeoutMs??3e4,this.deps.spawnImpl)}catch{return this.memory.delete(e.id),null}return P(this.channelDeps(),{...e,via:"queue"},"queue")}async tryInboxDeliver(e,s){const t=s.promptInbox,i=this.inboxSockets.get(e.sessionId);if(!t||!i||e.forceTerminal===!0)return null;this.memory.set(e.id,{...e,status:"delivering",via:"socket"});try{await $(t,i,this.wireTextOfEntry(e))}catch{return this.memory.delete(e.id),null}return P(this.channelDeps(),e,"socket")}queueEntry(e,s){pe(this.hooksDir,s,e),this.queuedSessions.add(e.sessionId)}sessionJudgedDead(e,s=this.deps.livenessOf?.()??T()){return ke(this.deps.sessions,e,s)}failOffline(e){return ge(this.memory,s=>this.bus(s),e)}failQueuedOffline(e,s){ye(this.deps,this.hooksDir,this.memory,t=>this.bus(t),e,s),this.queuedSessions.delete(e)}relayDeps(){return{headless:this.headless,getMemory:e=>this.memory.get(e)??null,setMemory:e=>this.memory.set(e.id,e),awaitAck:(e,s)=>this.gate.await(e,s),stall:(e,s,t)=>this.gate.stall(e,s,t),commandModeOf:(e,s)=>this.commandModeOf(s,e),armAckWatch:e=>this.gate.armAckWatch(e),armCommandAck:(e,s)=>this.gate.armCommandAck(e,t=>{s(t),this.queuedSessions.has(t)&&this.scheduleFlushContinuation(t)}),onReleased:e=>{this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e)},emit:e=>this.bus(e),...this.deps.bin?{bin:this.deps.bin}:{},...this.deps.relayTimeoutMs===void 0?{}:{relayTimeoutMs:this.deps.relayTimeoutMs}}}runRelay(e,s,t){return Ie(this.relayDeps(),e,s,t,this.wireTextOfEntry(e))}async runTerminalPrompt(e,s,t){await this.runTerminal(e,i=>i.sendText(s,t))}async runTerminalSubmit(e,s){await this.runTerminal(e,t=>t.sendKeys(s,"\r"))}async runTerminal(e,s){const t=this.memory.get(e);if(!t)return;const i=this.terminalChannel();if(i)try{await s(i);const n=this.memory.get(e);if(!n)return;const r={...n,status:"delivered",settledAt:new Date().toISOString()};this.memory.set(e,r),this.commandModeOf(n.sessionId,n.text)?this.armCommandAck(e):this.armAckWatch(r),this.bus({kind:"message.delivered",messageId:e,sessionId:t.sessionId,at:new Date().toISOString()})}catch(n){const r=this.memory.get(e);if(!r)return;if(r.acked===!0){const a=new Date().toISOString();this.memory.set(e,{...r,status:"delivered",settledAt:a}),this.bus({kind:"message.delivered",messageId:e,sessionId:r.sessionId,at:a});return}const d={...r,status:"failed",error:n instanceof Error?n.message:String(n),settledAt:new Date().toISOString()};this.memory.set(e,d),this.stall(t.sessionId,d,"failed"),this.bus({kind:"message.finished",messageId:e,sessionId:t.sessionId,status:"failed",at:new Date().toISOString()})}}deliverInject(e,s,t,i){return me({hooksDir:this.hooksDir,memory:this.memory,bus:this.busSafe()},e,s,t,i,this.wireTextOfEntry(e))}deliverHeadless(e,s,t,i){return ce({memory:this.memory,headless:this.headless,bus:this.busSafe(),...this.deps.bin?{bin:this.deps.bin}:{},...this.deps.timeoutMs===void 0?{}:{timeoutMs:this.deps.timeoutMs}},e,s,t,i,this.wireTextOfEntry(e))}bus(e){try{this.deps.bus?.emit(e)}catch{}}}
@@ -1,5 +1,5 @@
1
- import{createHash as l}from"node:crypto";import{mkdirSync as a,readFileSync as h,renameSync as f,writeFileSync as p}from"node:fs";import{join as n}from"node:path";import{hookSourceHarness as u}from"@weavecode/core";import{getDataDir as m,getHooksDir as y}from"../db/paths.js";import{projectLabel as j}from"./match.js";export const MIRROR_VERSION=1,OUTBOX_MAX_LINES=200;export function hookShardId(i){return l("sha256").update(i).digest("hex").slice(0,16)}export function hooksRoot(i){return y(i)}export function mirrorPathOf(i,r){return n(i,"mirror",`${r}.json`)}export function outboxPathOf(i,r){return n(i,"outbox",`${r}.jsonl`)}export function buildMirrorDoc(i,r,e){const o=r.filter(t=>!(t.source.kind!=="hook-event"||t.handler.type!=="inject"||!t.enabled||t.projectId!==null&&t.projectId!==i.id||t.filter.crossProjectOnly)).sort((t,s)=>s.priority-t.priority).map(t=>({id:t.id,name:t.name,harness:u(t.source)??"claude",events:[t.source.kind==="hook-event"?t.source.event:""],tools:t.filter.toolNames?.length?[...t.filter.toolNames]:null,globs:t.filter.pathGlobs?.length?[...t.filter.pathGlobs]:null,template:t.handler.type==="inject"?t.handler.template:""}));return{version:MIRROR_VERSION,project:j(i.path),pendingTitles:e,rules:o}}export class MirrorWriter{deps;dataDir;constructor(r){this.deps=r,this.dataDir=r.dataDir??m()}syncAll(){for(const r of this.deps.projects.list())this.syncProject(r.id)}syncProject(r){const e=this.deps.projects.getById(r);if(!e||e.archivedAt)return;const o={};for(const t of this.deps.sessions.list(r))t.pendingTitle&&(o[t.sessionId]=t.pendingTitle);this.write(e.token,buildMirrorDoc(e,this.deps.rules.list(),o))}write(r,e){try{const o=mirrorPathOf(hooksRoot(this.dataDir),hookShardId(r));a(n(hooksRoot(this.dataDir),"mirror"),{recursive:!0});const t=`${o}.tmp`;p(t,JSON.stringify(e)),f(t,o)}catch{}}attachToBus(r){r.subscribe(e=>{if(e.kind!=="session.renamed"||!e.sessionId)return;const o=this.deps.sessions.getBySessionId(e.sessionId);o?.projectId!=null&&this.syncProject(o.projectId)})}}export function appendInbox(i,r,e){const o=n(i,"inbox");a(o,{recursive:!0}),p(n(o,`${hookShardId(r)}.jsonl`),JSON.stringify({v:1,token:r,report:e})+`
2
- `,{flag:"a"})}export function appendOutbox(i,r,e){if(e.length!==0)try{const o=outboxPathOf(i,hookShardId(r));a(n(i,"outbox"),{recursive:!0});let t=[];try{t=h(o,"utf8").split(`
3
- `).filter(c=>c.trim())}catch{t=[]}for(const c of e)t.push(JSON.stringify({...c,at:new Date().toISOString()}));const s=t.slice(-OUTBOX_MAX_LINES),d=`${o}.tmp`;p(d,s.length?s.join(`
1
+ import{createHash as h}from"node:crypto";import{mkdirSync as a,readFileSync as u,renameSync as d,writeFileSync as l}from"node:fs";import{join as i}from"node:path";import{hookSourceHarness as f}from"@weavecode/core";import{getDataDir as m,getHooksDir as y}from"../db/paths.js";import{projectLabel as j}from"./match.js";export const MIRROR_VERSION=1,OUTBOX_MAX_LINES=200;export function hookShardId(s){return h("sha256").update(s).digest("hex").slice(0,16)}export function hooksRoot(s){return y(s)}export function mirrorPathOf(s,r){return i(s,"mirror",`${r}.json`)}export function outboxPathOf(s,r){return i(s,"outbox",`${r}.jsonl`)}export function buildMirrorDoc(s,r,e){const o=r.filter(t=>!(t.source.kind!=="hook-event"||t.handler.type!=="inject"||!t.enabled||t.projectId!==null&&t.projectId!==s.id||t.filter.crossProjectOnly)).sort((t,n)=>n.priority-t.priority).map(t=>({id:t.id,name:t.name,harness:f(t.source)??"claude",events:[t.source.kind==="hook-event"?t.source.event:""],tools:t.filter.toolNames?.length?[...t.filter.toolNames]:null,globs:t.filter.pathGlobs?.length?[...t.filter.pathGlobs]:null,template:t.handler.type==="inject"?t.handler.template:""}));return{version:MIRROR_VERSION,project:j(s.path),pendingTitles:e,rules:o}}export class MirrorWriter{deps;dataDir;constructor(r){this.deps=r,this.dataDir=r.dataDir??m()}syncAll(){for(const r of this.deps.projects.list())this.syncProject(r.id)}syncProject(r){const e=this.deps.projects.getById(r);if(!e||e.archivedAt)return;const o={};for(const t of this.deps.sessions.list(r))t.pendingTitle&&(o[t.sessionId]=t.pendingTitle);this.write(e.token,buildMirrorDoc(e,this.deps.rules.list(),o))}write(r,e){try{const o=mirrorPathOf(hooksRoot(this.dataDir),hookShardId(r));a(i(hooksRoot(this.dataDir),"mirror"),{recursive:!0});const t=`${o}.tmp`;l(t,JSON.stringify(e)),d(t,o)}catch{}}attachToBus(r){r.subscribe(e=>{if(e.kind!=="session.renamed"||!e.sessionId)return;const o=this.deps.sessions.getBySessionId(e.sessionId);o?.projectId!=null&&this.syncProject(o.projectId)})}}export function appendInbox(s,r,e){const o=i(s,"inbox");a(o,{recursive:!0}),l(i(o,`${hookShardId(r)}.jsonl`),JSON.stringify({v:1,token:r,report:e})+`
2
+ `,{flag:"a"})}export function appendOutbox(s,r,e){if(e.length!==0)try{const o=outboxPathOf(s,hookShardId(r));a(i(s,"outbox"),{recursive:!0});let t=[];try{t=u(o,"utf8").split(`
3
+ `).filter(c=>c.trim())}catch{t=[]}for(const c of e)t.push(JSON.stringify({...c,at:new Date().toISOString()}));const n=t.slice(-OUTBOX_MAX_LINES),p=`${o}.tmp`;l(p,n.length?n.join(`
4
4
  `)+`
5
- `:""),f(d,o)}catch{}}
5
+ `:""),d(p,o)}catch{}}
@@ -1,11 +1,11 @@
1
- import{randomUUID as S}from"node:crypto";import{graphDirections as m,pendingCountsOf as v}from"../repositories/orchestrations.js";import{messagingSurfaceFor as _}from"../surfaces.js";import{matchesMinute as b,minuteKey as E,parseCron as A}from"../daemon/cron.js";import{pidAliveOf as y}from"../daemon/claude-liveness.js";import{combinedLivenessOf as w}from"../daemon/liveness.js";import{postJson as C}from"./handlers/webhook.js";import{renderTemplate as R}from"./match.js";import{ORCH_MAX_DELIVERIES as I,ORCH_MAX_FORKS as p,validateOrchestration as k}from"./orchestration-graph.js";const N=5e3;function $(l,e){const t=l.trim();return t?`${t}
1
+ import{randomUUID as D}from"node:crypto";import{graphDirections as m,pendingCountsOf as v}from"../repositories/orchestrations.js";import{messagingSurfaceFor as S}from"../surfaces.js";import{matchesMinute as C,minuteKey as $,parseCron as F}from"../daemon/cron.js";import{pidAliveOf as y}from"../daemon/claude-liveness.js";import{combinedLivenessOf as I}from"../daemon/liveness.js";import{postJson as E}from"./handlers/webhook.js";import{renderTemplate as B}from"./match.js";import{ORCH_MAX_DELIVERIES as w,ORCH_MAX_FORKS as p,validateOrchestration as k}from"./orchestration-graph.js";const A=5e3;function j(h,e){const t=h.trim();return t?`${t}
2
2
 
3
- ${e}`:e}const O=4e3,g=32e3;function T(l){if(l.length<=g)return{text:l,truncated:!1};const e=g/2;return{text:`${l.slice(0,e)}
4
- \u2026[weave: ${l.length-g} chars truncated]\u2026
5
- ${l.slice(-e)}`,truncated:!0}}function x(l,e){return R(l,{upstream:e.upstream,summary:e.summary,session:e.session})}const M=/\{(upstream|summary|session)\}/,F=new Set(["yes","y","true","\u662F","\u5BF9\u7684","\u5BF9","\u9700\u8981","\u53D1"]),j=new Set(["no","n","false","\u5426","\u4E0D\u5BF9","\u4E0D\u9700\u8981","\u4E0D\u53D1"]);export function parseConditionAnswer(l){const t=((l??"").trim().toLowerCase().split(/\s+/)[0]??"").replace(/[.。!!??]/g,"");return F.has(t)?!0:j.has(t)?!1:null}export function createForkConditionJudge(l){const e=t=>t===null?{ok:!1,source:"fork",reason:"condition could not be evaluated (no clear yes/no answer) \u2014 delivery blocked"}:t?{ok:!0,source:"fork"}:{ok:!1,source:"fork",reason:"condition answered no \u2014 delivery skipped"};return{async judge({sessionId:t,condition:n}){if(!l.supports(t))return{ok:!1,source:"fork",reason:"condition not evaluable \u2014 harness has no context sub-agent face"};const s=await l.extract({sessionId:t,prompt:`${n}
3
+ ${e}`:e}const x=4e3,g=32e3;function P(h){if(h.length<=g)return{text:h,truncated:!1};const e=g/2;return{text:`${h.slice(0,e)}
4
+ \u2026[weave: ${h.length-g} chars truncated]\u2026
5
+ ${h.slice(-e)}`,truncated:!0}}function O(h,e){return B(h,{upstream:e.upstream,summary:e.summary,session:e.session})}const M=/\{(upstream|summary|session)\}/,R=new Set(["yes","y","true","\u662F","\u5BF9\u7684","\u5BF9","\u9700\u8981","\u53D1"]),J=new Set(["no","n","false","\u5426","\u4E0D\u5BF9","\u4E0D\u9700\u8981","\u4E0D\u53D1"]);export function parseConditionAnswer(h){const e=((h??"").trim().toLowerCase().split(/\s+/)[0]??"").replace(/[.。!!??]/g,"");return R.has(e)?!0:J.has(e)?!1:null}export function createForkConditionJudge(h){const e=t=>t===null?{ok:!1,source:"fork",reason:"condition could not be evaluated (no clear yes/no answer) \u2014 delivery blocked"}:t?{ok:!0,source:"fork"}:{ok:!1,source:"fork",reason:"condition answered no \u2014 delivery skipped"};return{async judge({sessionId:t,condition:n}){if(!h.supports(t))return{ok:!1,source:"fork",reason:"condition not evaluable \u2014 harness has no context sub-agent face"};const s=await h.extract({sessionId:t,prompt:`${n}
6
6
 
7
- Based on the conversation context above, answer with exactly one word: yes or no.`});return e(parseConditionAnswer(s))},async judgeBatch({sessionId:t,items:n}){if(!l.supports(t))return n.map(()=>({ok:!1,source:"fork",reason:"condition not evaluable \u2014 harness has no context sub-agent face"}));const s=n.map((o,d)=>`${d+1}. key=${o.key}: ${o.condition}`).join(`
8
- `),r=await l.extract({sessionId:t,prompt:`For EACH item below, decide yes/no based on the conversation context.
7
+ Based on the conversation context above, answer with exactly one word: yes or no.`});return e(parseConditionAnswer(s))},async judgeBatch({sessionId:t,items:n}){if(!h.supports(t))return n.map(()=>({ok:!1,source:"fork",reason:"condition not evaluable \u2014 harness has no context sub-agent face"}));const s=n.map((d,u)=>`${u+1}. key=${d.key}: ${d.condition}`).join(`
8
+ `),r=await h.extract({sessionId:t,prompt:`For EACH item below, decide yes/no based on the conversation context.
9
9
  ${s}
10
10
 
11
- Respond with ONLY a JSON object mapping each key to true or false, e.g. {"k1":true,"k2":false}. No other text.`}),i=B(r,n.map(o=>o.key));return i?n.map(o=>{const d=i.get(o.key);return e(d===void 0?null:d)}):n.map(()=>e(null))}}}function B(l,e){if(!l)return null;const t=l.match(/\{[\s\S]*\}/);if(!t)return null;try{const n=JSON.parse(t[0]),s=new Map;for(const r of e){const i=n[r];if(typeof i!="boolean")return null;s.set(r,i)}return s}catch{return null}}export function matchWhen(l,e){const t=(e??"").toLowerCase();return l.split(/[,,\n;;]/).map(n=>n.trim().toLowerCase()).filter(n=>n.length>0).some(n=>t.includes(n))}const P=5e3,U=1e4,J=6e4,L=2*36e5,W=1500,Y=9e4;export class OrchestrationEngine{deps;runtime=new Map;opening=new Map;startedByOrchestration=new Map;forkJudge;constructor(e){this.deps=e,this.forkJudge=createForkConditionJudge({supports:t=>e.supportsExtract?.(t)??!1,extract:t=>e.extract?.(t)??Promise.resolve(null)})}async start(e,t,n,s){const r=this.deps.orchestrations.getById(e);if(!r)return{ok:!1,status:404,error:"Orchestration not found"};const i=k(r);if(!i.ok)return{ok:!1,status:409,error:i.error};const{entries:o}=m(r.nodes,r.edges),d=s?o.filter(f=>s.includes(f.id)):o.filter(f=>f.kind==="session");if(d.length===0)return{ok:!1,status:409,error:"No matching entry node"};const u=this.activeOf(e);if(u)return await Promise.allSettled(d.map(f=>f.kind==="session"?this.deliver(u,{from:"",to:f.id,edgeId:"",text:n?.trim()||r.startText.trim()||r.name}):this.fireEdges(u,f.id,n??""))),this.maybeDone(u),{ok:!0,runId:u.id};const c={id:this.deps.runs.insert({orchestrationId:r.id,orchestrationName:r.name,projectId:r.projectId,trigger:t}).id,orchestrationId:r.id,trigger:t,name:r.name,nodes:r.nodes,edges:r.edges,state:{pending:{},events:[]},deliveries:0,forks:0,inflight:0,running:!0,finishing:!1};return this.runtime.set(c.id,c),this.emitRun(c,"running"),await Promise.allSettled(d.map(f=>f.kind==="session"?this.deliver(c,{from:"",to:f.id,edgeId:"",text:n?.trim()||r.startText.trim()||r.name}):this.fireEdges(c,f.id,n??""))),this.maybeDone(c),{ok:!0,runId:c.id}}cancel(e){const t=this.runtime.get(e);return t?this.finish(t,"cancelled"):!1}onEvent(e){try{if(!(this.deps.surfaceFor?.(e.source)??_(e.source)).idleSignals.includes(e.eventType))return;const n=this.hasPendingAnywhere(e.sessionId);for(const s of[...this.runtime.values()]){const r=s.state.pending[e.sessionId];!s.running||!r||r.length===0||(r.shift(),this.persist(s),this.fireEdges(s,e.sessionId))}if(!n){const s=Date.now(),r=this.startedByOrchestration.get(e.sessionId);r!==void 0&&s-r<J?this.startedByOrchestration.delete(e.sessionId):(this.startedByOrchestration.delete(e.sessionId),this.maybeAutoFire(e.sessionId))}}catch{}}hasPendingAnywhere(e){return[...this.runtime.values()].some(t=>(t.state.pending[e]?.length??0)>0)}maybeAutoFire(e){const t=Date.now();for(const n of this.entryIndex().get(e)??[]){if(!n.enabled||this.activeOf(n.id))continue;const s=`${n.id}:${e}`;if(t-(this.lastAutoFireAt.get(s)??0)<P)continue;const r=n.nodes.find(i=>i.id===e&&i.kind==="session");!r||!n.edges.some(i=>i.from===r.id)||(this.lastAutoFireAt.set(s,t),this.fireFromEntry(n,r.id))}}async fireFromEntry(e,t){if(!k(e).ok)return;const r={id:this.deps.runs.insert({orchestrationId:e.id,orchestrationName:e.name,projectId:e.projectId,trigger:"manual"}).id,orchestrationId:e.id,trigger:"manual",name:e.name,nodes:e.nodes,edges:e.edges,state:{pending:{},events:[]},deliveries:0,forks:0,inflight:0,running:!0,finishing:!1};this.runtime.set(r.id,r),this.emitRun(r,"running"),await this.fireEdges(r,t),this.maybeDone(r)}entryIndexCache=null;lastAutoFireAt=new Map;entryIndex(){if(this.entryIndexCache&&Date.now()-this.entryIndexCache.at<U)return this.entryIndexCache.map;const e=new Map;for(const t of this.deps.orchestrations.list()){if(!t.enabled)continue;const n=new Set(m(t.nodes,t.edges).entries.map(s=>s.id));for(const s of t.nodes){if(s.kind!=="session"||!n.has(s.id)&&!s.autoFire)continue;const r=e.get(s.id)??[];r.push(t),e.set(s.id,r)}}return this.entryIndexCache={at:Date.now(),map:e},e}cronTick(e=new Date){let t=0;try{const n=E(e);for(const s of this.deps.orchestrations.list()){if(!s.enabled||!s.cron||s.lastCronAt===n)continue;const r=A(s.cron);if(!r||!b(r,e))continue;const{entries:i}=m(s.nodes,s.edges);i.some(o=>o.kind==="session")&&(this.deps.orchestrations.setCronFired(s.id,n),!this.activeOf(s.id)&&(this.start(s.id,"cron"),t++))}}catch{}return t}rearm(){try{for(const e of this.deps.runs.listRunning()){const t=this.deps.orchestrations.getById(e.orchestrationId);if(!(t?k(t):{ok:!1}).ok){this.deps.runs.finish(e.id,"interrupted","daemon restarted \u2014 definition gone");continue}const s={id:e.id,orchestrationId:e.orchestrationId,trigger:e.trigger,name:e.orchestrationName,nodes:t.nodes,edges:t.edges,state:e.state,deliveries:e.deliveries,forks:0,inflight:0,running:!0,finishing:!1};this.runtime.set(s.id,s),this.emitRun(s,"running")}}catch{}try{this.deps.runs.prune()}catch{}}sweepTerminated(){let e=0;try{const t=this.deps.livenessOf?.()??w(),n=new Map(this.deps.sessions.listWithCounts().map(r=>[r.sessionId,r])),s=Date.now();for(const r of[...this.runtime.values()])if(r.running)for(const[i,o]of Object.entries(r.state.pending)){if(o.length===0)continue;const d=n.get(i);if(d&&y(d,t)!==!1){const u=Date.parse(o[0].at);Number.isFinite(u)&&s-u>L&&(this.staleOut(r,i,o[0].at),e++);continue}this.reviveOrDrop(r,i),e++}}catch{return e}return e}staleOut(e,t,n){const s=((Date.now()-Date.parse(n))/36e5).toFixed(1);this.dropPending(e,t,`pending stale ${s}h (oldest ${n}) \u2014 target alive but never turned; run failed to release the single-run gate`),this.finish(e,"failed",`pending stale on ${t} (${s}h) \u2014 run aborted`)}ensureOpenOnce(e){const t=this.opening.get(e);if(t)return t;const n=(this.deps.ensureOpen?.(e)??Promise.resolve({ok:!0})).finally(()=>this.opening.delete(e));return this.opening.set(e,n),n}async reviveOrDrop(e,t){const n=await this.ensureOpen(t);n.ok||!e.running||(this.dropPending(e,t,`pending dropped \u2014 session is not running: ${n.error}`),this.maybeDone(e))}async fireEdges(e,t,n=""){const s=e.edges.filter(a=>a.from===t);if(s.length===0){this.maybeDone(e);return}const r=n||(this.deps.deliverer.lastTurnOutput?.(t)?.output??null),i=r?r.length>O?`${r.slice(0,O)}\u2026`:r:null,o=r?T(r):{text:null,truncated:!1},d=this.deps.sessions.getBySessionId(t)?.title||t,u=new Map;if(!n){const a=s.filter(c=>c.condition?.trim()&&!(c.when?.trim()&&!matchWhen(c.when,o.text)));if(a.length>0){const c=await this.evaluateConditionsBatch(e,t,a,o.text,o.truncated);for(const[f,h]of c)u.set(f,h)}}await Promise.allSettled(s.map(async a=>{if(n){const h=x(a.text,{upstream:n,summary:n,session:t}),D=a.text.includes("{upstream}")?h:$(h,n);return this.deliver(e,{from:t,to:a.to,edgeId:a.id,text:D})}let c=null;if(a.extract?.trim()&&this.takeFork(e)&&(c=this.deps.supportsExtract?.(t)?await this.deps.extract?.({sessionId:t,prompt:a.extract.trim()})??null:null),a.when?.trim()&&!matchWhen(a.when,o.text)){this.record(e,{from:t,to:a.to,edgeId:a.id,ok:!1,stage:"condition",error:"pre-filter no match \u2014 upstream output lacks all keywords"});return}if(a.condition?.trim()){const h=u.get(a.id);if(h!==!0){h===void 0&&this.record(e,{from:t,to:a.to,edgeId:a.id,ok:!1,stage:"condition",error:`fork budget exhausted (${p}) \u2014 condition not evaluated`});return}}const f=x(a.text,{upstream:i,summary:c??i,session:d});if(M.test(f)){this.record(e,{from:t,to:a.to,edgeId:a.id,ok:!1,error:"unresolved template variable \u2014 no upstream output and no extract result"});return}return this.deliver(e,{from:t,to:a.to,edgeId:a.id,text:f})})),this.maybeDone(e)}async evaluateConditionsBatch(e,t,n,s,r){const i=new Map,o=this.deps.conditionJudge??this.forkJudge;if(!this.takeFork(e)){for(const d of n)this.record(e,{from:t,to:d.to,edgeId:d.id,ok:!1,stage:"condition",error:`fork budget exhausted (${p}) \u2014 condition not evaluated`});return i}if(o.judgeBatch&&n.length>1)try{const d=await o.judgeBatch({sessionId:t,items:n.map(u=>({key:u.id,condition:u.condition.trim()})),upstream:s});return n.forEach((u,a)=>{const c=d[a];c&&(i.set(u.id,c.ok),this.record(e,{from:t,to:u.to,edgeId:u.id,ok:c.ok,stage:"condition",...c.ok?{}:{error:c.reason??"condition not satisfied \u2014 delivery blocked"},...c.source?{source:c.source}:{},...this.conditionDetail(c.detail,r)}))}),i}catch{}for(const d of n)i.set(d.id,await this.evaluateCondition(e,t,d,s,r));return i}conditionDetail(e,t){const n=[e,t?`state truncated to ${g} chars`:null].filter(Boolean);return n.length>0?{detail:n.join(" \u2014 ")}:{}}async evaluateCondition(e,t,n,s,r=!1){const i=a=>(this.record(e,{from:t,to:n.to,edgeId:n.id,ok:!1,stage:"condition",error:a,...o?{source:o}:{},...this.conditionDetail(d,r)}),!1);let o,d,u;try{u=await(this.deps.conditionJudge??this.forkJudge).judge({sessionId:t,condition:n.condition.trim(),upstream:s}),o=u.source,d=u.detail}catch(a){return i(`condition judge failed \u2014 delivery blocked: ${a instanceof Error?a.message:String(a)}`)}return u.ok?(this.record(e,{from:t,to:n.to,edgeId:n.id,ok:!0,stage:"condition",...o?{source:o}:{},...this.conditionDetail(d,r)}),!0):i(u.reason??"condition not satisfied \u2014 delivery blocked")}async deliver(e,t){const n=await this.deliverOnce(e,t);return n||!e.running?n:(await new Promise(s=>setTimeout(s,this.deps.retryDelayMs??W)),e.running?this.deliverOnce(e,t):!1)}async deliverOnce(e,t){e.inflight++;try{if(!e.running)return!1;const n=e.nodes.find(i=>i.id===t.to);if(!n)return this.record(e,{...t,ok:!1,error:"target node is not in the snapshot"}),!1;if(n.kind==="webhook"){const i=await(this.deps.postJsonImpl??C)(n.url??"",n.secret,{orchestrationName:e.name,runId:e.id,from:t.from,text:t.text,at:new Date().toISOString()});return e.running?(e.deliveries++,this.record(e,{...t,ok:i.ok,...i.ok?{}:{error:i.error}}),this.checkBudget(e),i.ok):!1}if(!t.text.trim())return this.record(e,{...t,ok:!1,error:"edge has no message"}),!1;if(this.deps.deferWhenUserPresent&&this.userPresent(t.to)){const i=e.state.pending[t.to]??[];return i.push({messageId:`defer-${S()}`,at:new Date().toISOString()}),e.state.pending[t.to]=i,this.record(e,{from:t.from,to:t.to,edgeId:t.edgeId,ok:!0,stage:"defer"}),!0}if(this.aliveOf(t.to)===!1){const i=Date.now(),o=await this.ensureOpenOnce(t.to);if(!e.running)return!1;if(this.record(e,{from:"",to:t.to,edgeId:t.edgeId,ok:o.ok,stage:"ensure",ms:Date.now()-i,...o.ok?{}:{error:o.error}}),!o.ok)return this.record(e,{...t,ok:!1,error:`target session is not open: ${o.error}`}),!1;if(this.startedByOrchestration.set(t.to,Date.now()),await this.deps.deliverer.awaitInboxReady?.(t.to,N),!e.running)return!1}const s=await this.deps.deliverer.send({toSessionId:t.to,text:t.text,mode:"prompt",fromKind:"dashboard"});if(!e.running)return!1;if(!s.ok)return this.record(e,{...t,ok:!1,error:s.error}),!1;if(s.status==="failed"){const i=s.message;return this.record(e,{...t,ok:!1,error:i.error??(i.reason==="offline"?"target session is offline":"delivery failed")}),!1}const r=e.state.pending[t.to]??[];return r.push({messageId:s.messageId,at:new Date().toISOString()}),e.state.pending[t.to]=r,e.deliveries++,this.record(e,{...t,ok:!0}),this.checkBudget(e),!0}catch(n){return this.record(e,{...t,ok:!1,error:n instanceof Error?n.message:String(n)}),!1}finally{e.inflight--}}checkBudget(e){e.deliveries>=(this.deps.maxDeliveries??I)&&this.finish(e,"failed",`delivery budget exceeded (${I})`)}takeFork(e){return e.forks>=p?!1:(e.forks++,!0)}record(e,t){if(!e.running)return;const n={at:new Date().toISOString(),from:t.from,to:t.to,edgeId:t.edgeId,ok:t.ok,...t.error?{error:t.error}:{},...t.stage?{stage:t.stage}:{},...t.ms===void 0?{}:{ms:t.ms},...t.source?{source:t.source}:{},...t.detail?{detail:t.detail}:{}};e.state.events.push(n),e.state.events.length>200&&e.state.events.splice(0,e.state.events.length-200),this.persist(e),this.deps.bus.emit({kind:"orchestration.delivery",orchestrationId:e.orchestrationId,runId:e.id,edgeId:t.edgeId,from:t.from,to:t.to,ok:t.ok,...t.error?{error:t.error}:{},deliveries:e.deliveries,pendingCounts:v(e.state),at:n.at})}maybeDone(e){if(!e.running||e.finishing||e.inflight>0||Object.values(e.state.pending).some(s=>s.length>0))return;e.finishing=!0;const t=e.state.events.some(s=>s.ok&&s.stage===void 0);if(!t&&e.state.events.length>0&&e.state.events.every(s=>s.stage==="condition")){this.finish(e,"done");return}const n=[...e.state.events].reverse().find(s=>!s.ok)?.error??null;this.finish(e,t?"done":"failed",t?null:n??"no delivery succeeded")}finish(e,t,n=null){if(!e.running||(e.running=!1,!this.deps.runs.finish(e.id,t,n,e.state)))return!1;this.runtime.delete(e.id),this.emitRun(e,t);try{this.deps.runs.prune()}catch{}return!0}persist(e){this.deps.runs.updateState(e.id,e.state,e.deliveries),this.emitRun(e,"running")}emitRun(e,t){this.deps.bus.emit({kind:"orchestration.run",orchestrationId:e.orchestrationId,runId:e.id,status:t,trigger:e.trigger,deliveries:e.deliveries,pendingCounts:v(e.state),at:new Date().toISOString()})}dropPending(e,t,n){delete e.state.pending[t],this.record(e,{from:"",to:t,edgeId:"",ok:!1,error:n})}activeOf(e){return[...this.runtime.values()].find(t=>t.running&&t.orchestrationId===e)}aliveOf(e){const t=this.deps.sessions.listWithCounts().find(s=>s.sessionId===e);if(!t)return!1;const n=this.deps.livenessOf?.()??w();return y(t,n)}userPresent(e){if(this.deps.sessions.turnOpenOf(e))return!1;const t=this.deps.sessions.listWithCounts().find(s=>s.sessionId===e);if(!t)return!1;const n=Date.parse(t.lastSeenAt);return Number.isFinite(n)&&Date.now()-n<Y}async ensureOpen(e){if(!this.deps.ensureOpen)return{ok:!1,error:"terminal auto-open unavailable"};try{return await this.deps.ensureOpen(e)}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}}}
11
+ Respond with ONLY a JSON object mapping each key to true or false, e.g. {"k1":true,"k2":false}. No other text.`}),i=N(r,n.map(d=>d.key));return i?n.map(d=>{const u=i.get(d.key);return e(u===void 0?null:u)}):n.map(()=>e(null))}}}function N(h,e){if(!h)return null;const t=h.match(/\{[\s\S]*\}/);if(!t)return null;try{const n=JSON.parse(t[0]),s=new Map;for(const r of e){const i=n[r];if(typeof i!="boolean")return null;s.set(r,i)}return s}catch{return null}}export function matchWhen(h,e){const t=(e??"").toLowerCase();return h.split(/[,,\n;;]/).map(n=>n.trim().toLowerCase()).filter(n=>n.length>0).some(n=>t.includes(n))}const T=5e3,L=1e4,W=6e4,_=2*36e5,H=1500,U=9e4;export class OrchestrationEngine{deps;runtime=new Map;opening=new Map;startedByOrchestration=new Map;forkJudge;constructor(e){this.deps=e,this.forkJudge=createForkConditionJudge({supports:t=>e.supportsExtract?.(t)??!1,extract:t=>e.extract?.(t)??Promise.resolve(null)})}async start(e,t,n,s){const r=this.deps.orchestrations.getById(e);if(!r)return{ok:!1,status:404,error:"Orchestration not found"};const i=k(r);if(!i.ok)return{ok:!1,status:409,error:i.error};const{entries:d}=m(r.nodes,r.edges),u=s?d.filter(a=>s.includes(a.id)):d.filter(a=>a.kind==="session");if(u.length===0)return{ok:!1,status:409,error:"No matching entry node"};const c=this.activeOf(e);if(c)return await Promise.allSettled(u.map(a=>a.kind==="session"?this.deliver(c,{from:"",to:a.id,edgeId:"",text:n?.trim()||r.startText.trim()||r.name}):this.fireEdges(c,a.id,n??""))),this.maybeDone(c),{ok:!0,runId:c.id};const o={id:this.deps.runs.insert({orchestrationId:r.id,orchestrationName:r.name,projectId:r.projectId,trigger:t}).id,orchestrationId:r.id,trigger:t,name:r.name,nodes:r.nodes,edges:r.edges,state:{pending:{},events:[]},deliveries:0,forks:0,inflight:0,running:!0,finishing:!1};return this.runtime.set(o.id,o),this.emitRun(o,"running"),await Promise.allSettled(u.map(a=>a.kind==="session"?this.deliver(o,{from:"",to:a.id,edgeId:"",text:n?.trim()||r.startText.trim()||r.name}):this.fireEdges(o,a.id,n??""))),this.maybeDone(o),{ok:!0,runId:o.id}}cancel(e){const t=this.runtime.get(e);return t?this.finish(t,"cancelled"):!1}onEvent(e){try{if(!(this.deps.surfaceFor?.(e.source)??S(e.source)).idleSignals.includes(e.eventType))return;const t=this.hasPendingAnywhere(e.sessionId);for(const n of[...this.runtime.values()]){const s=n.state.pending[e.sessionId];!n.running||!s||s.length===0||(s.shift(),this.persist(n),this.fireEdges(n,e.sessionId))}if(!t){const n=Date.now(),s=this.startedByOrchestration.get(e.sessionId);s!==void 0&&n-s<W?this.startedByOrchestration.delete(e.sessionId):(this.startedByOrchestration.delete(e.sessionId),this.maybeAutoFire(e.sessionId))}}catch{}}hasPendingAnywhere(e){return[...this.runtime.values()].some(t=>(t.state.pending[e]?.length??0)>0)}maybeAutoFire(e){const t=Date.now();for(const n of this.entryIndex().get(e)??[]){if(!n.enabled||this.activeOf(n.id))continue;const s=`${n.id}:${e}`;if(t-(this.lastAutoFireAt.get(s)??0)<T)continue;const r=n.nodes.find(i=>i.id===e&&i.kind==="session");!r||!n.edges.some(i=>i.from===r.id)||(this.lastAutoFireAt.set(s,t),this.fireFromEntry(n,r.id))}}async fireFromEntry(e,t){if(!k(e).ok)return;const n={id:this.deps.runs.insert({orchestrationId:e.id,orchestrationName:e.name,projectId:e.projectId,trigger:"manual"}).id,orchestrationId:e.id,trigger:"manual",name:e.name,nodes:e.nodes,edges:e.edges,state:{pending:{},events:[]},deliveries:0,forks:0,inflight:0,running:!0,finishing:!1};this.runtime.set(n.id,n),this.emitRun(n,"running"),await this.fireEdges(n,t),this.maybeDone(n)}entryIndexCache=null;lastAutoFireAt=new Map;entryIndex(){if(this.entryIndexCache&&Date.now()-this.entryIndexCache.at<L)return this.entryIndexCache.map;const e=new Map;for(const t of this.deps.orchestrations.list()){if(!t.enabled)continue;const n=new Set(m(t.nodes,t.edges).entries.map(s=>s.id));for(const s of t.nodes){if(s.kind!=="session"||!n.has(s.id)&&!s.autoFire)continue;const r=e.get(s.id)??[];r.push(t),e.set(s.id,r)}}return this.entryIndexCache={at:Date.now(),map:e},e}cronTick(e=new Date){let t=0;try{const n=$(e);for(const s of this.deps.orchestrations.list()){if(!s.enabled||!s.cron||s.lastCronAt===n)continue;const r=F(s.cron);if(!r||!C(r,e))continue;const{entries:i}=m(s.nodes,s.edges);i.some(d=>d.kind==="session")&&(this.deps.orchestrations.setCronFired(s.id,n),!this.activeOf(s.id)&&(this.start(s.id,"cron"),t++))}}catch{}return t}rearm(){try{for(const e of this.deps.runs.listRunning()){const t=this.deps.orchestrations.getById(e.orchestrationId);if(!(t?k(t):{ok:!1}).ok){this.deps.runs.finish(e.id,"interrupted","daemon restarted \u2014 definition gone");continue}const n={id:e.id,orchestrationId:e.orchestrationId,trigger:e.trigger,name:e.orchestrationName,nodes:t.nodes,edges:t.edges,state:e.state,deliveries:e.deliveries,forks:0,inflight:0,running:!0,finishing:!1};this.runtime.set(n.id,n),this.emitRun(n,"running")}}catch{}try{this.deps.runs.prune()}catch{}}sweepTerminated(){let e=0;try{const t=this.deps.livenessOf?.()??I(),n=new Map(this.deps.sessions.listWithCounts().map(r=>[r.sessionId,r])),s=Date.now();for(const r of[...this.runtime.values()])if(r.running)for(const[i,d]of Object.entries(r.state.pending)){if(d.length===0)continue;const u=n.get(i);if(u&&y(u,t)!==!1){const c=Date.parse(d[0].at);Number.isFinite(c)&&s-c>_&&(this.staleOut(r,i,d[0].at),e++);continue}this.reviveOrDrop(r,i),e++}}catch{return e}return e}staleOut(e,t,n){const s=((Date.now()-Date.parse(n))/36e5).toFixed(1);this.dropPending(e,t,`pending stale ${s}h (oldest ${n}) \u2014 target alive but never turned; run failed to release the single-run gate`),this.finish(e,"failed",`pending stale on ${t} (${s}h) \u2014 run aborted`)}ensureOpenOnce(e){const t=this.opening.get(e);if(t)return t;const n=(this.deps.ensureOpen?.(e)??Promise.resolve({ok:!0})).finally(()=>this.opening.delete(e));return this.opening.set(e,n),n}async reviveOrDrop(e,t){const n=await this.ensureOpen(t);n.ok||!e.running||(this.dropPending(e,t,`pending dropped \u2014 session is not running: ${n.error}`),this.maybeDone(e))}async fireEdges(e,t,n=""){const s=e.edges.filter(o=>o.from===t);if(s.length===0){this.maybeDone(e);return}const r=n||(this.deps.deliverer.lastTurnOutput?.(t)?.output??null),i=r?r.length>x?`${r.slice(0,x)}\u2026`:r:null,d=r?P(r):{text:null,truncated:!1},u=this.deps.sessions.getBySessionId(t)?.title||t,c=new Map;if(!n){const o=s.filter(a=>a.condition?.trim()&&!(a.when?.trim()&&!matchWhen(a.when,d.text)));if(o.length>0){const a=await this.evaluateConditionsBatch(e,t,o,d.text,d.truncated);for(const[f,l]of a)c.set(f,l)}}await Promise.allSettled(s.map(async o=>{if(n){const l=O(o.text,{upstream:n,summary:n,session:t}),b=o.text.includes("{upstream}")?l:j(l,n);return this.deliver(e,{from:t,to:o.to,edgeId:o.id,text:b})}let a=null;if(o.extract?.trim()&&this.takeFork(e)&&(a=this.deps.supportsExtract?.(t)?await this.deps.extract?.({sessionId:t,prompt:o.extract.trim()})??null:null),o.when?.trim()&&!matchWhen(o.when,d.text)){this.record(e,{from:t,to:o.to,edgeId:o.id,ok:!1,stage:"condition",error:"pre-filter no match \u2014 upstream output lacks all keywords"});return}if(o.condition?.trim()){const l=c.get(o.id);if(l!==!0){l===void 0&&this.record(e,{from:t,to:o.to,edgeId:o.id,ok:!1,stage:"condition",error:`fork budget exhausted (${p}) \u2014 condition not evaluated`});return}}const f=O(o.text,{upstream:i,summary:a??i,session:u});if(M.test(f)){this.record(e,{from:t,to:o.to,edgeId:o.id,ok:!1,error:"unresolved template variable \u2014 no upstream output and no extract result"});return}return this.deliver(e,{from:t,to:o.to,edgeId:o.id,text:f})})),this.maybeDone(e)}async evaluateConditionsBatch(e,t,n,s,r){const i=new Map,d=this.deps.conditionJudge??this.forkJudge;if(!this.takeFork(e)){for(const u of n)this.record(e,{from:t,to:u.to,edgeId:u.id,ok:!1,stage:"condition",error:`fork budget exhausted (${p}) \u2014 condition not evaluated`});return i}if(d.judgeBatch&&n.length>1)try{const u=await d.judgeBatch({sessionId:t,items:n.map(c=>({key:c.id,condition:c.condition.trim()})),upstream:s});return n.forEach((c,o)=>{const a=u[o];a&&(i.set(c.id,a.ok),this.record(e,{from:t,to:c.to,edgeId:c.id,ok:a.ok,stage:"condition",...a.ok?{}:{error:a.reason??"condition not satisfied \u2014 delivery blocked"},...a.source?{source:a.source}:{},...this.conditionDetail(a.detail,r)}))}),i}catch{}for(const u of n)i.set(u.id,await this.evaluateCondition(e,t,u,s,r));return i}conditionDetail(e,t){const n=[e,t?`state truncated to ${g} chars`:null].filter(Boolean);return n.length>0?{detail:n.join(" \u2014 ")}:{}}async evaluateCondition(e,t,n,s,r=!1){const i=o=>(this.record(e,{from:t,to:n.to,edgeId:n.id,ok:!1,stage:"condition",error:o,...d?{source:d}:{},...this.conditionDetail(u,r)}),!1);let d,u,c;try{c=await(this.deps.conditionJudge??this.forkJudge).judge({sessionId:t,condition:n.condition.trim(),upstream:s}),d=c.source,u=c.detail}catch(o){return i(`condition judge failed \u2014 delivery blocked: ${o instanceof Error?o.message:String(o)}`)}return c.ok?(this.record(e,{from:t,to:n.to,edgeId:n.id,ok:!0,stage:"condition",...d?{source:d}:{},...this.conditionDetail(u,r)}),!0):i(c.reason??"condition not satisfied \u2014 delivery blocked")}async deliver(e,t){const n=await this.deliverOnce(e,t);return n||!e.running?n:(await new Promise(s=>setTimeout(s,this.deps.retryDelayMs??H)),e.running?this.deliverOnce(e,t):!1)}async deliverOnce(e,t){e.inflight++;try{if(!e.running)return!1;const n=e.nodes.find(i=>i.id===t.to);if(!n)return this.record(e,{...t,ok:!1,error:"target node is not in the snapshot"}),!1;if(n.kind==="webhook"){const i=await(this.deps.postJsonImpl??E)(n.url??"",n.secret,{orchestrationName:e.name,runId:e.id,from:t.from,text:t.text,at:new Date().toISOString()});return e.running?(e.deliveries++,this.record(e,{...t,ok:i.ok,...i.ok?{}:{error:i.error}}),this.checkBudget(e),i.ok):!1}if(!t.text.trim())return this.record(e,{...t,ok:!1,error:"edge has no message"}),!1;if(this.deps.deferWhenUserPresent&&this.userPresent(t.to)){const i=e.state.pending[t.to]??[];return i.push({messageId:`defer-${D()}`,at:new Date().toISOString()}),e.state.pending[t.to]=i,this.record(e,{from:t.from,to:t.to,edgeId:t.edgeId,ok:!0,stage:"defer"}),!0}if(this.aliveOf(t.to)===!1){const i=Date.now(),d=await this.ensureOpenOnce(t.to);if(!e.running)return!1;if(this.record(e,{from:"",to:t.to,edgeId:t.edgeId,ok:d.ok,stage:"ensure",ms:Date.now()-i,...d.ok?{}:{error:d.error}}),!d.ok)return this.record(e,{...t,ok:!1,error:`target session is not open: ${d.error}`}),!1;if(this.startedByOrchestration.set(t.to,Date.now()),await this.deps.deliverer.awaitInboxReady?.(t.to,A),!e.running)return!1}const s=await this.deps.deliverer.send({toSessionId:t.to,text:t.text,mode:"prompt",fromKind:"dashboard"});if(!e.running)return!1;if(!s.ok)return this.record(e,{...t,ok:!1,error:s.error}),!1;if(s.status==="failed"){const i=s.message;return this.record(e,{...t,ok:!1,error:i.error??(i.reason==="offline"?"target session is offline":"delivery failed")}),!1}const r=e.state.pending[t.to]??[];return r.push({messageId:s.messageId,at:new Date().toISOString()}),e.state.pending[t.to]=r,e.deliveries++,this.record(e,{...t,ok:!0}),this.checkBudget(e),!0}catch(n){return this.record(e,{...t,ok:!1,error:n instanceof Error?n.message:String(n)}),!1}finally{e.inflight--}}checkBudget(e){e.deliveries>=(this.deps.maxDeliveries??w)&&this.finish(e,"failed",`delivery budget exceeded (${w})`)}takeFork(e){return e.forks>=p?!1:(e.forks++,!0)}record(e,t){if(!e.running)return;const n={at:new Date().toISOString(),from:t.from,to:t.to,edgeId:t.edgeId,ok:t.ok,...t.error?{error:t.error}:{},...t.stage?{stage:t.stage}:{},...t.ms===void 0?{}:{ms:t.ms},...t.source?{source:t.source}:{},...t.detail?{detail:t.detail}:{}};e.state.events.push(n),e.state.events.length>200&&e.state.events.splice(0,e.state.events.length-200),this.persist(e),this.deps.bus.emit({kind:"orchestration.delivery",orchestrationId:e.orchestrationId,runId:e.id,edgeId:t.edgeId,from:t.from,to:t.to,ok:t.ok,...t.error?{error:t.error}:{},deliveries:e.deliveries,pendingCounts:v(e.state),at:n.at})}maybeDone(e){if(!e.running||e.finishing||e.inflight>0||Object.values(e.state.pending).some(s=>s.length>0))return;e.finishing=!0;const t=e.state.events.some(s=>s.ok&&s.stage===void 0);if(!t&&e.state.events.length>0&&e.state.events.every(s=>s.stage==="condition")){this.finish(e,"done");return}const n=[...e.state.events].reverse().find(s=>!s.ok)?.error??null;this.finish(e,t?"done":"failed",t?null:n??"no delivery succeeded")}finish(e,t,n=null){if(!e.running||(e.running=!1,!this.deps.runs.finish(e.id,t,n,e.state)))return!1;this.runtime.delete(e.id),this.emitRun(e,t);try{this.deps.runs.prune()}catch{}return!0}persist(e){this.deps.runs.updateState(e.id,e.state,e.deliveries),this.emitRun(e,"running")}emitRun(e,t){this.deps.bus.emit({kind:"orchestration.run",orchestrationId:e.orchestrationId,runId:e.id,status:t,trigger:e.trigger,deliveries:e.deliveries,pendingCounts:v(e.state),at:new Date().toISOString()})}dropPending(e,t,n){delete e.state.pending[t],this.record(e,{from:"",to:t,edgeId:"",ok:!1,error:n})}activeOf(e){return[...this.runtime.values()].find(t=>t.running&&t.orchestrationId===e)}aliveOf(e){const t=this.deps.sessions.listWithCounts().find(s=>s.sessionId===e);if(!t)return!1;const n=this.deps.livenessOf?.()??I();return y(t,n)}userPresent(e){if(this.deps.sessions.turnOpenOf(e))return!1;const t=this.deps.sessions.listWithCounts().find(s=>s.sessionId===e);if(!t)return!1;const n=Date.parse(t.lastSeenAt);return Number.isFinite(n)&&Date.now()-n<U}async ensureOpen(e){if(!this.deps.ensureOpen)return{ok:!1,error:"terminal auto-open unavailable"};try{return await this.deps.ensureOpen(e)}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}}}
@@ -1 +1 @@
1
- import{graphDirections as p}from"../repositories/orchestrations.js";export const ORCH_MAX_NODES=24,ORCH_MAX_EDGES=64,ORCH_MAX_DELIVERIES=200,ORCH_MAX_FORKS=50;export function validateOrchestration(d){const{nodes:n,edges:t}=d;if(n.length===0)return{ok:!1,error:"At least one node is required"};if(n.length>ORCH_MAX_NODES)return{ok:!1,error:`At most ${ORCH_MAX_NODES} nodes per orchestration`};const s=new Set;for(const e of n){if(s.has(e.id))return{ok:!1,error:`Duplicate node ${e.id}`};if(s.add(e.id),e.kind==="webhook"&&!e.id.startsWith("w:"))return{ok:!1,error:`Webhook node id must start with "w:": ${e.id}`};if(e.kind==="trigger"&&!e.id.startsWith("t:"))return{ok:!1,error:`Trigger node id must start with "t:": ${e.id}`}}if(t.length>ORCH_MAX_EDGES)return{ok:!1,error:`At most ${ORCH_MAX_EDGES} edges per orchestration`};const i=new Set,a=new Set;for(const e of t){if(i.has(e.id))return{ok:!1,error:`Duplicate edge id ${e.id}`};if(i.add(e.id),!s.has(e.from)||!s.has(e.to))return{ok:!1,error:`Edge ${e.id} references a node outside the node set`};if(e.from===e.to)return{ok:!1,error:`Self-loop on ${e.from}`};const h=`${e.from}->${e.to}`;if(a.has(h))return{ok:!1,error:`Duplicate edge ${e.from}\u2192${e.to}`};a.add(h)}const{entries:o}=p(n,t),r=new Set(t.map(e=>e.to)),f=new Set(t.map(e=>e.from)),c=new Set(o.filter(e=>e.kind==="webhook").map(e=>e.id)),l=t.find(e=>!e.text.trim()&&!c.has(e.from)&&!e.extract?.trim());if(l)return{ok:!1,error:`Edge ${l.from}\u2192${l.to} has no message`};for(const e of t)if(c.has(e.from)&&(e.condition?.trim()||e.when?.trim()))return{ok:!1,error:`Edge ${e.from}\u2192${e.to} cannot have a condition/pre-filter (webhook entries have no session to ask)`};for(const e of n)if(e.kind==="webhook"&&e.autoFire)return{ok:!1,error:`Webhook node ${e.id} cannot have autoFire`};for(const e of n)if(e.kind==="webhook"){if(r.has(e.id)&&f.has(e.id))return{ok:!1,error:`Webhook node ${e.id} cannot have both inbound and outbound edges`};if(!r.has(e.id)&&!f.has(e.id))return{ok:!1,error:`Webhook node ${e.id} is not connected`}}for(const e of n)if(e.kind==="trigger"){if(r.has(e.id))return{ok:!1,error:`Trigger node ${e.id} cannot have inbound edges`};if(!f.has(e.id))return{ok:!1,error:`Trigger node ${e.id} is not connected`}}for(const e of n){if(e.kind!=="webhook"||!r.has(e.id))continue;const h=e.url??"";let u=null;try{u=new URL(h)}catch{u=null}if(!u||u.protocol!=="http:"&&u.protocol!=="https:")return{ok:!1,error:`Webhook node ${e.id} needs a valid http(s) URL`}}const k=findCycle(n,t);return k?{ok:!1,error:`Cycle detected: ${k.join(" \u2192 ")}`}:{ok:!0}}export function findCycle(d,n){const t=new Map;for(const o of d)t.set(o.id,[]);for(const o of n){const r=t.get(o.from);r&&r.push(o.to)}const s=new Map,i=[],a=o=>{s.set(o,1),i.push(o);for(const r of t.get(o)??[]){const f=s.get(r)??0;if(f===1)return[...i.slice(i.indexOf(r)),r];if(f===0){const c=a(r);if(c)return c}}return i.pop(),s.set(o,2),null};for(const o of d)if((s.get(o.id)??0)===0){const r=a(o.id);if(r)return r}return null}export function ranksOf(d,n){const t=new Map(d.map(o=>[o.id,0])),s=new Map(d.map(o=>[o.id,[]]));for(const o of n)s.get(o.from)?.push(o.to),t.set(o.to,(t.get(o.to)??0)+1);const i=new Map(d.map(o=>[o.id,0])),a=d.filter(o=>(t.get(o.id)??0)===0).map(o=>o.id);for(let o=0;o<a.length;o++){const r=a[o];for(const f of s.get(r)??[]){i.set(f,Math.max(i.get(f)??0,(i.get(r)??0)+1));const c=(t.get(f)??0)-1;t.set(f,c),c===0&&a.push(f)}}return i}
1
+ import{graphDirections as p}from"../repositories/orchestrations.js";export const ORCH_MAX_NODES=24,ORCH_MAX_EDGES=64,ORCH_MAX_DELIVERIES=200,ORCH_MAX_FORKS=50;export function validateOrchestration(f){const{nodes:n,edges:t}=f;if(n.length===0)return{ok:!1,error:"At least one node is required"};if(n.length>ORCH_MAX_NODES)return{ok:!1,error:`At most ${ORCH_MAX_NODES} nodes per orchestration`};const i=new Set;for(const o of n){if(i.has(o.id))return{ok:!1,error:`Duplicate node ${o.id}`};if(i.add(o.id),o.kind==="webhook"&&!o.id.startsWith("w:"))return{ok:!1,error:`Webhook node id must start with "w:": ${o.id}`};if(o.kind==="trigger"&&!o.id.startsWith("t:"))return{ok:!1,error:`Trigger node id must start with "t:": ${o.id}`}}if(t.length>ORCH_MAX_EDGES)return{ok:!1,error:`At most ${ORCH_MAX_EDGES} edges per orchestration`};const s=new Set,a=new Set;for(const o of t){if(s.has(o.id))return{ok:!1,error:`Duplicate edge id ${o.id}`};if(s.add(o.id),!i.has(o.from)||!i.has(o.to))return{ok:!1,error:`Edge ${o.id} references a node outside the node set`};if(o.from===o.to)return{ok:!1,error:`Self-loop on ${o.from}`};const h=`${o.from}->${o.to}`;if(a.has(h))return{ok:!1,error:`Duplicate edge ${o.from}\u2192${o.to}`};a.add(h)}const{entries:e}=p(n,t),r=new Set(t.map(o=>o.to)),d=new Set(t.map(o=>o.from)),c=new Set(e.filter(o=>o.kind==="webhook").map(o=>o.id)),k=t.find(o=>!o.text.trim()&&!c.has(o.from)&&!o.extract?.trim());if(k)return{ok:!1,error:`Edge ${k.from}\u2192${k.to} has no message`};for(const o of t)if(c.has(o.from)&&(o.condition?.trim()||o.when?.trim()))return{ok:!1,error:`Edge ${o.from}\u2192${o.to} cannot have a condition/pre-filter (webhook entries have no session to ask)`};for(const o of n)if(o.kind==="webhook"&&o.autoFire)return{ok:!1,error:`Webhook node ${o.id} cannot have autoFire`};for(const o of n)if(o.kind==="webhook"){if(r.has(o.id)&&d.has(o.id))return{ok:!1,error:`Webhook node ${o.id} cannot have both inbound and outbound edges`};if(!r.has(o.id)&&!d.has(o.id))return{ok:!1,error:`Webhook node ${o.id} is not connected`}}for(const o of n)if(o.kind==="trigger"){if(r.has(o.id))return{ok:!1,error:`Trigger node ${o.id} cannot have inbound edges`};if(!d.has(o.id))return{ok:!1,error:`Trigger node ${o.id} is not connected`}}for(const o of n){if(o.kind!=="webhook"||!r.has(o.id))continue;const h=o.url??"";let u=null;try{u=new URL(h)}catch{u=null}if(!u||u.protocol!=="http:"&&u.protocol!=="https:")return{ok:!1,error:`Webhook node ${o.id} needs a valid http(s) URL`}}const g=findCycle(n,t);return g?{ok:!1,error:`Cycle detected: ${g.join(" \u2192 ")}`}:{ok:!0}}export function findCycle(f,n){const t=new Map;for(const e of f)t.set(e.id,[]);for(const e of n){const r=t.get(e.from);r&&r.push(e.to)}const i=new Map,s=[],a=e=>{i.set(e,1),s.push(e);for(const r of t.get(e)??[]){const d=i.get(r)??0;if(d===1)return[...s.slice(s.indexOf(r)),r];if(d===0){const c=a(r);if(c)return c}}return s.pop(),i.set(e,2),null};for(const e of f)if((i.get(e.id)??0)===0){const r=a(e.id);if(r)return r}return null}export function ranksOf(f,n){const t=new Map(f.map(e=>[e.id,0])),i=new Map(f.map(e=>[e.id,[]]));for(const e of n)i.get(e.from)?.push(e.to),t.set(e.to,(t.get(e.to)??0)+1);const s=new Map(f.map(e=>[e.id,0])),a=f.filter(e=>(t.get(e.id)??0)===0).map(e=>e.id);for(let e=0;e<a.length;e++){const r=a[e];for(const d of i.get(r)??[]){s.set(d,Math.max(s.get(d)??0,(s.get(r)??0)+1));const c=(t.get(d)??0)-1;t.set(d,c),c===0&&a.push(d)}}return s}