stitchkit 0.94.0 → 0.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/agent-runtime/coding-shell-process.d.ts.map +1 -1
  3. package/dist/agent-runtime/coding-tool-listing.d.ts.map +1 -1
  4. package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -1
  5. package/dist/agent-runtime/contained-darwin.d.ts +38 -0
  6. package/dist/agent-runtime/contained-darwin.d.ts.map +1 -0
  7. package/dist/agent-runtime/contained-files.d.ts +11 -26
  8. package/dist/agent-runtime/contained-files.d.ts.map +1 -1
  9. package/dist/agent-runtime/contained-scan.d.ts +21 -0
  10. package/dist/agent-runtime/contained-scan.d.ts.map +1 -0
  11. package/dist/agent-runtime/contained-write.d.ts +9 -0
  12. package/dist/agent-runtime/contained-write.d.ts.map +1 -0
  13. package/dist/agent-runtime/deferred-tool-types.d.ts +3 -2
  14. package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
  15. package/dist/agent-runtime/harness-control.d.ts +0 -1
  16. package/dist/agent-runtime/harness-control.d.ts.map +1 -1
  17. package/dist/agent-runtime/harness.d.ts +0 -5
  18. package/dist/agent-runtime/harness.d.ts.map +1 -1
  19. package/dist/agent-runtime/managed-tools.d.ts +3 -2
  20. package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
  21. package/dist/agent-runtime/provider-origin.d.ts +7 -0
  22. package/dist/agent-runtime/provider-origin.d.ts.map +1 -1
  23. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
  24. package/dist/agent-runtime/runtime.d.ts +1 -1
  25. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  26. package/dist/agent-runtime/schedule-records.d.ts +34 -0
  27. package/dist/agent-runtime/schedule-records.d.ts.map +1 -0
  28. package/dist/agent-runtime/schedules.d.ts +34 -0
  29. package/dist/agent-runtime/schedules.d.ts.map +1 -1
  30. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +434 -0
  31. package/dist/agent-runtime/store-conformance-lifecycle.d.ts.map +1 -0
  32. package/dist/agent-runtime/store-conformance-ordering.d.ts +18 -0
  33. package/dist/agent-runtime/store-conformance-ordering.d.ts.map +1 -0
  34. package/dist/agent-runtime/store-conformance-support.d.ts +130 -0
  35. package/dist/agent-runtime/store-conformance-support.d.ts.map +1 -0
  36. package/dist/agent-runtime/store-conformance-terminal.d.ts +12 -0
  37. package/dist/agent-runtime/store-conformance-terminal.d.ts.map +1 -0
  38. package/dist/agent-runtime/store-conformance.d.ts.map +1 -1
  39. package/dist/application/admission-rate.d.ts +21 -0
  40. package/dist/application/admission-rate.d.ts.map +1 -0
  41. package/dist/application/admission-run.d.ts +8 -0
  42. package/dist/application/admission-run.d.ts.map +1 -0
  43. package/dist/application/admission.d.ts.map +1 -1
  44. package/dist/application/diagnostic-journal-lock.d.ts +9 -34
  45. package/dist/application/diagnostic-journal-lock.d.ts.map +1 -1
  46. package/dist/application/diagnostic-journal-manager.d.ts.map +1 -1
  47. package/dist/application/diagnostic-journal-storage.d.ts.map +1 -1
  48. package/dist/application/kernel-resource-context.d.ts.map +1 -1
  49. package/dist/application/notification-outbox.d.ts.map +1 -1
  50. package/dist/application/process-lifecycle-transitions.d.ts +175 -0
  51. package/dist/application/process-lifecycle-transitions.d.ts.map +1 -0
  52. package/dist/application/process-lifecycle.d.ts +3 -131
  53. package/dist/application/process-lifecycle.d.ts.map +1 -1
  54. package/dist/application/schedule.d.ts +0 -1
  55. package/dist/application/schedule.d.ts.map +1 -1
  56. package/dist/application/watch-hub-core.d.ts +122 -0
  57. package/dist/application/watch-hub-core.d.ts.map +1 -0
  58. package/dist/application/watch-hub.d.ts +2 -2
  59. package/dist/application/watch-hub.d.ts.map +1 -1
  60. package/dist/browser/client-executors.d.ts +14 -0
  61. package/dist/browser/client-executors.d.ts.map +1 -0
  62. package/dist/browser/client-multipart.d.ts +1 -1
  63. package/dist/browser/client-multipart.d.ts.map +1 -1
  64. package/dist/browser/client.d.ts +3 -2
  65. package/dist/browser/client.d.ts.map +1 -1
  66. package/dist/browser/http-fetch.d.ts +22 -0
  67. package/dist/browser/http-fetch.d.ts.map +1 -0
  68. package/dist/browser/http.d.ts +1 -3
  69. package/dist/browser/http.d.ts.map +1 -1
  70. package/dist/browser/live-state-core.d.ts +63 -0
  71. package/dist/browser/live-state-core.d.ts.map +1 -0
  72. package/dist/browser/live-state.d.ts.map +1 -1
  73. package/dist/browser/realtime-client.d.ts +26 -0
  74. package/dist/browser/realtime-client.d.ts.map +1 -0
  75. package/dist/browser/socket-io-connection.d.ts +82 -0
  76. package/dist/browser/socket-io-connection.d.ts.map +1 -0
  77. package/dist/browser/socket-io-peer.d.ts +20 -0
  78. package/dist/browser/socket-io-peer.d.ts.map +1 -0
  79. package/dist/browser/socket-io-trace.d.ts +31 -0
  80. package/dist/browser/socket-io-trace.d.ts.map +1 -0
  81. package/dist/browser/socket-io.d.ts +2 -18
  82. package/dist/browser/socket-io.d.ts.map +1 -1
  83. package/dist/contract/client-types.d.ts +134 -0
  84. package/dist/contract/client-types.d.ts.map +1 -0
  85. package/dist/contract/define-validation.d.ts +30 -0
  86. package/dist/contract/define-validation.d.ts.map +1 -0
  87. package/dist/contract/define.d.ts +3 -357
  88. package/dist/contract/define.d.ts.map +1 -1
  89. package/dist/contract/runtime-context.d.ts +131 -0
  90. package/dist/contract/runtime-context.d.ts.map +1 -0
  91. package/dist/contract/tool-options.d.ts +105 -0
  92. package/dist/contract/tool-options.d.ts.map +1 -0
  93. package/dist/contract/tool-view.d.ts +15 -2
  94. package/dist/contract/tool-view.d.ts.map +1 -1
  95. package/dist/durability/contract.d.ts +83 -0
  96. package/dist/durability/contract.d.ts.map +1 -1
  97. package/dist/durability/effect.d.ts +24 -0
  98. package/dist/durability/effect.d.ts.map +1 -0
  99. package/dist/durability/engine.d.ts +2 -2
  100. package/dist/durability/engine.d.ts.map +1 -1
  101. package/dist/durability/events.d.ts +2 -0
  102. package/dist/durability/events.d.ts.map +1 -1
  103. package/dist/durability/ledger.d.ts +11 -0
  104. package/dist/durability/ledger.d.ts.map +1 -1
  105. package/dist/durability/parks.d.ts +22 -0
  106. package/dist/durability/parks.d.ts.map +1 -0
  107. package/dist/durability/scheduler.d.ts +1 -0
  108. package/dist/durability/scheduler.d.ts.map +1 -1
  109. package/dist/entrypoints/agent-runtime/coding-tools.js +4 -4
  110. package/dist/entrypoints/agent-runtime/harness.js +127 -116
  111. package/dist/entrypoints/agent-runtime/sandbox.js +4 -4
  112. package/dist/entrypoints/agent-runtime/sqlite/bun.js +3 -3
  113. package/dist/entrypoints/agent-runtime/sqlite/node.js +3 -3
  114. package/dist/entrypoints/agent-runtime/testing.js +246 -214
  115. package/dist/entrypoints/agent-runtime.js +224 -203
  116. package/dist/entrypoints/application/diagnostic-journal.js +63 -203
  117. package/dist/entrypoints/application/opentelemetry.js +1 -1
  118. package/dist/entrypoints/application.d.ts +2 -1
  119. package/dist/entrypoints/application.d.ts.map +1 -1
  120. package/dist/entrypoints/application.js +506 -380
  121. package/dist/entrypoints/cli.d.ts +4 -1
  122. package/dist/entrypoints/cli.d.ts.map +1 -1
  123. package/dist/entrypoints/cli.js +57 -35
  124. package/dist/entrypoints/contract.d.ts +5 -2
  125. package/dist/entrypoints/contract.d.ts.map +1 -1
  126. package/dist/entrypoints/contract.js +3 -2
  127. package/dist/entrypoints/files.d.ts +2 -0
  128. package/dist/entrypoints/files.d.ts.map +1 -1
  129. package/dist/entrypoints/files.js +98 -2
  130. package/dist/entrypoints/index.d.ts +4 -2
  131. package/dist/entrypoints/index.d.ts.map +1 -1
  132. package/dist/entrypoints/index.js +348 -282
  133. package/dist/entrypoints/live.js +89 -71
  134. package/dist/entrypoints/node.d.ts +2 -1
  135. package/dist/entrypoints/node.d.ts.map +1 -1
  136. package/dist/entrypoints/node.js +13 -12
  137. package/dist/entrypoints/react.js +1 -1
  138. package/dist/entrypoints/remote.js +4 -4
  139. package/dist/entrypoints/server.d.ts +4 -2
  140. package/dist/entrypoints/server.d.ts.map +1 -1
  141. package/dist/entrypoints/server.js +293 -79
  142. package/dist/entrypoints/testing.d.ts +2 -1
  143. package/dist/entrypoints/testing.d.ts.map +1 -1
  144. package/dist/entrypoints/testing.js +288 -281
  145. package/dist/entrypoints/tools/connections.js +2 -2
  146. package/dist/entrypoints/tools/invoker.js +4 -4
  147. package/dist/entrypoints/tools.d.ts +6 -3
  148. package/dist/entrypoints/tools.d.ts.map +1 -1
  149. package/dist/entrypoints/tools.js +245 -202
  150. package/dist/entrypoints/tracking.js +325 -274
  151. package/dist/files/boundary.d.ts.map +1 -1
  152. package/dist/files/file-io.d.ts +14 -0
  153. package/dist/files/file-io.d.ts.map +1 -0
  154. package/dist/index-1j8pm5t0.js +412 -0
  155. package/dist/{index-kvj7f4hq.js → index-2bxbyab0.js} +10 -2
  156. package/dist/{index-xnj9bgxe.js → index-2gxzjvyr.js} +2 -162
  157. package/dist/index-4gcnakhx.js +82 -0
  158. package/dist/{index-j05z5gn4.js → index-55hkhm67.js} +136 -119
  159. package/dist/{index-cq4qfycf.js → index-79z7m7dt.js} +227 -59
  160. package/dist/{index-r7nmqpsg.js → index-7p1f0vp2.js} +1 -1
  161. package/dist/{index-dcpdvjsc.js → index-863ea9jb.js} +4 -4
  162. package/dist/index-8p52kd5p.js +242 -0
  163. package/dist/{index-97ac4gph.js → index-8zja4pkf.js} +26 -4
  164. package/dist/{index-2qzskm8h.js → index-dce5wxcq.js} +1 -1
  165. package/dist/index-e01r054k.js +105 -0
  166. package/dist/{index-sees8yf8.js → index-ensd0r74.js} +118 -111
  167. package/dist/{index-1nd48tm9.js → index-gxrt5h6v.js} +2 -1
  168. package/dist/{index-e87kakm4.js → index-ham5sz8m.js} +112 -110
  169. package/dist/{index-kj2wbeta.js → index-het2he5g.js} +280 -196
  170. package/dist/{index-vcvkq30w.js → index-hmp4800j.js} +3 -3
  171. package/dist/{index-gyr48nzg.js → index-k30h66yd.js} +71 -151
  172. package/dist/{index-2cr1cqhn.js → index-khtgb21r.js} +2 -2
  173. package/dist/{index-x4799c19.js → index-ndddxtyx.js} +11 -11
  174. package/dist/{index-dydjcp8z.js → index-np4m8hd3.js} +38 -36
  175. package/dist/{index-p81267c2.js → index-p3vsv1fb.js} +621 -357
  176. package/dist/{index-mp5sn43f.js → index-p9e0w8hs.js} +2 -2
  177. package/dist/{index-rewkc2hj.js → index-p9sh0xx4.js} +3 -3
  178. package/dist/{index-70yj2ebz.js → index-paz6sgdp.js} +107 -103
  179. package/dist/{index-bn2cjajt.js → index-px2gm2jp.js} +26 -102
  180. package/dist/index-s812ycat.js +65 -0
  181. package/dist/{index-xsqjkha9.js → index-sp1tzwnb.js} +2 -2
  182. package/dist/{index-n6hxg8as.js → index-t9rhjr9f.js} +615 -566
  183. package/dist/{index-zeemqhqx.js → index-v260df89.js} +88 -80
  184. package/dist/{index-fdp6hdxn.js → index-w906953s.js} +5 -1
  185. package/dist/{index-mdzh02yc.js → index-wdkt63w3.js} +1 -1
  186. package/dist/{index-58k4fp34.js → index-wsm5w4ee.js} +1 -1
  187. package/dist/{index-v38qyerb.js → index-xw3s75nj.js} +1 -1
  188. package/dist/internal/atomic-file.d.ts +23 -2
  189. package/dist/internal/atomic-file.d.ts.map +1 -1
  190. package/dist/internal/exclusive-lock.d.ts +51 -0
  191. package/dist/internal/exclusive-lock.d.ts.map +1 -0
  192. package/dist/internal/process-identity.d.ts +47 -0
  193. package/dist/internal/process-identity.d.ts.map +1 -0
  194. package/dist/internal/with-exclusive-lock.d.ts +67 -0
  195. package/dist/internal/with-exclusive-lock.d.ts.map +1 -0
  196. package/dist/live/watch-client.d.ts.map +1 -1
  197. package/dist/observability/audit.d.ts +2 -2
  198. package/dist/observability/audit.d.ts.map +1 -1
  199. package/dist/observability/event.d.ts +2 -1
  200. package/dist/observability/event.d.ts.map +1 -1
  201. package/dist/primitives/export-operation.d.ts +1 -1
  202. package/dist/primitives/export-operation.d.ts.map +1 -1
  203. package/dist/realtime/registry.d.ts +0 -5
  204. package/dist/realtime/registry.d.ts.map +1 -1
  205. package/dist/server/bun.d.ts +0 -1
  206. package/dist/server/bun.d.ts.map +1 -1
  207. package/dist/server/context-contribution.d.ts +1 -1
  208. package/dist/server/context-contribution.d.ts.map +1 -1
  209. package/dist/server/context.d.ts +1 -1
  210. package/dist/server/context.d.ts.map +1 -1
  211. package/dist/server/endpoint-response.d.ts +2 -1
  212. package/dist/server/endpoint-response.d.ts.map +1 -1
  213. package/dist/server/error-dispatch.d.ts +1 -1
  214. package/dist/server/error-dispatch.d.ts.map +1 -1
  215. package/dist/server/error-hook.d.ts +1 -1
  216. package/dist/server/error-hook.d.ts.map +1 -1
  217. package/dist/server/handler-state.d.ts +1 -1
  218. package/dist/server/handler-state.d.ts.map +1 -1
  219. package/dist/server/implement-registry.d.ts +54 -0
  220. package/dist/server/implement-registry.d.ts.map +1 -0
  221. package/dist/server/implement.d.ts +9 -55
  222. package/dist/server/implement.d.ts.map +1 -1
  223. package/dist/server/middleware/auth.d.ts +1 -31
  224. package/dist/server/middleware/auth.d.ts.map +1 -1
  225. package/dist/server/middleware/jwt.d.ts +31 -0
  226. package/dist/server/middleware/jwt.d.ts.map +1 -0
  227. package/dist/server/multipart-reader.d.ts +22 -0
  228. package/dist/server/multipart-reader.d.ts.map +1 -0
  229. package/dist/server/multipart.d.ts +1 -1
  230. package/dist/server/multipart.d.ts.map +1 -1
  231. package/dist/server/oauth/cimd.d.ts +88 -0
  232. package/dist/server/oauth/cimd.d.ts.map +1 -0
  233. package/dist/server/oauth/endpoints.d.ts +39 -0
  234. package/dist/server/oauth/endpoints.d.ts.map +1 -0
  235. package/dist/server/oauth/provider.d.ts +1 -68
  236. package/dist/server/oauth/provider.d.ts.map +1 -1
  237. package/dist/server/streaming-pump.d.ts +66 -0
  238. package/dist/server/streaming-pump.d.ts.map +1 -0
  239. package/dist/server/streaming-route.d.ts +12 -5
  240. package/dist/server/streaming-route.d.ts.map +1 -1
  241. package/dist/server/types.d.ts +4 -1
  242. package/dist/server/types.d.ts.map +1 -1
  243. package/dist/server/unix-client-bun.d.ts.map +1 -1
  244. package/dist/server/unix-client-http1.d.ts +76 -0
  245. package/dist/server/unix-client-http1.d.ts.map +1 -0
  246. package/dist/server/unix-client.d.ts.map +1 -1
  247. package/dist/testing/handler-clients.d.ts +2 -1
  248. package/dist/testing/handler-clients.d.ts.map +1 -1
  249. package/dist/testing/realtime-probe.d.ts +33 -0
  250. package/dist/testing/realtime-probe.d.ts.map +1 -0
  251. package/dist/testing/surface-conformance.d.ts +0 -31
  252. package/dist/testing/surface-conformance.d.ts.map +1 -1
  253. package/dist/testing/surface-manifest-digest.d.ts +44 -0
  254. package/dist/testing/surface-manifest-digest.d.ts.map +1 -0
  255. package/dist/testing/surface-manifest.d.ts.map +1 -1
  256. package/dist/tools/agent.d.ts +1 -1
  257. package/dist/tools/agent.d.ts.map +1 -1
  258. package/dist/tools/cli/args-fields.d.ts +31 -0
  259. package/dist/tools/cli/args-fields.d.ts.map +1 -0
  260. package/dist/tools/cli/args-route.d.ts +42 -0
  261. package/dist/tools/cli/args-route.d.ts.map +1 -0
  262. package/dist/tools/cli/args-view.d.ts +32 -0
  263. package/dist/tools/cli/args-view.d.ts.map +1 -0
  264. package/dist/tools/cli/args.d.ts +2 -65
  265. package/dist/tools/cli/args.d.ts.map +1 -1
  266. package/dist/tools/cli/argument-error.d.ts +5 -0
  267. package/dist/tools/cli/argument-error.d.ts.map +1 -0
  268. package/dist/tools/cli/checkpoint.d.ts.map +1 -1
  269. package/dist/tools/cli/command.d.ts +1 -1
  270. package/dist/tools/cli/command.d.ts.map +1 -1
  271. package/dist/tools/cli/config.d.ts +1 -1
  272. package/dist/tools/cli/config.d.ts.map +1 -1
  273. package/dist/tools/cli/format.d.ts +2 -1
  274. package/dist/tools/cli/format.d.ts.map +1 -1
  275. package/dist/tools/cli/invoke.d.ts +2 -1
  276. package/dist/tools/cli/invoke.d.ts.map +1 -1
  277. package/dist/tools/cli/managed-surface.d.ts +1 -1
  278. package/dist/tools/cli/managed-surface.d.ts.map +1 -1
  279. package/dist/tools/cli/policy.d.ts +1 -1
  280. package/dist/tools/cli/policy.d.ts.map +1 -1
  281. package/dist/tools/cli/prepare.d.ts.map +1 -1
  282. package/dist/tools/cli/run-managed.d.ts.map +1 -1
  283. package/dist/tools/cli/session.d.ts +1 -1
  284. package/dist/tools/cli/session.d.ts.map +1 -1
  285. package/dist/tools/cli/update.d.ts +19 -0
  286. package/dist/tools/cli/update.d.ts.map +1 -1
  287. package/dist/tools/cli/view.d.ts +1 -1
  288. package/dist/tools/cli/view.d.ts.map +1 -1
  289. package/dist/tools/cli/wait.d.ts +1 -1
  290. package/dist/tools/cli/wait.d.ts.map +1 -1
  291. package/dist/tools/connections/runtime.d.ts +1 -1
  292. package/dist/tools/connections/runtime.d.ts.map +1 -1
  293. package/dist/tools/execute-args.d.ts +23 -0
  294. package/dist/tools/execute-args.d.ts.map +1 -0
  295. package/dist/tools/execute-hooks.d.ts +146 -0
  296. package/dist/tools/execute-hooks.d.ts.map +1 -0
  297. package/dist/tools/execute-result.d.ts +40 -0
  298. package/dist/tools/execute-result.d.ts.map +1 -0
  299. package/dist/tools/execute.d.ts +4 -193
  300. package/dist/tools/execute.d.ts.map +1 -1
  301. package/dist/tools/internal/surface-projector.d.ts +2 -1
  302. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  303. package/dist/tools/invoker.d.ts +2 -1
  304. package/dist/tools/invoker.d.ts.map +1 -1
  305. package/dist/tools/lifecycle.d.ts +1 -1
  306. package/dist/tools/lifecycle.d.ts.map +1 -1
  307. package/dist/tools/mcp/mount.d.ts +1 -1
  308. package/dist/tools/mcp/mount.d.ts.map +1 -1
  309. package/dist/tools/mcp/prepare.d.ts +2 -1
  310. package/dist/tools/mcp/prepare.d.ts.map +1 -1
  311. package/dist/tools/mcp/progress.d.ts +1 -1
  312. package/dist/tools/mcp/progress.d.ts.map +1 -1
  313. package/dist/tools/mcp/register.d.ts +3 -2
  314. package/dist/tools/mcp/register.d.ts.map +1 -1
  315. package/dist/tools/mcp/round-context.d.ts +38 -0
  316. package/dist/tools/mcp/round-context.d.ts.map +1 -0
  317. package/dist/tools/mcp/round-policy.d.ts +1 -1
  318. package/dist/tools/mcp/round-policy.d.ts.map +1 -1
  319. package/dist/tools/mcp/round.d.ts +3 -11
  320. package/dist/tools/mcp/round.d.ts.map +1 -1
  321. package/dist/tools/mcp/runtime-tools.d.ts +2 -1
  322. package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
  323. package/dist/tools/mount.d.ts +3 -1
  324. package/dist/tools/mount.d.ts.map +1 -1
  325. package/dist/tools/native-definition.d.ts +2 -1
  326. package/dist/tools/native-definition.d.ts.map +1 -1
  327. package/dist/tools/operations/async-operation-runtime-tools.d.ts +2 -1
  328. package/dist/tools/operations/async-operation-runtime-tools.d.ts.map +1 -1
  329. package/dist/tools/operations/async-operation-runtime-types.d.ts +2 -1
  330. package/dist/tools/operations/async-operation-runtime-types.d.ts.map +1 -1
  331. package/dist/tools/remote.d.ts.map +1 -1
  332. package/dist/tools/runtime-tool.d.ts +3 -1
  333. package/dist/tools/runtime-tool.d.ts.map +1 -1
  334. package/dist/tools/tool-logger.d.ts +1 -1
  335. package/dist/tools/tool-logger.d.ts.map +1 -1
  336. package/dist/tracking/client-listeners.d.ts +30 -0
  337. package/dist/tracking/client-listeners.d.ts.map +1 -0
  338. package/dist/tracking/client-visit.d.ts +61 -0
  339. package/dist/tracking/client-visit.d.ts.map +1 -0
  340. package/dist/tracking/client.d.ts +3 -2
  341. package/dist/tracking/client.d.ts.map +1 -1
  342. package/llms/application.2.txt +271 -7
  343. package/llms/application.txt +66 -289
  344. package/llms/cli.2.txt +80 -0
  345. package/llms/cli.txt +45 -74
  346. package/llms/contract.txt +3 -3
  347. package/llms/files.txt +3 -0
  348. package/llms/stitchkit.txt +3 -3
  349. package/llms/tools.2.txt +88 -279
  350. package/llms/tools.3.txt +288 -0
  351. package/llms/tools.txt +27 -65
  352. package/llms/{upgrading-0.37.0-0.50.0.txt → upgrading-0.37.0-0.53.0.txt} +25 -3
  353. package/llms/{upgrading-0.53.0-0.67.0.txt → upgrading-0.55.0-0.68.0.txt} +53 -25
  354. package/llms/{upgrading-0.68.0-0.94.0.txt → upgrading-0.69.0-0.95.0.txt} +45 -53
  355. package/llms/upgrading.txt +3 -3
  356. package/llms-full.txt +216 -43
  357. package/llms.txt +8 -8
  358. package/package.json +1 -1
  359. package/dist/index-e2z3ahw9.js +0 -325
  360. package/dist/index-mkm15s49.js +0 -394
@@ -13,7 +13,7 @@ import {
13
13
  createActivityProjection,
14
14
  createApplicationSnapshotSink,
15
15
  createManagedSchedule
16
- } from "../index-sees8yf8.js";
16
+ } from "../index-ensd0r74.js";
17
17
  import {
18
18
  DiagnosticJournalCloseResultSchema,
19
19
  DiagnosticJournalFailurePhaseSchema,
@@ -36,7 +36,7 @@ import {
36
36
  ApplicationShutdownBudgetSchema,
37
37
  ApplicationShutdownOptionsSchema,
38
38
  createApplication
39
- } from "../index-fdp6hdxn.js";
39
+ } from "../index-w906953s.js";
40
40
  import {
41
41
  ApplicationAdmissionSnapshotSchema,
42
42
  ApplicationHealthSchema,
@@ -69,8 +69,8 @@ import {
69
69
  deltaWins,
70
70
  diff,
71
71
  watchKeyString
72
- } from "../index-mdzh02yc.js";
73
- import"../index-mkm15s49.js";
72
+ } from "../index-wdkt63w3.js";
73
+ import"../index-1j8pm5t0.js";
74
74
  import {
75
75
  BoundedChannelPolicySchema,
76
76
  BoundedChannelReaderError,
@@ -94,6 +94,85 @@ import"../index-kzfs85xp.js";
94
94
  import"../index-hffjpmq9.js";
95
95
  // src/application/admission.ts
96
96
  import { z } from "zod";
97
+
98
+ // src/application/admission-rate.ts
99
+ function pruneRate(samples, intervalMs, at) {
100
+ let expired = 0;
101
+ while (expired < samples.length && at - (samples[expired]?.at ?? at) >= intervalMs) {
102
+ expired += 1;
103
+ }
104
+ if (expired > 0)
105
+ samples.splice(0, expired);
106
+ }
107
+ function pruneExpired(policy, globalRate, keys, at) {
108
+ if (policy.global.rate)
109
+ pruneRate(globalRate, policy.global.rate.intervalMs, at);
110
+ for (const [key, record] of keys) {
111
+ if (record.limits?.rate)
112
+ pruneRate(record.rate, record.limits.rate.intervalMs, at);
113
+ if (record.active === 0 && record.rate.length === 0)
114
+ keys.delete(key);
115
+ }
116
+ }
117
+ function retryAfter(samples, budget, at) {
118
+ return Math.max(1, Math.ceil(budget.intervalMs - (at - (samples[0]?.at ?? at))));
119
+ }
120
+ function removeSample(samples, sample) {
121
+ if (!sample)
122
+ return;
123
+ const index = samples.indexOf(sample);
124
+ if (index >= 0)
125
+ samples.splice(index, 1);
126
+ }
127
+
128
+ // src/application/admission-run.ts
129
+ async function runAdmitted(acquire, key, work, options) {
130
+ if (options.timeoutMs !== undefined) {
131
+ if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 0) {
132
+ throw new TypeError("run timeoutMs must be a non-negative safe integer");
133
+ }
134
+ }
135
+ if (options.signal?.aborted)
136
+ throw new BoundedOperationWaitError("cancelled");
137
+ const admission = acquire(key);
138
+ if (admission.outcome === "refused") {
139
+ throw new BoundedAdmissionRefusalError(admission.reason, admission.retryAfterMs);
140
+ }
141
+ const workAbort = new AbortController;
142
+ const abortWork = () => workAbort.abort(options.signal?.reason);
143
+ options.signal?.addEventListener("abort", abortWork, { once: true });
144
+ let timer;
145
+ let timedOut = false;
146
+ if (options.timeoutMs !== undefined) {
147
+ timer = setTimeout(() => {
148
+ timedOut = true;
149
+ workAbort.abort(new DOMException("Operation wait timed out", "TimeoutError"));
150
+ }, options.timeoutMs);
151
+ }
152
+ const outcome = Promise.resolve().then(() => work({ signal: workAbort.signal })).then((value) => ({ kind: "value", value }), (error) => ({ kind: "error", error })).finally(() => admission.lease.release());
153
+ const callerDone = new Promise((resolve) => {
154
+ const settle = () => resolve({ kind: "caller" });
155
+ if (workAbort.signal.aborted)
156
+ settle();
157
+ else
158
+ workAbort.signal.addEventListener("abort", settle, { once: true });
159
+ });
160
+ try {
161
+ const settled = await Promise.race([outcome, callerDone]);
162
+ if (settled.kind === "caller") {
163
+ throw new BoundedOperationWaitError(timedOut ? "timed-out" : "cancelled");
164
+ }
165
+ if (settled.kind === "error")
166
+ throw settled.error;
167
+ return settled.value;
168
+ } finally {
169
+ if (timer !== undefined)
170
+ clearTimeout(timer);
171
+ options.signal?.removeEventListener("abort", abortWork);
172
+ }
173
+ }
174
+
175
+ // src/application/admission.ts
97
176
  var PositiveSafeIntegerSchema = z.number().int().positive().safe();
98
177
  var BoundedRateBudgetSchema = z.object({
99
178
  limit: PositiveSafeIntegerSchema,
@@ -176,22 +255,59 @@ class BoundedOperationWaitError extends Error {
176
255
  this.name = "BoundedOperationWaitError";
177
256
  }
178
257
  }
258
+ var NO_REFUSALS = {
259
+ "not-accepting": 0,
260
+ "global-concurrency": 0,
261
+ "key-required": 0,
262
+ "key-capacity": 0,
263
+ "key-concurrency": 0,
264
+ "global-rate": 0,
265
+ "key-rate": 0,
266
+ upstream: 0
267
+ };
268
+ async function waitForDrain(drainWaiters, options) {
269
+ const controller = new AbortController;
270
+ let timer;
271
+ const abort = () => controller.abort();
272
+ if (options.signal?.aborted)
273
+ abort();
274
+ else
275
+ options.signal?.addEventListener("abort", abort, { once: true });
276
+ if (options.timeoutMs !== undefined) {
277
+ if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 0) {
278
+ throw new TypeError("drain timeoutMs must be a non-negative safe integer");
279
+ }
280
+ timer = setTimeout(abort, options.timeoutMs);
281
+ }
282
+ let resolveDrained;
283
+ try {
284
+ await Promise.race([
285
+ new Promise((resolve) => {
286
+ resolveDrained = resolve;
287
+ drainWaiters.add(resolve);
288
+ }),
289
+ new Promise((resolve) => {
290
+ if (controller.signal.aborted)
291
+ resolve();
292
+ else
293
+ controller.signal.addEventListener("abort", () => resolve(), { once: true });
294
+ })
295
+ ]);
296
+ } finally {
297
+ if (timer !== undefined)
298
+ clearTimeout(timer);
299
+ options.signal?.removeEventListener("abort", abort);
300
+ if (resolveDrained)
301
+ drainWaiters.delete(resolveDrained);
302
+ }
303
+ }
179
304
  function createBoundedAdmission(config) {
180
305
  const policy = BoundedAdmissionPolicySchema.parse(config.policy);
181
306
  const clock = config.clock ?? { now: () => performance.now() };
182
307
  const keys = new Map;
183
308
  const globalRate = [];
184
309
  const drainWaiters = new Set;
185
- const refusals = {
186
- "not-accepting": 0,
187
- "global-concurrency": 0,
188
- "key-required": 0,
189
- "key-capacity": 0,
190
- "key-concurrency": 0,
191
- "global-rate": 0,
192
- "key-rate": 0,
193
- upstream: 0
194
- };
310
+ const refusals = { ...NO_REFUSALS };
195
311
  let state = "accepting";
196
312
  let active = 0;
197
313
  let accepted = 0;
@@ -219,26 +335,7 @@ function createBoundedAdmission(config) {
219
335
  ...perKey.rate && { rate: perKey.rate }
220
336
  });
221
337
  };
222
- const pruneRate = (samples, intervalMs, at) => {
223
- let expired = 0;
224
- while (expired < samples.length && at - (samples[expired]?.at ?? at) >= intervalMs) {
225
- expired += 1;
226
- }
227
- if (expired > 0)
228
- samples.splice(0, expired);
229
- };
230
- const prune = (at) => {
231
- if (policy.global.rate)
232
- pruneRate(globalRate, policy.global.rate.intervalMs, at);
233
- for (const [key, record] of keys) {
234
- if (record.limits?.rate) {
235
- pruneRate(record.rate, record.limits.rate.intervalMs, at);
236
- }
237
- if (record.active === 0 && record.rate.length === 0)
238
- keys.delete(key);
239
- }
240
- };
241
- const retryAfter = (samples, budget, at) => Math.max(1, Math.ceil(budget.intervalMs - (at - (samples[0]?.at ?? at))));
338
+ const prune = (at) => pruneExpired(policy, globalRate, keys, at);
242
339
  const refuse = (reason, retryAfterMs) => {
243
340
  refused += 1;
244
341
  refusals[reason] += 1;
@@ -260,16 +357,9 @@ function createBoundedAdmission(config) {
260
357
  active -= 1;
261
358
  if (keyRecord)
262
359
  keyRecord.active -= 1;
263
- if (globalSample) {
264
- const index = globalRate.indexOf(globalSample);
265
- if (index >= 0)
266
- globalRate.splice(index, 1);
267
- }
268
- if (keySample && keyRecord) {
269
- const index = keyRecord.rate.indexOf(keySample);
270
- if (index >= 0)
271
- keyRecord.rate.splice(index, 1);
272
- }
360
+ removeSample(globalRate, globalSample);
361
+ if (keyRecord)
362
+ removeSample(keyRecord.rate, keySample);
273
363
  if (key !== undefined && keyRecord?.active === 0 && keyRecord.rate.length === 0) {
274
364
  keys.delete(key);
275
365
  }
@@ -368,87 +458,10 @@ function createBoundedAdmission(config) {
368
458
  stopAdmission();
369
459
  if (active === 0)
370
460
  return { drained: true, remaining: 0 };
371
- const controller = new AbortController;
372
- let timer;
373
- const abort = () => controller.abort();
374
- if (options.signal?.aborted)
375
- abort();
376
- else
377
- options.signal?.addEventListener("abort", abort, { once: true });
378
- if (options.timeoutMs !== undefined) {
379
- if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 0) {
380
- throw new TypeError("drain timeoutMs must be a non-negative safe integer");
381
- }
382
- timer = setTimeout(abort, options.timeoutMs);
383
- }
384
- let resolveDrained;
385
- try {
386
- await Promise.race([
387
- new Promise((resolve) => {
388
- resolveDrained = resolve;
389
- drainWaiters.add(resolve);
390
- }),
391
- new Promise((resolve) => {
392
- if (controller.signal.aborted)
393
- resolve();
394
- else
395
- controller.signal.addEventListener("abort", () => resolve(), { once: true });
396
- })
397
- ]);
398
- } finally {
399
- if (timer !== undefined)
400
- clearTimeout(timer);
401
- options.signal?.removeEventListener("abort", abort);
402
- if (resolveDrained)
403
- drainWaiters.delete(resolveDrained);
404
- }
461
+ await waitForDrain(drainWaiters, options);
405
462
  return { drained: active === 0, remaining: active };
406
463
  };
407
- const run = async (key, work, options = {}) => {
408
- if (options.timeoutMs !== undefined) {
409
- if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 0) {
410
- throw new TypeError("run timeoutMs must be a non-negative safe integer");
411
- }
412
- }
413
- if (options.signal?.aborted)
414
- throw new BoundedOperationWaitError("cancelled");
415
- const admission = acquire(key);
416
- if (admission.outcome === "refused") {
417
- throw new BoundedAdmissionRefusalError(admission.reason, admission.retryAfterMs);
418
- }
419
- const workAbort = new AbortController;
420
- const abortWork = () => workAbort.abort(options.signal?.reason);
421
- options.signal?.addEventListener("abort", abortWork, { once: true });
422
- let timer;
423
- let timedOut = false;
424
- if (options.timeoutMs !== undefined) {
425
- timer = setTimeout(() => {
426
- timedOut = true;
427
- workAbort.abort(new DOMException("Operation wait timed out", "TimeoutError"));
428
- }, options.timeoutMs);
429
- }
430
- const outcome = Promise.resolve().then(() => work({ signal: workAbort.signal })).then((value) => ({ kind: "value", value }), (error) => ({ kind: "error", error })).finally(() => admission.lease.release());
431
- const callerDone = new Promise((resolve) => {
432
- const settle = () => resolve({ kind: "caller" });
433
- if (workAbort.signal.aborted)
434
- settle();
435
- else
436
- workAbort.signal.addEventListener("abort", settle, { once: true });
437
- });
438
- try {
439
- const settled = await Promise.race([outcome, callerDone]);
440
- if (settled.kind === "caller") {
441
- throw new BoundedOperationWaitError(timedOut ? "timed-out" : "cancelled");
442
- }
443
- if (settled.kind === "error")
444
- throw settled.error;
445
- return settled.value;
446
- } finally {
447
- if (timer !== undefined)
448
- clearTimeout(timer);
449
- options.signal?.removeEventListener("abort", abortWork);
450
- }
451
- };
464
+ const run = (key, work, options = {}) => runAdmitted(acquire, key, work, options);
452
465
  return {
453
466
  acquire,
454
467
  run,
@@ -815,37 +828,67 @@ var emptyOutboxState = () => ({
815
828
  });
816
829
  var sizeOf = (value) => measureSize(value).responseBytes;
817
830
  var DEFAULT_OUTBOX_BACKOFF = { minDelayMs: 1000, maxDelayMs: 60000, jitter: 0 };
818
- function createNotificationOutbox(config) {
819
- const schema = notificationOutboxStateSchema(config.payloadSchema);
820
- const clock = config.clock ?? (() => new Date);
821
- const ownerId = config.ownerId ?? crypto.randomUUID();
831
+ function resolveOutboxLimits(config) {
822
832
  const pollIntervalMs = z5.number().int().min(10).parse(config.pollIntervalMs ?? 1000);
823
833
  const leaseMs = z5.number().int().min(100).parse(config.leaseMs ?? 30000);
824
834
  const maxAttempts = z5.number().int().min(1).max(1e4).parse(config.maxAttempts ?? 100);
825
835
  const maxQueue = z5.number().int().min(1).max(1e5).parse(config.maxQueue ?? 1000);
826
836
  const maxStateBytes = z5.number().int().min(1024).parse(config.maxStateBytes ?? 1024 * 1024);
827
837
  const retainReceipts = z5.number().int().min(0).max(1e5).parse(config.retainReceipts ?? 1000);
838
+ return { pollIntervalMs, leaseMs, maxAttempts, maxQueue, maxStateBytes, retainReceipts };
839
+ }
840
+ function boundedOutboxState(parsed, maxQueue, retainReceipts, maxStateBytes) {
841
+ if (parsed.queue.length > maxQueue) {
842
+ throw new Error(`[stitchkit] notification outbox queue limit (${maxQueue}) exceeded`);
843
+ }
844
+ let receipts = parsed.receipts.slice(0, retainReceipts);
845
+ let next = { ...parsed, receipts };
846
+ while (sizeOf(next) > maxStateBytes && receipts.length > 0) {
847
+ receipts = receipts.slice(0, -1);
848
+ next = { ...parsed, receipts };
849
+ }
850
+ if (sizeOf(next) > maxStateBytes) {
851
+ throw new Error(`[stitchkit] notification outbox state limit (${maxStateBytes} bytes) exceeded`);
852
+ }
853
+ return next;
854
+ }
855
+ function dropReason(classification, attempts, maxAttempts) {
856
+ if (classification.recipientUnreachable === true)
857
+ return "recipient-unreachable";
858
+ if (!classification.retryable)
859
+ return "terminal";
860
+ return attempts >= maxAttempts ? "attempt-limit" : null;
861
+ }
862
+ var RetryDelaySchema = z5.number().nonnegative().max(24 * 60 * 60 * 1000);
863
+ var NO_LEASE = { leaseOwner: null, leaseId: null, leaseUntil: null };
864
+ function claimDue(queue, now, ownerId, leaseMs) {
865
+ const candidate = [...queue].sort((left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt)).find((item) => Date.parse(item.nextAttemptAt) <= now.getTime() && (item.leaseUntil === null || Date.parse(item.leaseUntil) <= now.getTime()));
866
+ if (!candidate)
867
+ return null;
868
+ return {
869
+ ...candidate,
870
+ attempts: candidate.attempts + 1,
871
+ leaseOwner: ownerId,
872
+ leaseId: crypto.randomUUID(),
873
+ leaseUntil: new Date(now.getTime() + leaseMs).toISOString()
874
+ };
875
+ }
876
+ async function reportOutboxError(config, error) {
877
+ try {
878
+ await config.onError?.(error);
879
+ } catch {}
880
+ }
881
+ function createNotificationOutbox(config) {
882
+ const schema = notificationOutboxStateSchema(config.payloadSchema);
883
+ const clock = config.clock ?? (() => new Date);
884
+ const ownerId = config.ownerId ?? crypto.randomUUID();
885
+ const { pollIntervalMs, leaseMs, maxAttempts, maxQueue, maxStateBytes, retainReceipts } = resolveOutboxLimits(config);
828
886
  const backoffMs = config.backoffMs ?? ((attempt) => backoffDelay(DEFAULT_OUTBOX_BACKOFF, attempt));
829
887
  let timer;
830
888
  let running = false;
831
889
  let interrupted = false;
832
890
  let flushTail = Promise.resolve();
833
- const bounded = (input) => {
834
- const parsed = schema.parse(input);
835
- if (parsed.queue.length > maxQueue) {
836
- throw new Error(`[stitchkit] notification outbox queue limit (${maxQueue}) exceeded`);
837
- }
838
- let receipts = parsed.receipts.slice(0, retainReceipts);
839
- let next = { ...parsed, receipts };
840
- while (sizeOf(next) > maxStateBytes && receipts.length > 0) {
841
- receipts = receipts.slice(0, -1);
842
- next = { ...parsed, receipts };
843
- }
844
- if (sizeOf(next) > maxStateBytes) {
845
- throw new Error(`[stitchkit] notification outbox state limit (${maxStateBytes} bytes) exceeded`);
846
- }
847
- return next;
848
- };
891
+ const bounded = (input) => boundedOutboxState(schema.parse(input), maxQueue, retainReceipts, maxStateBytes);
849
892
  const parse = (state) => schema.parse(state ?? emptyOutboxState());
850
893
  const enqueue = async (input) => {
851
894
  const key = z5.string().min(1).max(512).parse(input.key);
@@ -857,18 +900,10 @@ function createNotificationOutbox(config) {
857
900
  return { state, result: false };
858
901
  }
859
902
  const now = clock().toISOString();
903
+ const fresh = { key, payload, createdAt: now, attempts: 0, nextAttemptAt: now };
860
904
  const queue = [
861
905
  ...state.queue.filter((item) => !supersedes.has(item.key)),
862
- {
863
- key,
864
- payload,
865
- createdAt: now,
866
- attempts: 0,
867
- nextAttemptAt: now,
868
- leaseOwner: null,
869
- leaseId: null,
870
- leaseUntil: null
871
- }
906
+ { ...fresh, ...NO_LEASE }
872
907
  ];
873
908
  const next = bounded({ ...state, queue });
874
909
  return { state: next, result: true };
@@ -878,21 +913,13 @@ function createNotificationOutbox(config) {
878
913
  const now = clock();
879
914
  return config.store.update((current) => {
880
915
  const state = parse(current);
881
- const candidate = [...state.queue].sort((left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt)).find((item) => Date.parse(item.nextAttemptAt) <= now.getTime() && (item.leaseUntil === null || Date.parse(item.leaseUntil) <= now.getTime()));
882
- if (!candidate)
916
+ const claimed = claimDue(state.queue, now, ownerId, leaseMs);
917
+ if (!claimed)
883
918
  return { state, result: null };
884
- const leaseId = crypto.randomUUID();
885
- const claimed = {
886
- ...candidate,
887
- attempts: candidate.attempts + 1,
888
- leaseOwner: ownerId,
889
- leaseId,
890
- leaseUntil: new Date(now.getTime() + leaseMs).toISOString()
891
- };
892
919
  return {
893
920
  state: bounded({
894
921
  ...state,
895
- queue: state.queue.map((item) => item.key === candidate.key ? claimed : item)
922
+ queue: state.queue.map((item) => item.key === claimed.key ? claimed : item)
896
923
  }),
897
924
  result: claimed
898
925
  };
@@ -926,7 +953,7 @@ function createNotificationOutbox(config) {
926
953
  if (!live)
927
954
  return { state, result: null };
928
955
  const attempts = live.attempts;
929
- const reason = classification.recipientUnreachable === true ? "recipient-unreachable" : !classification.retryable ? "terminal" : attempts >= maxAttempts ? "attempt-limit" : null;
956
+ const reason = dropReason(classification, attempts, maxAttempts);
930
957
  if (reason) {
931
958
  return {
932
959
  state: bounded({
@@ -936,15 +963,11 @@ function createNotificationOutbox(config) {
936
963
  result: { item: { ...live, attempts }, error, reason }
937
964
  };
938
965
  }
939
- const rawDelay = backoffMs(attempts);
940
- const wait = z5.number().finite().nonnegative().max(24 * 60 * 60 * 1000).parse(rawDelay);
966
+ const wait = RetryDelaySchema.parse(backoffMs(attempts));
941
967
  const retry = {
942
968
  ...live,
943
- attempts,
944
969
  nextAttemptAt: new Date(clock().getTime() + wait).toISOString(),
945
- leaseOwner: null,
946
- leaseId: null,
947
- leaseUntil: null
970
+ ...NO_LEASE
948
971
  };
949
972
  return {
950
973
  state: bounded({
@@ -990,9 +1013,7 @@ function createNotificationOutbox(config) {
990
1013
  try {
991
1014
  await flush();
992
1015
  } catch (error) {
993
- try {
994
- await config.onError?.(error);
995
- } catch {}
1016
+ await reportOutboxError(config, error);
996
1017
  } finally {
997
1018
  schedule();
998
1019
  }
@@ -1047,21 +1068,28 @@ function notificationOutboxResource(outbox, config = {}) {
1047
1068
  });
1048
1069
  }
1049
1070
  // src/application/process-lifecycle.ts
1071
+ import { z as z7 } from "zod";
1072
+
1073
+ // src/application/process-lifecycle-transitions.ts
1050
1074
  import { z as z6 } from "zod";
1051
1075
  var LifecycleTerminationSchema = z6.enum([
1052
1076
  "active",
1077
+ "draining",
1053
1078
  "clean",
1054
1079
  "forced",
1080
+ "startup-failed",
1055
1081
  "hot-reload",
1056
1082
  "abnormal"
1057
1083
  ]);
1084
+ var timestamp = z6.string().datetime({ offset: true });
1058
1085
  var LifecycleRunSchema = z6.object({
1059
1086
  runId: z6.string().min(1).max(128),
1060
1087
  pid: z6.number().int().positive(),
1061
1088
  version: z6.string().min(1).max(256),
1062
- startedAt: z6.string().datetime({ offset: true }),
1063
- readyAt: z6.string().datetime({ offset: true }).nullable(),
1064
- stoppedAt: z6.string().datetime({ offset: true }).nullable(),
1089
+ startedAt: timestamp,
1090
+ readyAt: timestamp.nullable(),
1091
+ unavailableAt: timestamp.nullable().default(null),
1092
+ stoppedAt: timestamp.nullable(),
1065
1093
  termination: LifecycleTerminationSchema
1066
1094
  }).strict();
1067
1095
  var LifecycleStateSchema = z6.object({
@@ -1073,17 +1101,20 @@ var PreviousExitSchema = z6.enum([
1073
1101
  "hot-reload",
1074
1102
  "clean",
1075
1103
  "forced",
1104
+ "startup-failed",
1076
1105
  "handoff",
1077
1106
  "abnormal"
1078
1107
  ]);
1079
1108
  var emptyState = () => ({ schemaVersion: 1, runs: [] });
1080
1109
  var epoch = (value) => Date.parse(value);
1081
1110
  var elapsed = (later, earlier) => Math.max(0, epoch(later) - epoch(earlier));
1082
- function normalize(state, retain) {
1111
+ var isOpenRun = (run) => run.termination === "active" || run.termination === "draining";
1112
+ function normalizeLifecycleState(state, retain) {
1083
1113
  const parsed = LifecycleStateSchema.parse(state ?? emptyState());
1084
1114
  const runs = [...parsed.runs];
1085
1115
  for (let index = runs.length - 1;runs.length > retain && index >= 0; index -= 1) {
1086
- if (runs[index]?.termination !== "active")
1116
+ const run = runs[index];
1117
+ if (run && !isOpenRun(run))
1087
1118
  runs.splice(index, 1);
1088
1119
  }
1089
1120
  return { schemaVersion: 1, runs: runs.slice(0, retain) };
@@ -1092,15 +1123,45 @@ var UNKNOWN_VERSION = "unknown";
1092
1123
  function isVersionChange(previous, next) {
1093
1124
  return previous !== UNKNOWN_VERSION && next !== UNKNOWN_VERSION && previous !== next;
1094
1125
  }
1126
+ var RetainSchema = z6.number().int().min(1).max(1000);
1127
+ function previousExitOf(previous, run, overlap) {
1128
+ if (!previous)
1129
+ return "first-boot";
1130
+ const ended = previous.termination;
1131
+ if (ended !== "active" && ended !== "draining")
1132
+ return ended;
1133
+ if (previous.pid === run.pid)
1134
+ return "hot-reload";
1135
+ if (isVersionChange(previous.version, run.version) || overlap === "handoff")
1136
+ return "handoff";
1137
+ return previous.readyAt === null ? "startup-failed" : "abnormal";
1138
+ }
1139
+ function closeAbandoned(candidate, previous, previousExit, run, overlap) {
1140
+ if (!isOpenRun(candidate))
1141
+ return candidate;
1142
+ const closed = { ...candidate, stoppedAt: run.startedAt };
1143
+ if (candidate.pid === run.pid)
1144
+ return { ...closed, termination: "hot-reload" };
1145
+ if (overlap === "handoff")
1146
+ return candidate;
1147
+ if (candidate.runId === previous?.runId && previousExit === "handoff")
1148
+ return candidate;
1149
+ return {
1150
+ ...closed,
1151
+ termination: candidate.readyAt === null ? "startup-failed" : "abnormal"
1152
+ };
1153
+ }
1095
1154
  function transitionProcessStart(source, input) {
1096
- const retain = z6.number().int().min(1).max(1000).parse(input.retain ?? 20);
1097
- const state = normalize(source, 1000);
1155
+ const retain = RetainSchema.parse(input.retain ?? 20);
1156
+ const overlap = input.sameVersionOverlap ?? "abnormal";
1157
+ const state = normalizeLifecycleState(source, 1000);
1098
1158
  const run = LifecycleRunSchema.parse({
1099
1159
  runId: input.runId,
1100
1160
  pid: input.pid,
1101
1161
  version: input.version,
1102
1162
  startedAt: input.now,
1103
1163
  readyAt: null,
1164
+ unavailableAt: null,
1104
1165
  stoppedAt: null,
1105
1166
  termination: "active"
1106
1167
  });
@@ -1108,31 +1169,10 @@ function transitionProcessStart(source, input) {
1108
1169
  throw new Error(`[stitchkit] lifecycle run id already exists: ${run.runId}`);
1109
1170
  }
1110
1171
  const previous = state.runs[0] ?? null;
1111
- let previousExit = "first-boot";
1112
- let downtimeMs = null;
1113
- if (previous?.stoppedAt) {
1114
- previousExit = previous.termination === "forced" || previous.termination === "abnormal" || previous.termination === "hot-reload" ? previous.termination : "clean";
1115
- downtimeMs = elapsed(run.startedAt, previous.stoppedAt);
1116
- } else if (previous) {
1117
- if (previous.pid === run.pid)
1118
- previousExit = "hot-reload";
1119
- else if (isVersionChange(previous.version, run.version))
1120
- previousExit = "handoff";
1121
- else
1122
- previousExit = input.sameVersionOverlap ?? "abnormal";
1123
- }
1124
- const runs = state.runs.map((candidate) => {
1125
- if (candidate.runId !== previous?.runId || candidate.termination !== "active")
1126
- return candidate;
1127
- if (previousExit === "hot-reload") {
1128
- return { ...candidate, stoppedAt: run.startedAt, termination: "hot-reload" };
1129
- }
1130
- if (previousExit === "abnormal") {
1131
- return { ...candidate, stoppedAt: run.startedAt, termination: "abnormal" };
1132
- }
1133
- return candidate;
1134
- });
1135
- const next = normalize({ schemaVersion: 1, runs: [run, ...runs] }, retain);
1172
+ const previousExit = previousExitOf(previous, run, overlap);
1173
+ const processGapMs = previous && !isOpenRun(previous) && previous.stoppedAt ? elapsed(run.startedAt, previous.stoppedAt) : null;
1174
+ const runs = state.runs.map((candidate) => closeAbandoned(candidate, previous, previousExit, run, overlap));
1175
+ const next = normalizeLifecycleState({ schemaVersion: 1, runs: [run, ...runs] }, retain);
1136
1176
  return {
1137
1177
  state: next,
1138
1178
  fact: {
@@ -1146,15 +1186,30 @@ function transitionProcessStart(source, input) {
1146
1186
  previousPid: previous?.pid ?? null,
1147
1187
  previousVersion: previous?.version ?? null,
1148
1188
  versionChanged: previous !== null && isVersionChange(previous.version, run.version),
1149
- downtimeMs
1189
+ processGapMs
1150
1190
  }
1151
1191
  };
1152
1192
  }
1193
+ function findOpen(state, input) {
1194
+ return state.runs.find((run) => run.runId === input.runId && run.pid === input.pid && isOpenRun(run));
1195
+ }
1196
+ function unavailability(state, target, readyAt) {
1197
+ const older = state.runs.slice(state.runs.indexOf(target) + 1);
1198
+ const served = older.find((run) => run.readyAt !== null);
1199
+ if (!served)
1200
+ return { downtimeMs: null, unavailableSince: null };
1201
+ if (served.unavailableAt !== null) {
1202
+ return {
1203
+ downtimeMs: elapsed(readyAt, served.unavailableAt),
1204
+ unavailableSince: served.unavailableAt
1205
+ };
1206
+ }
1207
+ return isOpenRun(served) ? { downtimeMs: 0, unavailableSince: null } : { downtimeMs: null, unavailableSince: null };
1208
+ }
1153
1209
  function transitionProcessReady(source, input) {
1154
- const retain = input.retain ?? 20;
1155
- const state = normalize(source, retain);
1156
- const target = state.runs.find((run) => run.runId === input.runId && run.pid === input.pid && run.termination === "active");
1157
- if (!target) {
1210
+ const state = normalizeLifecycleState(source, input.retain ?? 20);
1211
+ const target = findOpen(state, input);
1212
+ if (target?.termination !== "active") {
1158
1213
  return {
1159
1214
  state,
1160
1215
  fact: {
@@ -1163,6 +1218,8 @@ function transitionProcessReady(source, input) {
1163
1218
  pid: input.pid,
1164
1219
  readyAt: input.now,
1165
1220
  startupMs: 0,
1221
+ downtimeMs: null,
1222
+ unavailableSince: null,
1166
1223
  recorded: false
1167
1224
  }
1168
1225
  };
@@ -1179,14 +1236,45 @@ function transitionProcessReady(source, input) {
1179
1236
  pid: target.pid,
1180
1237
  readyAt,
1181
1238
  startupMs: elapsed(readyAt, target.startedAt),
1239
+ ...unavailability(state, target, readyAt),
1182
1240
  recorded: target.readyAt === null
1183
1241
  }
1184
1242
  };
1185
1243
  }
1244
+ function transitionProcessDraining(source, input) {
1245
+ const state = normalizeLifecycleState(source, input.retain ?? 20);
1246
+ const target = findOpen(state, input);
1247
+ if (target?.termination !== "active") {
1248
+ return {
1249
+ state,
1250
+ fact: {
1251
+ type: "draining",
1252
+ runId: input.runId,
1253
+ pid: input.pid,
1254
+ unavailableAt: target ? target.unavailableAt : null,
1255
+ recorded: false
1256
+ }
1257
+ };
1258
+ }
1259
+ const unavailableAt = target.readyAt === null ? null : input.now;
1260
+ return {
1261
+ state: {
1262
+ schemaVersion: 1,
1263
+ runs: state.runs.map((run) => run.runId === target.runId ? { ...run, termination: "draining", unavailableAt } : run)
1264
+ },
1265
+ fact: {
1266
+ type: "draining",
1267
+ runId: target.runId,
1268
+ pid: target.pid,
1269
+ unavailableAt,
1270
+ recorded: true
1271
+ }
1272
+ };
1273
+ }
1186
1274
  function transitionProcessShutdown(source, input) {
1187
- const state = normalize(source, input.retain ?? 20);
1188
- const target = state.runs.find((run) => run.runId === input.runId && run.pid === input.pid && run.termination === "active");
1189
- const termination = input.forced ? "forced" : "clean";
1275
+ const state = normalizeLifecycleState(source, input.retain ?? 20);
1276
+ const target = findOpen(state, input);
1277
+ const requested = input.forced ? "forced" : "clean";
1190
1278
  if (!target) {
1191
1279
  return {
1192
1280
  state,
@@ -1196,15 +1284,17 @@ function transitionProcessShutdown(source, input) {
1196
1284
  pid: input.pid,
1197
1285
  stoppedAt: input.now,
1198
1286
  uptimeMs: null,
1199
- termination,
1287
+ termination: requested,
1200
1288
  recorded: false
1201
1289
  }
1202
1290
  };
1203
1291
  }
1292
+ const termination = target.readyAt === null ? "startup-failed" : requested;
1293
+ const unavailableAt = target.unavailableAt ?? (target.readyAt === null ? null : input.now);
1204
1294
  return {
1205
1295
  state: {
1206
1296
  schemaVersion: 1,
1207
- runs: state.runs.map((run) => run.runId === target.runId ? { ...run, stoppedAt: input.now, termination } : run)
1297
+ runs: state.runs.map((run) => run.runId === target.runId ? { ...run, unavailableAt, stoppedAt: input.now, termination } : run)
1208
1298
  },
1209
1299
  fact: {
1210
1300
  type: "stopped",
@@ -1217,10 +1307,12 @@ function transitionProcessShutdown(source, input) {
1217
1307
  }
1218
1308
  };
1219
1309
  }
1310
+
1311
+ // src/application/process-lifecycle.ts
1220
1312
  function createProcessLifecycleLedger(config) {
1221
1313
  const clock = config.clock ?? (() => new Date);
1222
- const pid = z6.number().int().positive().parse(config.pid ?? process.pid);
1223
- const retain = z6.number().int().min(1).max(1000).parse(config.retain ?? 20);
1314
+ const pid = z7.number().int().positive().parse(config.pid ?? process.pid);
1315
+ const retain = z7.number().int().min(1).max(1000).parse(config.retain ?? 20);
1224
1316
  const configuredRunId = config.runId;
1225
1317
  const nextRunId = typeof configuredRunId === "function" ? configuredRunId : configuredRunId === undefined ? () => crypto.randomUUID() : () => configuredRunId;
1226
1318
  let runId = nextRunId();
@@ -1264,6 +1356,20 @@ function createProcessLifecycleLedger(config) {
1264
1356
  publish(fact);
1265
1357
  return fact;
1266
1358
  },
1359
+ async recordDraining() {
1360
+ const fact = await config.store.update((state) => {
1361
+ const transition = transitionProcessDraining(state, {
1362
+ runId,
1363
+ pid,
1364
+ now: clock().toISOString(),
1365
+ retain
1366
+ });
1367
+ return { state: transition.state, result: transition.fact };
1368
+ });
1369
+ if (fact.recorded)
1370
+ publish(fact);
1371
+ return fact;
1372
+ },
1267
1373
  async recordShutdown(input) {
1268
1374
  const fact = await config.store.update((state) => {
1269
1375
  const transition = transitionProcessShutdown(state, {
@@ -1280,11 +1386,11 @@ function createProcessLifecycleLedger(config) {
1280
1386
  return fact;
1281
1387
  },
1282
1388
  async current() {
1283
- const state = normalize(await config.store.read(), retain);
1389
+ const state = normalizeLifecycleState(await config.store.read(), retain);
1284
1390
  return state.runs.find((run) => run.runId === runId && run.pid === pid) ?? null;
1285
1391
  },
1286
1392
  async runs() {
1287
- return normalize(await config.store.read(), retain).runs;
1393
+ return normalizeLifecycleState(await config.store.read(), retain).runs;
1288
1394
  },
1289
1395
  subscribe(listener) {
1290
1396
  listeners.add(listener);
@@ -1319,6 +1425,9 @@ function lifecycleLedgerResource(ledger, config) {
1319
1425
  async activate() {
1320
1426
  await ledger.recordReady();
1321
1427
  },
1428
+ async stopAdmission() {
1429
+ await ledger.recordDraining();
1430
+ },
1322
1431
  async close() {
1323
1432
  await stop(false);
1324
1433
  },
@@ -1328,27 +1437,27 @@ function lifecycleLedgerResource(ledger, config) {
1328
1437
  });
1329
1438
  }
1330
1439
  // src/application/revision-signal.ts
1331
- import { z as z7 } from "zod";
1332
- var NonNegativeSafeIntegerSchema = z7.number().int().nonnegative().safe();
1333
- var PositiveSafeIntegerSchema2 = z7.number().int().positive().safe();
1334
- var TimerDelaySchema = z7.number().int().positive().max(2147483647);
1335
- var RevisionSignalStateSchema = z7.enum(["open", "closed"]);
1336
- var RevisionSignalWaitOutcomeSchema = z7.enum([
1440
+ import { z as z8 } from "zod";
1441
+ var NonNegativeSafeIntegerSchema = z8.number().int().nonnegative().safe();
1442
+ var PositiveSafeIntegerSchema2 = z8.number().int().positive().safe();
1443
+ var TimerDelaySchema = z8.number().int().positive().max(2147483647);
1444
+ var RevisionSignalStateSchema = z8.enum(["open", "closed"]);
1445
+ var RevisionSignalWaitOutcomeSchema = z8.enum([
1337
1446
  "changed",
1338
1447
  "timed-out",
1339
1448
  "aborted",
1340
1449
  "closed",
1341
1450
  "capacity"
1342
1451
  ]);
1343
- var RevisionSignalWaitResultSchema = z7.object({
1452
+ var RevisionSignalWaitResultSchema = z8.object({
1344
1453
  outcome: RevisionSignalWaitOutcomeSchema,
1345
1454
  revision: NonNegativeSafeIntegerSchema
1346
1455
  }).strict().readonly();
1347
- var RevisionSignalAdvanceResultSchema = z7.object({
1348
- outcome: z7.enum(["advanced", "closed"]),
1456
+ var RevisionSignalAdvanceResultSchema = z8.object({
1457
+ outcome: z8.enum(["advanced", "closed"]),
1349
1458
  revision: NonNegativeSafeIntegerSchema
1350
1459
  }).strict().readonly();
1351
- var RevisionSignalSnapshotSchema = z7.object({
1460
+ var RevisionSignalSnapshotSchema = z8.object({
1352
1461
  state: RevisionSignalStateSchema,
1353
1462
  revision: NonNegativeSafeIntegerSchema,
1354
1463
  pending: NonNegativeSafeIntegerSchema,
@@ -1552,59 +1661,60 @@ function managedServerResource(config) {
1552
1661
  }
1553
1662
  });
1554
1663
  }
1555
- // src/application/watch-hub.ts
1664
+ // src/application/watch-hub-core.ts
1556
1665
  var DEFAULT_BACKOFF = { minDelayMs: 250, maxDelayMs: 30000, jitter: 0.2 };
1557
- function watchKey(operation, args) {
1558
- const record = typeof args === "object" && args !== null && !Array.isArray(args) ? args : { value: args };
1559
- return {
1560
- service: operation.service,
1561
- action: operation.action,
1562
- digest: argumentsDigest(record)
1563
- };
1564
- }
1565
- function createWatchHub(config) {
1566
- const sources = new Map;
1567
- const held = new Map;
1568
- const maxWatches = config.maxWatchesPerSubscriber ?? 64;
1569
- const holdMs = config.holdMs ?? 0;
1570
- const deltaMemoryBytes = config.deltaMemoryBytes ?? 262144;
1571
- const same = config.same ?? ((previous, next) => serializeCanonicalJson(previous) === serializeCanonicalJson(next));
1572
- let reads = 0;
1573
- let closed = false;
1574
- function tell(key, deliver2) {
1666
+
1667
+ class WatchHubCore {
1668
+ config;
1669
+ sources = new Map;
1670
+ held = new Map;
1671
+ maxWatches;
1672
+ holdMs;
1673
+ deltaMemoryBytes;
1674
+ same;
1675
+ reads = 0;
1676
+ closed = false;
1677
+ constructor(config) {
1678
+ this.config = config;
1679
+ this.maxWatches = config.maxWatchesPerSubscriber ?? 64;
1680
+ this.holdMs = config.holdMs ?? 0;
1681
+ this.deltaMemoryBytes = config.deltaMemoryBytes ?? 262144;
1682
+ this.same = config.same ?? ((previous, next) => serializeCanonicalJson(previous) === serializeCanonicalJson(next));
1683
+ }
1684
+ tell(key, send) {
1575
1685
  try {
1576
- deliver2();
1686
+ send();
1577
1687
  return true;
1578
1688
  } catch (error) {
1579
- config.logger?.warn?.("[stitchkit] watch subscriber threw", {
1689
+ this.config.logger?.warn?.("[stitchkit] watch subscriber threw", {
1580
1690
  key: watchKeyString(key),
1581
1691
  error: error instanceof Error ? error.message : String(error)
1582
1692
  });
1583
1693
  return false;
1584
1694
  }
1585
1695
  }
1586
- function announceState(source, state) {
1696
+ announceState(source, state) {
1587
1697
  source.state = state;
1588
1698
  for (const subscriber of source.subscribers) {
1589
- tell(source.key, () => subscriber.state(state));
1699
+ this.tell(source.key, () => subscriber.state(state));
1590
1700
  }
1591
1701
  }
1592
- function remember(source, revision, value, fingerprint) {
1593
- if (deltaMemoryBytes === 0)
1702
+ remember(source, revision, value, fingerprint) {
1703
+ if (this.deltaMemoryBytes === 0)
1594
1704
  return;
1595
1705
  const bytes = (JSON.stringify(value) ?? "null").length;
1596
- if (bytes > deltaMemoryBytes)
1706
+ if (bytes > this.deltaMemoryBytes)
1597
1707
  return;
1598
1708
  source.history.set(revision, { value, fingerprint, bytes });
1599
1709
  source.historyBytes += bytes;
1600
1710
  for (const [oldest, entry] of source.history) {
1601
- if (source.historyBytes <= deltaMemoryBytes)
1711
+ if (source.historyBytes <= this.deltaMemoryBytes)
1602
1712
  break;
1603
1713
  source.history.delete(oldest);
1604
1714
  source.historyBytes -= entry.bytes;
1605
1715
  }
1606
1716
  }
1607
- function frameFor(source, subscriber) {
1717
+ frameFor(source, subscriber) {
1608
1718
  const value = source.value;
1609
1719
  const fingerprint = source.fingerprint ?? valueFingerprint(value);
1610
1720
  const full = {
@@ -1620,10 +1730,10 @@ function createWatchHub(config) {
1620
1730
  if (base === source.revision) {
1621
1731
  return { kind: "unchanged", key: source.key, revision: source.revision, fingerprint };
1622
1732
  }
1623
- const held2 = source.history.get(base);
1624
- if (!held2)
1733
+ const held = source.history.get(base);
1734
+ if (!held)
1625
1735
  return full;
1626
- const delta = diff(held2.value, value);
1736
+ const delta = diff(held.value, value);
1627
1737
  if (delta === undefined || !deltaWins(delta, value))
1628
1738
  return full;
1629
1739
  return {
@@ -1635,7 +1745,7 @@ function createWatchHub(config) {
1635
1745
  delta
1636
1746
  };
1637
1747
  }
1638
- function adoptBaseline(source, subscriber, have) {
1748
+ adoptBaseline(source, subscriber, have) {
1639
1749
  if (source.fingerprint === have.fingerprint) {
1640
1750
  source.baselines.set(subscriber, source.revision);
1641
1751
  return;
@@ -1652,56 +1762,56 @@ function createWatchHub(config) {
1652
1762
  return;
1653
1763
  }
1654
1764
  }
1655
- function deliver(source, subscriber) {
1656
- const frame = frameFor(source, subscriber);
1657
- const delivered = tell(source.key, () => subscriber.value(frame));
1765
+ deliver(source, subscriber) {
1766
+ const frame = this.frameFor(source, subscriber);
1767
+ const delivered = this.tell(source.key, () => subscriber.value(frame));
1658
1768
  if (delivered)
1659
1769
  source.baselines.set(subscriber, source.revision);
1660
1770
  return delivered;
1661
1771
  }
1662
- function publish(source, value, fingerprint) {
1772
+ publish(source, value, fingerprint) {
1663
1773
  if (source.fingerprint !== undefined) {
1664
- remember(source, source.revision, source.value, source.fingerprint);
1774
+ this.remember(source, source.revision, source.value, source.fingerprint);
1665
1775
  }
1666
1776
  source.revision += 1;
1667
1777
  source.value = value;
1668
1778
  source.fingerprint = fingerprint;
1669
1779
  for (const subscriber of source.subscribers)
1670
- deliver(source, subscriber);
1780
+ this.deliver(source, subscriber);
1671
1781
  }
1672
- async function pump(source) {
1673
- if (source.reading || closed)
1782
+ async pump(source) {
1783
+ if (source.reading || this.closed)
1674
1784
  return;
1675
1785
  source.reading = true;
1676
1786
  try {
1677
- while (source.dirty && !closed) {
1787
+ while (source.dirty && !this.closed) {
1678
1788
  source.dirty = false;
1679
- reads += 1;
1789
+ this.reads += 1;
1680
1790
  try {
1681
- const value = await config.read(source.operation, source.args);
1791
+ const value = await this.config.read(source.operation, source.args);
1682
1792
  source.backoff.reset();
1683
1793
  const signature = serializeCanonicalJson(value);
1684
- const unchanged = source.signature !== undefined && same(source.value, value);
1794
+ const unchanged = source.signature !== undefined && this.same(source.value, value);
1685
1795
  source.signature = signature;
1686
1796
  if (!unchanged)
1687
- publish(source, value, valueFingerprint(value));
1797
+ this.publish(source, value, valueFingerprint(value));
1688
1798
  if (source.state.phase !== "live") {
1689
- announceState(source, { key: source.key, phase: "live" });
1799
+ this.announceState(source, { key: source.key, phase: "live" });
1690
1800
  }
1691
1801
  } catch (error) {
1692
1802
  const code = readErrorCode(error);
1693
- announceState(source, {
1803
+ this.announceState(source, {
1694
1804
  key: source.key,
1695
1805
  phase: "unavailable",
1696
1806
  reason: "source-error",
1697
1807
  ...code !== undefined && { code },
1698
1808
  message: error instanceof Error ? error.message : String(error)
1699
1809
  });
1700
- config.logger?.warn?.("[stitchkit] watched read failed", {
1810
+ this.config.logger?.warn?.("[stitchkit] watched read failed", {
1701
1811
  key: watchKeyString(source.key),
1702
1812
  error
1703
1813
  });
1704
- scheduleRetry(source);
1814
+ this.scheduleRetry(source);
1705
1815
  return;
1706
1816
  }
1707
1817
  }
@@ -1709,19 +1819,19 @@ function createWatchHub(config) {
1709
1819
  source.reading = false;
1710
1820
  }
1711
1821
  }
1712
- function scheduleRetry(source) {
1713
- if (closed || source.subscribers.size === 0)
1822
+ scheduleRetry(source) {
1823
+ if (this.closed || source.subscribers.size === 0)
1714
1824
  return;
1715
1825
  clearTimeout(source.retry);
1716
1826
  source.retry = setTimeout(() => {
1717
1827
  source.dirty = true;
1718
- pump(source);
1828
+ this.pump(source);
1719
1829
  }, source.backoff.next());
1720
1830
  source.retry.unref?.();
1721
1831
  }
1722
- function acquire(operation, key, args) {
1832
+ acquire(operation, key, args) {
1723
1833
  const id = watchKeyString(key);
1724
- const existing = sources.get(id);
1834
+ const existing = this.sources.get(id);
1725
1835
  if (existing) {
1726
1836
  clearTimeout(existing.release);
1727
1837
  existing.release = undefined;
@@ -1740,18 +1850,18 @@ function createWatchHub(config) {
1740
1850
  reading: false,
1741
1851
  dirty: true,
1742
1852
  state: { key, phase: "opening" },
1743
- backoff: createBackoff(config.backoff ?? DEFAULT_BACKOFF)
1853
+ backoff: createBackoff(this.config.backoff ?? DEFAULT_BACKOFF)
1744
1854
  };
1745
- for (const topic of config.invalidatedBy(operation, args)) {
1746
- source.unsubscribes.push(config.subscribe(topic, () => {
1855
+ for (const topic of this.config.invalidatedBy(operation, args)) {
1856
+ source.unsubscribes.push(this.config.subscribe(topic, () => {
1747
1857
  source.dirty = true;
1748
- pump(source);
1858
+ this.pump(source);
1749
1859
  }));
1750
1860
  }
1751
- sources.set(id, source);
1861
+ this.sources.set(id, source);
1752
1862
  return source;
1753
1863
  }
1754
- function release(source) {
1864
+ release(source) {
1755
1865
  if (source.subscribers.size > 0)
1756
1866
  return;
1757
1867
  clearTimeout(source.retry);
@@ -1764,109 +1874,105 @@ function createWatchHub(config) {
1764
1874
  }
1765
1875
  for (const unsubscribe of source.unsubscribes)
1766
1876
  unsubscribe();
1767
- sources.delete(watchKeyString(source.key));
1877
+ this.sources.delete(watchKeyString(source.key));
1768
1878
  };
1769
- if (holdMs === 0) {
1879
+ if (this.holdMs === 0) {
1770
1880
  drop();
1771
1881
  return;
1772
1882
  }
1773
- source.release = setTimeout(drop, holdMs);
1883
+ source.release = setTimeout(drop, this.holdMs);
1774
1884
  source.release.unref?.();
1775
1885
  }
1776
- return {
1777
- attach(subscriber) {
1778
- const keys = new Set;
1779
- held.set(subscriber, keys);
1780
- return {
1781
- open(key, args, have) {
1782
- if (closed)
1783
- return { accepted: false, reason: "the watch hub is closed" };
1784
- const operation = { service: key.service, action: key.action };
1785
- if (!config.watchable(operation)) {
1786
- return {
1787
- accepted: false,
1788
- reason: `${key.service}.${key.action} is not watchable`
1789
- };
1790
- }
1791
- const id = watchKeyString(key);
1792
- if (keys.has(id))
1793
- return { accepted: true };
1794
- if (keys.size >= maxWatches) {
1795
- return {
1796
- accepted: false,
1797
- reason: `this connection is already watching ${keys.size} reads (limit ${maxWatches})`
1798
- };
1799
- }
1800
- const source = acquire(operation, key, args);
1801
- source.subscribers.add(subscriber);
1802
- keys.add(id);
1803
- if (have)
1804
- adoptBaseline(source, subscriber, have);
1805
- try {
1806
- if (source.signature !== undefined) {
1807
- const frame = frameFor(source, subscriber);
1808
- subscriber.value(frame);
1809
- source.baselines.set(subscriber, source.revision);
1810
- }
1811
- subscriber.state(source.state);
1812
- } catch (error) {
1813
- source.subscribers.delete(subscriber);
1814
- source.baselines.delete(subscriber);
1815
- keys.delete(id);
1816
- release(source);
1817
- return {
1818
- accepted: false,
1819
- reason: error instanceof Error ? error.message : String(error)
1820
- };
1821
- }
1822
- pump(source);
1886
+ attach(subscriber) {
1887
+ const keys = new Set;
1888
+ this.held.set(subscriber, keys);
1889
+ return {
1890
+ open: (key, args, have) => {
1891
+ if (this.closed)
1892
+ return { accepted: false, reason: "the watch hub is closed" };
1893
+ const operation = { service: key.service, action: key.action };
1894
+ if (!this.config.watchable(operation)) {
1895
+ return {
1896
+ accepted: false,
1897
+ reason: `${key.service}.${key.action} is not watchable`
1898
+ };
1899
+ }
1900
+ const id = watchKeyString(key);
1901
+ if (keys.has(id))
1823
1902
  return { accepted: true };
1824
- },
1825
- close(key) {
1826
- const id = watchKeyString(key);
1827
- if (!keys.delete(id))
1828
- return;
1829
- const source = sources.get(id);
1903
+ if (keys.size >= this.maxWatches) {
1904
+ return {
1905
+ accepted: false,
1906
+ reason: `this connection is already watching ${keys.size} reads (limit ${this.maxWatches})`
1907
+ };
1908
+ }
1909
+ const source = this.acquire(operation, key, args);
1910
+ source.subscribers.add(subscriber);
1911
+ keys.add(id);
1912
+ if (have)
1913
+ this.adoptBaseline(source, subscriber, have);
1914
+ try {
1915
+ if (source.signature !== undefined) {
1916
+ const frame = this.frameFor(source, subscriber);
1917
+ subscriber.value(frame);
1918
+ source.baselines.set(subscriber, source.revision);
1919
+ }
1920
+ subscriber.state(source.state);
1921
+ } catch (error) {
1922
+ source.subscribers.delete(subscriber);
1923
+ source.baselines.delete(subscriber);
1924
+ keys.delete(id);
1925
+ this.release(source);
1926
+ return {
1927
+ accepted: false,
1928
+ reason: error instanceof Error ? error.message : String(error)
1929
+ };
1930
+ }
1931
+ this.pump(source);
1932
+ return { accepted: true };
1933
+ },
1934
+ close: (key) => {
1935
+ const id = watchKeyString(key);
1936
+ if (!keys.delete(id))
1937
+ return;
1938
+ const source = this.sources.get(id);
1939
+ if (!source)
1940
+ return;
1941
+ source.subscribers.delete(subscriber);
1942
+ source.baselines.delete(subscriber);
1943
+ this.release(source);
1944
+ },
1945
+ detach: () => {
1946
+ for (const id of keys) {
1947
+ const source = this.sources.get(id);
1830
1948
  if (!source)
1831
- return;
1949
+ continue;
1832
1950
  source.subscribers.delete(subscriber);
1833
1951
  source.baselines.delete(subscriber);
1834
- release(source);
1835
- },
1836
- detach() {
1837
- for (const id of keys) {
1838
- const source = sources.get(id);
1839
- if (!source)
1840
- continue;
1841
- source.subscribers.delete(subscriber);
1842
- source.baselines.delete(subscriber);
1843
- release(source);
1844
- }
1845
- keys.clear();
1846
- held.delete(subscriber);
1952
+ this.release(source);
1847
1953
  }
1848
- };
1849
- },
1850
- readCount: () => reads,
1851
- size: () => sources.size,
1852
- close() {
1853
- closed = true;
1854
- const teardown = [...sources.values()];
1855
- sources.clear();
1856
- held.clear();
1857
- for (const source of teardown) {
1858
- clearTimeout(source.retry);
1859
- clearTimeout(source.release);
1860
- announceState(source, {
1861
- key: source.key,
1862
- phase: "unavailable",
1863
- reason: "source-unavailable"
1864
- });
1865
- for (const unsubscribe of source.unsubscribes)
1866
- unsubscribe();
1954
+ keys.clear();
1955
+ this.held.delete(subscriber);
1867
1956
  }
1957
+ };
1958
+ }
1959
+ close() {
1960
+ this.closed = true;
1961
+ const teardown = [...this.sources.values()];
1962
+ this.sources.clear();
1963
+ this.held.clear();
1964
+ for (const source of teardown) {
1965
+ clearTimeout(source.retry);
1966
+ clearTimeout(source.release);
1967
+ this.announceState(source, {
1968
+ key: source.key,
1969
+ phase: "unavailable",
1970
+ reason: "source-unavailable"
1971
+ });
1972
+ for (const unsubscribe of source.unsubscribes)
1973
+ unsubscribe();
1868
1974
  }
1869
- };
1975
+ }
1870
1976
  }
1871
1977
  function valueFingerprint(value) {
1872
1978
  return argumentsDigest({ value });
@@ -1877,11 +1983,31 @@ function readErrorCode(error) {
1877
1983
  const code = Reflect.get(error, "code");
1878
1984
  return typeof code === "string" ? code : undefined;
1879
1985
  }
1986
+
1987
+ // src/application/watch-hub.ts
1988
+ function watchKey(operation, args) {
1989
+ const record = typeof args === "object" && args !== null && !Array.isArray(args) ? args : { value: args };
1990
+ return {
1991
+ service: operation.service,
1992
+ action: operation.action,
1993
+ digest: argumentsDigest(record)
1994
+ };
1995
+ }
1996
+ function createWatchHub(config) {
1997
+ const hub = new WatchHubCore(config);
1998
+ return {
1999
+ attach: (subscriber) => hub.attach(subscriber),
2000
+ readCount: () => hub.reads,
2001
+ size: () => hub.sources.size,
2002
+ close: () => hub.close()
2003
+ };
2004
+ }
1880
2005
  export {
1881
2006
  watchKey,
1882
2007
  transitionProcessStart,
1883
2008
  transitionProcessShutdown,
1884
2009
  transitionProcessReady,
2010
+ transitionProcessDraining,
1885
2011
  sqliteKeyspaceBackend,
1886
2012
  readDiagnosticJournalLockDiagnosis,
1887
2013
  projectApplicationStatus,