stitchkit 0.93.0 → 0.94.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.
- package/CHANGELOG.md +134 -0
- package/README.md +5 -2
- package/dist/agent-runtime/children-control.d.ts +6 -0
- package/dist/agent-runtime/children-control.d.ts.map +1 -0
- package/dist/agent-runtime/children-spawn.d.ts +6 -0
- package/dist/agent-runtime/children-spawn.d.ts.map +1 -0
- package/dist/agent-runtime/children-state.d.ts +46 -0
- package/dist/agent-runtime/children-state.d.ts.map +1 -0
- package/dist/agent-runtime/children-usage.d.ts +8 -0
- package/dist/agent-runtime/children-usage.d.ts.map +1 -0
- package/dist/agent-runtime/children.d.ts +11 -14
- package/dist/agent-runtime/children.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-contract.d.ts +2 -1
- package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
- package/dist/agent-runtime/deferred-tool-types.d.ts +2 -2
- package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
- package/dist/agent-runtime/events.d.ts +1 -1
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/managed-tools.d.ts +1 -1
- package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts +1 -1
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/{agent-runtime-openrouter.d.ts → agent-runtime/openrouter.d.ts} +3 -3
- package/dist/agent-runtime/openrouter.d.ts.map +1 -0
- package/dist/agent-runtime/projections.d.ts +1 -1
- package/dist/agent-runtime/projections.d.ts.map +1 -1
- package/dist/agent-runtime/run-acquisition.d.ts +17 -0
- package/dist/agent-runtime/run-acquisition.d.ts.map +1 -0
- package/dist/agent-runtime/run-execution-state.d.ts +133 -0
- package/dist/agent-runtime/run-execution-state.d.ts.map +1 -0
- package/dist/agent-runtime/run-execution.d.ts +17 -33
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/run-failure.d.ts +18 -0
- package/dist/agent-runtime/run-failure.d.ts.map +1 -0
- package/dist/agent-runtime/run-model-attempts.d.ts +6 -0
- package/dist/agent-runtime/run-model-attempts.d.ts.map +1 -0
- package/dist/agent-runtime/run-prompt-history.d.ts +58 -0
- package/dist/agent-runtime/run-prompt-history.d.ts.map +1 -0
- package/dist/agent-runtime/run-provider-ledger.d.ts +26 -0
- package/dist/agent-runtime/run-provider-ledger.d.ts.map +1 -0
- package/dist/agent-runtime/run-stream-parts.d.ts +17 -0
- package/dist/agent-runtime/run-stream-parts.d.ts.map +1 -0
- package/dist/agent-runtime/run-terminal.d.ts +11 -0
- package/dist/agent-runtime/run-terminal.d.ts.map +1 -0
- package/dist/agent-runtime/run-turn-preparation.d.ts +22 -0
- package/dist/agent-runtime/run-turn-preparation.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-admission-gate.d.ts +29 -0
- package/dist/agent-runtime/runtime-admission-gate.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-control.d.ts +9 -0
- package/dist/agent-runtime/runtime-control.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-recovery.d.ts +10 -0
- package/dist/agent-runtime/runtime-recovery.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-state.d.ts +36 -0
- package/dist/agent-runtime/runtime-state.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-submit.d.ts +5 -0
- package/dist/agent-runtime/runtime-submit.d.ts.map +1 -0
- package/dist/agent-runtime/runtime.d.ts +9 -4
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/{agent-runtime-sqlite-bun.d.ts → agent-runtime/sqlite-bun.d.ts} +5 -4
- package/dist/agent-runtime/sqlite-bun.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-events.d.ts +5 -0
- package/dist/agent-runtime/sqlite-events.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-history.d.ts +7 -0
- package/dist/agent-runtime/sqlite-history.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-ledger.d.ts +7 -0
- package/dist/agent-runtime/sqlite-ledger.d.ts.map +1 -0
- package/dist/{agent-runtime-sqlite-node.d.ts → agent-runtime/sqlite-node.d.ts} +5 -4
- package/dist/agent-runtime/sqlite-node.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-reads.d.ts +14 -0
- package/dist/agent-runtime/sqlite-reads.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-rows.d.ts +178 -0
- package/dist/agent-runtime/sqlite-rows.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-schema.d.ts +7 -0
- package/dist/agent-runtime/sqlite-schema.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-transactions.d.ts +25 -0
- package/dist/agent-runtime/sqlite-transactions.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite.d.ts +10 -6
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime/store-archive.d.ts +16 -0
- package/dist/agent-runtime/store-archive.d.ts.map +1 -0
- package/dist/{testing/agent-store-conformance.d.ts → agent-runtime/store-conformance.d.ts} +2 -2
- package/dist/agent-runtime/store-conformance.d.ts.map +1 -0
- package/dist/agent-runtime/store-create.d.ts +11 -0
- package/dist/agent-runtime/store-create.d.ts.map +1 -0
- package/dist/agent-runtime/{store-driver.d.ts → store-driver-contract.d.ts} +7 -7
- package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -0
- package/dist/agent-runtime/store-events.d.ts +7 -89
- package/dist/agent-runtime/store-events.d.ts.map +1 -1
- package/dist/agent-runtime/store-memory.d.ts +4 -0
- package/dist/agent-runtime/store-memory.d.ts.map +1 -0
- package/dist/agent-runtime/store-mutate.d.ts +13 -0
- package/dist/agent-runtime/store-mutate.d.ts.map +1 -0
- package/dist/agent-runtime/store-purge.d.ts +1 -1
- package/dist/agent-runtime/store-purge.d.ts.map +1 -1
- package/dist/agent-runtime/store-reads.d.ts +29 -0
- package/dist/agent-runtime/store-reads.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-history.d.ts +10 -0
- package/dist/agent-runtime/store-reduce-history.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-run.d.ts +14 -0
- package/dist/agent-runtime/store-reduce-run.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-shared.d.ts +76 -0
- package/dist/agent-runtime/store-reduce-shared.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-terminal.d.ts +6 -0
- package/dist/agent-runtime/store-reduce-terminal.d.ts.map +1 -0
- package/dist/agent-runtime/store-reducer.d.ts +24 -0
- package/dist/agent-runtime/store-reducer.d.ts.map +1 -0
- package/dist/agent-runtime/store-snapshot.d.ts +19 -0
- package/dist/agent-runtime/store-snapshot.d.ts.map +1 -0
- package/dist/agent-runtime/store.d.ts +1 -1
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/application/admission.d.ts +1 -1
- package/dist/application/admission.d.ts.map +1 -1
- package/dist/application/events.d.ts +1 -1
- package/dist/application/events.d.ts.map +1 -1
- package/dist/{server → application}/file-state-store.d.ts +1 -1
- package/dist/application/file-state-store.d.ts.map +1 -0
- package/dist/application/health.d.ts +1 -1
- package/dist/application/health.d.ts.map +1 -1
- package/dist/application/kernel-admission.d.ts +8 -0
- package/dist/application/kernel-admission.d.ts.map +1 -0
- package/dist/application/kernel-contract.d.ts +142 -0
- package/dist/application/kernel-contract.d.ts.map +1 -0
- package/dist/application/kernel-resource-context.d.ts +24 -0
- package/dist/application/kernel-resource-context.d.ts.map +1 -0
- package/dist/application/kernel-restart.d.ts +5 -0
- package/dist/application/kernel-restart.d.ts.map +1 -0
- package/dist/application/kernel-rollback.d.ts +4 -0
- package/dist/application/kernel-rollback.d.ts.map +1 -0
- package/dist/application/kernel-shutdown.d.ts +6 -0
- package/dist/application/kernel-shutdown.d.ts.map +1 -0
- package/dist/application/kernel-start.d.ts +20 -0
- package/dist/application/kernel-start.d.ts.map +1 -0
- package/dist/application/kernel-state.d.ts +116 -0
- package/dist/application/kernel-state.d.ts.map +1 -0
- package/dist/application/kernel.d.ts +9 -140
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/latest-sink.d.ts +1 -1
- package/dist/application/latest-sink.d.ts.map +1 -1
- package/dist/application/opentelemetry.d.ts +1 -1
- package/dist/application/opentelemetry.d.ts.map +1 -1
- package/dist/application/watch-hub.d.ts +2 -2
- package/dist/application/watch-hub.d.ts.map +1 -1
- package/dist/browser/client-multipart.d.ts +1 -1
- package/dist/browser/client-multipart.d.ts.map +1 -1
- package/dist/browser/client-url.d.ts +1 -1
- package/dist/browser/client-url.d.ts.map +1 -1
- package/dist/browser/client.d.ts +1 -1
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/contract-stream.d.ts +1 -1
- package/dist/browser/contract-stream.d.ts.map +1 -1
- package/dist/browser/http.d.ts +1 -1
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/resumable.d.ts +1 -28
- package/dist/browser/resumable.d.ts.map +1 -1
- package/dist/browser/retained.d.ts.map +1 -0
- package/dist/browser/socket-io.d.ts +23 -1
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +14 -2
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/contract/define.d.ts +72 -65
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/{internal/errors.d.ts → contract/normalize.d.ts} +3 -39
- package/dist/contract/normalize.d.ts.map +1 -0
- package/dist/contract/tool-view.d.ts +135 -0
- package/dist/contract/tool-view.d.ts.map +1 -0
- package/dist/declaration/declaration.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-contract.d.ts → durability/contract.d.ts} +2 -2
- package/dist/durability/contract.d.ts.map +1 -0
- package/dist/{agent-runtime/durability.d.ts → durability/engine.d.ts} +4 -4
- package/dist/durability/engine.d.ts.map +1 -0
- package/dist/durability/events.d.ts +101 -0
- package/dist/durability/events.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-json.d.ts → durability/json.d.ts} +1 -1
- package/dist/durability/json.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-ledger.d.ts → durability/ledger.d.ts} +2 -2
- package/dist/durability/ledger.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-scheduler.d.ts → durability/scheduler.d.ts} +2 -2
- package/dist/durability/scheduler.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/browser.d.ts +5 -0
- package/dist/entrypoints/agent-runtime/browser.d.ts.map +1 -0
- package/dist/{agent-runtime-browser.js → entrypoints/agent-runtime/browser.js} +3 -3
- package/dist/{agent-runtime-coding-tools.d.ts → entrypoints/agent-runtime/coding-tools.d.ts} +2 -2
- package/dist/entrypoints/agent-runtime/coding-tools.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/coding-tools.js +22 -0
- package/dist/{agent-runtime-harness.d.ts → entrypoints/agent-runtime/harness.d.ts} +2 -2
- package/dist/entrypoints/agent-runtime/harness.d.ts.map +1 -0
- package/dist/{agent-runtime-harness.js → entrypoints/agent-runtime/harness.js} +24 -25
- package/dist/entrypoints/agent-runtime/openrouter.d.ts +2 -0
- package/dist/entrypoints/agent-runtime/openrouter.d.ts.map +1 -0
- package/dist/{agent-runtime-openrouter.js → entrypoints/agent-runtime/openrouter.js} +3 -3
- package/dist/{agent-runtime-sandbox.d.ts → entrypoints/agent-runtime/sandbox.d.ts} +5 -5
- package/dist/entrypoints/agent-runtime/sandbox.d.ts.map +1 -0
- package/dist/{agent-runtime-sandbox.js → entrypoints/agent-runtime/sandbox.js} +11 -12
- package/dist/entrypoints/agent-runtime/sqlite/bun.d.ts +2 -0
- package/dist/entrypoints/agent-runtime/sqlite/bun.d.ts.map +1 -0
- package/dist/{agent-runtime-sqlite-bun.js → entrypoints/agent-runtime/sqlite/bun.js} +8 -6
- package/dist/entrypoints/agent-runtime/sqlite/node.d.ts +2 -0
- package/dist/entrypoints/agent-runtime/sqlite/node.d.ts.map +1 -0
- package/dist/{agent-runtime-sqlite-node.js → entrypoints/agent-runtime/sqlite/node.js} +8 -6
- package/dist/entrypoints/agent-runtime/testing.d.ts +4 -0
- package/dist/entrypoints/agent-runtime/testing.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/testing.js +1084 -0
- package/dist/{agent-runtime.d.ts → entrypoints/agent-runtime.d.ts} +41 -37
- package/dist/entrypoints/agent-runtime.d.ts.map +1 -0
- package/dist/{agent-runtime.js → entrypoints/agent-runtime.js} +408 -366
- package/dist/{application-diagnostic-journal.d.ts → entrypoints/application/diagnostic-journal.d.ts} +2 -2
- package/dist/entrypoints/application/diagnostic-journal.d.ts.map +1 -0
- package/dist/{application-diagnostic-journal.js → entrypoints/application/diagnostic-journal.js} +3 -3
- package/dist/{application-grammy.d.ts → entrypoints/application/grammy.d.ts} +2 -2
- package/dist/entrypoints/application/grammy.d.ts.map +1 -0
- package/dist/{application-grammy.js → entrypoints/application/grammy.js} +2 -2
- package/dist/{application-opentelemetry.d.ts → entrypoints/application/opentelemetry.d.ts} +2 -2
- package/dist/entrypoints/application/opentelemetry.d.ts.map +1 -0
- package/dist/{application-opentelemetry.js → entrypoints/application/opentelemetry.js} +4 -4
- package/dist/{application-schemas.d.ts → entrypoints/application/schemas.d.ts} +2 -2
- package/dist/entrypoints/application/schemas.d.ts.map +1 -0
- package/dist/{application-schemas.js → entrypoints/application/schemas.js} +2 -2
- package/dist/{application.d.ts → entrypoints/application.d.ts} +22 -21
- package/dist/entrypoints/application.d.ts.map +1 -0
- package/dist/{application.js → entrypoints/application.js} +24 -22
- package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -0
- package/dist/{upgrade-cli.js → entrypoints/bin/upgrade-cli.js} +3 -3
- package/dist/{cli.d.ts → entrypoints/cli.d.ts} +15 -15
- package/dist/entrypoints/cli.d.ts.map +1 -0
- package/dist/{cli.js → entrypoints/cli.js} +30 -32
- package/dist/entrypoints/contract.d.ts +9 -0
- package/dist/entrypoints/contract.d.ts.map +1 -0
- package/dist/{contract/index.js → entrypoints/contract.js} +14 -6
- package/dist/entrypoints/declaration.d.ts +2 -0
- package/dist/entrypoints/declaration.d.ts.map +1 -0
- package/dist/{declaration.js → entrypoints/declaration.js} +1 -1
- package/dist/{files.d.ts → entrypoints/files.d.ts} +2 -2
- package/dist/entrypoints/files.d.ts.map +1 -0
- package/dist/{files.js → entrypoints/files.js} +3 -3
- package/dist/{geo.d.ts → entrypoints/geo.d.ts} +1 -1
- package/dist/entrypoints/geo.d.ts.map +1 -0
- package/dist/{geo.js → entrypoints/geo.js} +4 -4
- package/dist/entrypoints/google.d.ts +2 -0
- package/dist/entrypoints/google.d.ts.map +1 -0
- package/dist/{google.js → entrypoints/google.js} +1 -1
- package/dist/{index.d.ts → entrypoints/index.d.ts} +14 -13
- package/dist/entrypoints/index.d.ts.map +1 -0
- package/dist/{index.js → entrypoints/index.js} +99 -33
- package/dist/{live.d.ts → entrypoints/live.d.ts} +4 -4
- package/dist/entrypoints/live.d.ts.map +1 -0
- package/dist/{live.js → entrypoints/live.js} +6 -4
- package/dist/{node.d.ts → entrypoints/node.d.ts} +11 -11
- package/dist/entrypoints/node.d.ts.map +1 -0
- package/dist/{node.js → entrypoints/node.js} +20 -16
- package/dist/entrypoints/oauth.d.ts +2 -0
- package/dist/entrypoints/oauth.d.ts.map +1 -0
- package/dist/{oauth.js → entrypoints/oauth.js} +3 -3
- package/dist/{observability/index.d.ts → entrypoints/observability.d.ts} +11 -11
- package/dist/entrypoints/observability.d.ts.map +1 -0
- package/dist/{observability/index.js → entrypoints/observability.js} +9 -11
- package/dist/entrypoints/primitives.d.ts +3 -0
- package/dist/entrypoints/primitives.d.ts.map +1 -0
- package/dist/{primitives.js → entrypoints/primitives.js} +1 -6
- package/dist/{react.d.ts → entrypoints/react.d.ts} +4 -4
- package/dist/entrypoints/react.d.ts.map +1 -0
- package/dist/{react.js → entrypoints/react.js} +3 -3
- package/dist/{release.d.ts → entrypoints/release.d.ts} +4 -4
- package/dist/entrypoints/release.d.ts.map +1 -0
- package/dist/{release.js → entrypoints/release.js} +1 -1
- package/dist/{remote.d.ts → entrypoints/remote.d.ts} +1 -1
- package/dist/entrypoints/remote.d.ts.map +1 -0
- package/dist/entrypoints/remote.js +66 -0
- package/dist/{server/index.d.ts → entrypoints/server.d.ts} +35 -34
- package/dist/entrypoints/server.d.ts.map +1 -0
- package/dist/{server/index.js → entrypoints/server.js} +294 -244
- package/dist/{telegram.d.ts → entrypoints/telegram.d.ts} +2 -2
- package/dist/entrypoints/telegram.d.ts.map +1 -0
- package/dist/{telegram.js → entrypoints/telegram.js} +1 -1
- package/dist/entrypoints/testing.d.ts +9 -0
- package/dist/entrypoints/testing.d.ts.map +1 -0
- package/dist/{testing.js → entrypoints/testing.js} +80 -919
- package/dist/{tools/connections/index.d.ts → entrypoints/tools/connections.d.ts} +6 -7
- package/dist/entrypoints/tools/connections.d.ts.map +1 -0
- package/dist/{tools/connections/index.js → entrypoints/tools/connections.js} +7 -4
- package/dist/{tools-contract.d.ts → entrypoints/tools/contract.d.ts} +5 -5
- package/dist/entrypoints/tools/contract.d.ts.map +1 -0
- package/dist/{tools-contract.js → entrypoints/tools/contract.js} +4 -4
- package/dist/{tool-invoker.d.ts → entrypoints/tools/invoker.d.ts} +2 -2
- package/dist/entrypoints/tools/invoker.d.ts.map +1 -0
- package/dist/entrypoints/tools/invoker.js +20 -0
- package/dist/entrypoints/tools.d.ts +51 -0
- package/dist/entrypoints/tools.d.ts.map +1 -0
- package/dist/{tools.js → entrypoints/tools.js} +1787 -1751
- package/dist/{tracking-server.d.ts → entrypoints/tracking/server.d.ts} +8 -8
- package/dist/entrypoints/tracking/server.d.ts.map +1 -0
- package/dist/{tracking.d.ts → entrypoints/tracking.d.ts} +15 -15
- package/dist/entrypoints/tracking.d.ts.map +1 -0
- package/dist/{tracking.js → entrypoints/tracking.js} +5 -8
- package/dist/google/google.d.ts.map +1 -0
- package/dist/{index-a916km3r.js → index-0b1ess5e.js} +6 -13
- package/dist/{index-zwkgfq1s.js → index-1nd48tm9.js} +113 -118
- package/dist/{index-7zbps32p.js → index-1zbbzgfh.js} +2 -2
- package/dist/{index-mzvgxp0n.js → index-2cr1cqhn.js} +16 -4
- package/dist/{index-f9mb610r.js → index-35mh5bhj.js} +2 -2
- package/dist/index-48ffdxgk.js +6 -0
- package/dist/index-58k4fp34.js +1023 -0
- package/dist/{index-p13mwz16.js → index-83bdg97s.js} +1 -1
- package/dist/index-97ac4gph.js +3618 -0
- package/dist/{index-kw9t5ayn.js → index-cq4qfycf.js} +5 -5
- package/dist/{index-7gnkny4z.js → index-d9pk3td4.js} +1 -1
- package/dist/{index-e42wzzz5.js → index-dcpdvjsc.js} +7 -7
- package/dist/{index-h9etkkyk.js → index-dydjcp8z.js} +102 -25
- package/dist/{index-sr93cwf9.js → index-e2z3ahw9.js} +2 -2
- package/dist/{index-yewwh1w4.js → index-e87kakm4.js} +96 -11
- package/dist/{index-cby4ar3v.js → index-eqqvgdb3.js} +1 -1
- package/dist/index-fdp6hdxn.js +942 -0
- package/dist/{index-0a349c33.js → index-gyr48nzg.js} +125 -198
- package/dist/{index-wnfk50r0.js → index-h6eeys1v.js} +1 -1
- package/dist/{index-77fekveh.js → index-hffjpmq9.js} +1 -15
- package/dist/{index-yn8remb1.js → index-j05z5gn4.js} +50 -14
- package/dist/index-k5hsfc3j.js +31 -0
- package/dist/{index-fq77yfzc.js → index-kj2wbeta.js} +1 -1
- package/dist/index-kvj7f4hq.js +39 -0
- package/dist/{index-4fpa9dy2.js → index-m9zn6cpq.js} +2 -2
- package/dist/{index-2na0qrfn.js → index-mdzh02yc.js} +7 -7
- package/dist/{index-ywd0y6m1.js → index-mkm15s49.js} +1 -1
- package/dist/index-mp5sn43f.js +1084 -0
- package/dist/{index-3bmnp41g.js → index-n6hxg8as.js} +507 -419
- package/dist/index-ngfhvfpd.js +30 -0
- package/dist/{index-e0znv5gn.js → index-p5r7qpdd.js} +2 -93
- package/dist/{index-n0f51dfn.js → index-p81267c2.js} +608 -549
- package/dist/{index-wc4x00bn.js → index-qwhynv6x.js} +1 -1
- package/dist/index-r7nmqpsg.js +154 -0
- package/dist/{index-bx75zwt7.js → index-rewkc2hj.js} +5 -4
- package/dist/{index-9f8ztey6.js → index-sees8yf8.js} +2 -2
- package/dist/{index-vjpvjqpn.js → index-v38qyerb.js} +16 -2
- package/dist/{index-2c2bjw3s.js → index-vcvkq30w.js} +3 -3
- package/dist/index-wqgjfstn.js +18 -0
- package/dist/{index-f477k430.js → index-x4799c19.js} +1 -1
- package/dist/{index-cxqhvq21.js → index-xnj9bgxe.js} +142 -129
- package/dist/index-xsqjkha9.js +281 -0
- package/dist/{index-6kyksz9y.js → index-yw4fzmdz.js} +1 -1
- package/dist/{index-3xwxfj4z.js → index-zeemqhqx.js} +1 -1
- package/dist/{index-tmwcrptx.js → index-zfrhfn82.js} +1 -1
- package/dist/index-znfs20ss.js +98 -0
- package/dist/internal/backoff.d.ts +29 -0
- package/dist/internal/backoff.d.ts.map +1 -0
- package/dist/internal/canonical-json.d.ts +21 -11
- package/dist/internal/canonical-json.d.ts.map +1 -1
- package/dist/internal/channel.d.ts.map +1 -0
- package/dist/internal/logger.d.ts.map +1 -0
- package/dist/internal/request.d.ts.map +1 -0
- package/dist/internal/stable-digest.d.ts +0 -43
- package/dist/internal/stable-digest.d.ts.map +1 -1
- package/dist/internal/typed.d.ts +0 -8
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/internal/zod-issues.d.ts +38 -0
- package/dist/internal/zod-issues.d.ts.map +1 -0
- package/dist/{tools/json-schema-dialect.d.ts → json-schema/dialect.d.ts} +1 -1
- package/dist/json-schema/dialect.d.ts.map +1 -0
- package/dist/json-schema/json-schema.d.ts.map +1 -0
- package/dist/live/watch-delta.d.ts.map +1 -1
- package/dist/{oauth.d.ts → oauth/authorization-code.d.ts} +1 -1
- package/dist/oauth/authorization-code.d.ts.map +1 -0
- package/dist/observability/audit.d.ts +2 -2
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/bounded-logger.d.ts +1 -1
- package/dist/observability/bounded-logger.d.ts.map +1 -1
- package/dist/{internal/observability-sink.d.ts → observability/bounded-sink.d.ts} +2 -2
- package/dist/observability/bounded-sink.d.ts.map +1 -0
- package/dist/observability/context.d.ts +1 -1
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/event.d.ts +1 -1
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/work.d.ts +1 -1
- package/dist/observability/work.d.ts.map +1 -1
- package/dist/primitives/audit.d.ts +1 -1
- package/dist/primitives/audit.d.ts.map +1 -1
- package/dist/primitives/export-operation.d.ts +4 -2
- package/dist/primitives/export-operation.d.ts.map +1 -1
- package/dist/realtime/socket.d.ts +1 -1
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/server/context-contribution.d.ts +1 -1
- package/dist/server/context-contribution.d.ts.map +1 -1
- package/dist/server/context.d.ts +2 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/contract-method.d.ts +15 -0
- package/dist/server/contract-method.d.ts.map +1 -0
- package/dist/server/contract-stream.d.ts +1 -1
- package/dist/server/contract-stream.d.ts.map +1 -1
- package/dist/server/create.d.ts +8 -0
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/endpoint-dispatch.d.ts +10 -0
- package/dist/server/endpoint-dispatch.d.ts.map +1 -0
- package/dist/server/endpoint-response.d.ts +18 -0
- package/dist/server/endpoint-response.d.ts.map +1 -0
- package/dist/server/error-dispatch.d.ts +17 -1
- package/dist/server/error-dispatch.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +2 -2
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/handler-state.d.ts +44 -0
- package/dist/server/handler-state.d.ts.map +1 -0
- package/dist/server/implement.d.ts +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +1 -1
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/cors.d.ts +17 -0
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/middleware/trust-fence.d.ts +1 -1
- package/dist/server/middleware/trust-fence.d.ts.map +1 -1
- package/dist/server/multipart.d.ts +1 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/{tools/oauth-metadata.d.ts → server/oauth/metadata.d.ts} +2 -2
- package/dist/server/oauth/metadata.d.ts.map +1 -0
- package/dist/{tools/oauth-provider.d.ts → server/oauth/provider.d.ts} +2 -2
- package/dist/server/oauth/provider.d.ts.map +1 -0
- package/dist/server/realtime.d.ts +1 -1
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/request-completion.d.ts +16 -0
- package/dist/server/request-completion.d.ts.map +1 -0
- package/dist/server/response-metadata.d.ts +1 -1
- package/dist/server/response-metadata.d.ts.map +1 -1
- package/dist/server/route-table.d.ts +9 -0
- package/dist/server/route-table.d.ts.map +1 -0
- package/dist/server/stream.d.ts +0 -13
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/types.d.ts +7 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/handler-clients.d.ts +31 -0
- package/dist/testing/handler-clients.d.ts.map +1 -0
- package/dist/testing/managed-resource-conformance-scenarios.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +2 -1
- package/dist/testing/surface-conformance.d.ts.map +1 -1
- package/dist/testing/surface-manifest.d.ts +30 -4
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/tools/{cli-args.d.ts → cli/args.d.ts} +1 -1
- package/dist/tools/cli/args.d.ts.map +1 -0
- package/dist/tools/{cli-checkpoint.d.ts → cli/checkpoint.d.ts} +1 -1
- package/dist/tools/cli/checkpoint.d.ts.map +1 -0
- package/dist/tools/{cli-command.d.ts → cli/command.d.ts} +4 -4
- package/dist/tools/cli/command.d.ts.map +1 -0
- package/dist/tools/{cli.d.ts → cli/config.d.ts} +5 -9
- package/dist/tools/cli/config.d.ts.map +1 -0
- package/dist/tools/cli/create-cli.d.ts +36 -0
- package/dist/tools/cli/create-cli.d.ts.map +1 -0
- package/dist/tools/cli/download.d.ts +8 -0
- package/dist/tools/cli/download.d.ts.map +1 -0
- package/dist/tools/{cli-format.d.ts → cli/format.d.ts} +2 -2
- package/dist/tools/cli/format.d.ts.map +1 -0
- package/dist/tools/cli/help.d.ts +24 -0
- package/dist/tools/cli/help.d.ts.map +1 -0
- package/dist/tools/{cli-installer.d.ts → cli/installer.d.ts} +2 -2
- package/dist/tools/cli/installer.d.ts.map +1 -0
- package/dist/tools/{cli-invoke.d.ts → cli/invoke.d.ts} +8 -8
- package/dist/tools/cli/invoke.d.ts.map +1 -0
- package/dist/tools/cli/io.d.ts +10 -0
- package/dist/tools/cli/io.d.ts.map +1 -0
- package/dist/tools/cli/managed-surface.d.ts +30 -0
- package/dist/tools/cli/managed-surface.d.ts.map +1 -0
- package/dist/tools/{cli-manifest.d.ts → cli/manifest.d.ts} +1 -1
- package/dist/tools/cli/manifest.d.ts.map +1 -0
- package/dist/tools/{cli-policy.d.ts → cli/policy.d.ts} +2 -2
- package/dist/tools/cli/policy.d.ts.map +1 -0
- package/dist/tools/cli/prepare.d.ts +14 -0
- package/dist/tools/cli/prepare.d.ts.map +1 -0
- package/dist/tools/{cli-profile.d.ts → cli/profile.d.ts} +2 -2
- package/dist/tools/cli/profile.d.ts.map +1 -0
- package/dist/tools/cli/run-managed.d.ts +5 -0
- package/dist/tools/cli/run-managed.d.ts.map +1 -0
- package/dist/tools/cli/run-native.d.ts +9 -0
- package/dist/tools/cli/run-native.d.ts.map +1 -0
- package/dist/tools/cli/session.d.ts +40 -0
- package/dist/tools/cli/session.d.ts.map +1 -0
- package/dist/tools/{cli-signature.d.ts → cli/signature.d.ts} +2 -2
- package/dist/tools/cli/signature.d.ts.map +1 -0
- package/dist/tools/{cli-stream.d.ts → cli/stream.d.ts} +4 -4
- package/dist/tools/cli/stream.d.ts.map +1 -0
- package/dist/tools/{cli-update.d.ts → cli/update.d.ts} +3 -3
- package/dist/tools/cli/update.d.ts.map +1 -0
- package/dist/tools/{cli-view.d.ts → cli/view.d.ts} +2 -2
- package/dist/tools/cli/view.d.ts.map +1 -0
- package/dist/tools/{cli-wait.d.ts → cli/wait.d.ts} +2 -2
- package/dist/tools/cli/wait.d.ts.map +1 -0
- package/dist/tools/connections/runtime.d.ts +1 -1
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/connections/types.d.ts +3 -3
- package/dist/tools/connections/types.d.ts.map +1 -1
- package/dist/tools/durability-context.d.ts +1 -1
- package/dist/tools/durability-context.d.ts.map +1 -1
- package/dist/tools/execute.d.ts +30 -3
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +8 -6
- package/dist/tools/internal/surface-projector.d.ts.map +1 -1
- package/dist/tools/internal/tool-view.d.ts +41 -0
- package/dist/tools/internal/tool-view.d.ts.map +1 -0
- package/dist/tools/invoker.d.ts +11 -3
- package/dist/tools/invoker.d.ts.map +1 -1
- package/dist/tools/list-names.d.ts +3 -3
- package/dist/tools/list-names.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +2 -2
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/{mcp-app.d.ts → mcp/app.d.ts} +1 -1
- package/dist/tools/mcp/app.d.ts.map +1 -0
- package/dist/tools/{mcp-catalog.d.ts → mcp/catalog.d.ts} +2 -2
- package/dist/tools/mcp/catalog.d.ts.map +1 -0
- package/dist/tools/{mcp-handler.d.ts → mcp/handler.d.ts} +5 -5
- package/dist/tools/mcp/handler.d.ts.map +1 -0
- package/dist/tools/{mcp.d.ts → mcp/mount.d.ts} +9 -9
- package/dist/tools/mcp/mount.d.ts.map +1 -0
- package/dist/tools/{mcp-prepare.d.ts → mcp/prepare.d.ts} +9 -9
- package/dist/tools/mcp/prepare.d.ts.map +1 -0
- package/dist/tools/{mcp-progress.d.ts → mcp/progress.d.ts} +2 -2
- package/dist/tools/mcp/progress.d.ts.map +1 -0
- package/dist/tools/mcp/register.d.ts +35 -0
- package/dist/tools/mcp/register.d.ts.map +1 -0
- package/dist/tools/{mcp-round-policy.d.ts → mcp/round-policy.d.ts} +2 -2
- package/dist/tools/mcp/round-policy.d.ts.map +1 -0
- package/dist/tools/{mcp-round.d.ts → mcp/round.d.ts} +4 -4
- package/dist/tools/mcp/round.d.ts.map +1 -0
- package/dist/tools/mcp/runtime-tools.d.ts +24 -0
- package/dist/tools/mcp/runtime-tools.d.ts.map +1 -0
- package/dist/tools/{mcp-stdio-signals.d.ts → mcp/stdio-signals.d.ts} +3 -3
- package/dist/tools/mcp/stdio-signals.d.ts.map +1 -0
- package/dist/tools/{mcp-stdio.d.ts → mcp/stdio.d.ts} +4 -4
- package/dist/tools/mcp/stdio.d.ts.map +1 -0
- package/dist/tools/{mcp-trace.d.ts → mcp/trace.d.ts} +1 -1
- package/dist/tools/mcp/trace.d.ts.map +1 -0
- package/dist/tools/mount.d.ts +12 -7
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/native-definition.d.ts +3 -3
- package/dist/tools/native-definition.d.ts.map +1 -1
- package/dist/tools/operations/async-operation-binding-types.d.ts +127 -0
- package/dist/tools/operations/async-operation-binding-types.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-binding.d.ts +28 -0
- package/dist/tools/operations/async-operation-binding.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-canonical-types.d.ts +110 -0
- package/dist/tools/operations/async-operation-canonical-types.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-canonical.d.ts +7 -0
- package/dist/tools/operations/async-operation-canonical.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-contract.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-id.d.ts +23 -0
- package/dist/tools/operations/async-operation-id.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-runtime-tools.d.ts +40 -0
- package/dist/tools/operations/async-operation-runtime-tools.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-runtime-types.d.ts +68 -0
- package/dist/tools/operations/async-operation-runtime-types.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-runtime.d.ts +5 -0
- package/dist/tools/operations/async-operation-runtime.d.ts.map +1 -0
- package/dist/tools/{define-wait-tool.d.ts → operations/define-wait-tool.d.ts} +2 -2
- package/dist/tools/operations/define-wait-tool.d.ts.map +1 -0
- package/dist/tools/operations/mount-wait.d.ts.map +1 -0
- package/dist/tools/operations/wait-core.d.ts.map +1 -0
- package/dist/tools/remote.d.ts +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +11 -4
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/schema/coerce.d.ts.map +1 -0
- package/dist/tools/schema/flatten-join.d.ts.map +1 -0
- package/dist/tools/schema/flatten.d.ts.map +1 -0
- package/dist/tools/schema/portable-formats.d.ts.map +1 -0
- package/dist/tools/{presentation.d.ts → schema/presentation.d.ts} +2 -0
- package/dist/tools/schema/presentation.d.ts.map +1 -0
- package/dist/tools/schema/schema.d.ts.map +1 -0
- package/dist/tools/schema/untyped-properties.d.ts.map +1 -0
- package/dist/tools/surface.d.ts +2 -3
- package/dist/tools/surface.d.ts.map +1 -1
- package/dist/tools/toolkit.d.ts +4 -4
- package/dist/tools/toolkit.d.ts.map +1 -1
- package/dist/tools/{define-download-tool.d.ts → transfer/define-download-tool.d.ts} +4 -4
- package/dist/tools/transfer/define-download-tool.d.ts.map +1 -0
- package/dist/tools/{define-upload-tool.d.ts → transfer/define-upload-tool.d.ts} +3 -3
- package/dist/tools/transfer/define-upload-tool.d.ts.map +1 -0
- package/dist/tools/{define-view-file-tool.d.ts → transfer/define-view-file-tool.d.ts} +2 -2
- package/dist/tools/transfer/define-view-file-tool.d.ts.map +1 -0
- package/dist/tools/{download-core.d.ts → transfer/download-core.d.ts} +2 -2
- package/dist/tools/transfer/download-core.d.ts.map +1 -0
- package/dist/tools/{managed-file-error.d.ts → transfer/managed-file-error.d.ts} +1 -1
- package/dist/tools/transfer/managed-file-error.d.ts.map +1 -0
- package/dist/tools/{mount-download.d.ts → transfer/mount-download.d.ts} +1 -1
- package/dist/tools/transfer/mount-download.d.ts.map +1 -0
- package/dist/tools/{mount-upload.d.ts → transfer/mount-upload.d.ts} +1 -1
- package/dist/tools/transfer/mount-upload.d.ts.map +1 -0
- package/dist/tools/transfer/native-result.d.ts.map +1 -0
- package/dist/tools/{upload-core.d.ts → transfer/upload-core.d.ts} +1 -1
- package/dist/tools/transfer/upload-core.d.ts.map +1 -0
- package/dist/tools/transfer/view-file-contract.d.ts.map +1 -0
- package/dist/tools/{view-file.d.ts → transfer/view-file.d.ts} +1 -1
- package/dist/tools/transfer/view-file.d.ts.map +1 -0
- package/dist/tools/transports.d.ts +1 -1
- package/dist/tools/transports.d.ts.map +1 -1
- package/dist/tracking/contract.d.ts +1 -1
- package/dist/tracking/contract.d.ts.map +1 -1
- package/llms/agent-runtime-browser.txt +33 -0
- package/llms/agent-runtime-coding-tools.txt +58 -0
- package/llms/agent-runtime-harness.txt +45 -0
- package/llms/agent-runtime-openrouter.txt +22 -0
- package/llms/agent-runtime-sandbox.txt +159 -0
- package/llms/agent-runtime-sqlite-bun.txt +29 -0
- package/llms/agent-runtime-sqlite-node.txt +26 -0
- package/llms/agent-runtime-testing.txt +30 -0
- package/llms/agent-runtime.2.txt +550 -0
- package/llms/agent-runtime.3.txt +311 -0
- package/llms/agent-runtime.txt +893 -0
- package/llms/application-diagnostic-journal.txt +30 -0
- package/llms/application-grammy.txt +27 -0
- package/llms/application-opentelemetry.txt +27 -0
- package/llms/application-schemas.txt +42 -0
- package/llms/application.2.txt +223 -0
- package/llms/application.txt +1144 -0
- package/llms/cli.txt +977 -0
- package/llms/contract.txt +606 -0
- package/llms/declaration.txt +286 -0
- package/llms/files.txt +30 -0
- package/llms/geo.txt +114 -0
- package/llms/google.txt +27 -0
- package/llms/live.txt +471 -0
- package/llms/node.txt +43 -0
- package/llms/oauth.txt +136 -0
- package/llms/observability.txt +893 -0
- package/llms/primitives.txt +158 -0
- package/llms/react.txt +184 -0
- package/llms/release.txt +176 -0
- package/llms/remote.txt +25 -0
- package/llms/server.2.txt +361 -0
- package/llms/server.3.txt +1031 -0
- package/llms/server.4.txt +905 -0
- package/llms/server.5.txt +250 -0
- package/llms/server.txt +996 -0
- package/llms/stitchkit.2.txt +836 -0
- package/llms/stitchkit.3.txt +147 -0
- package/llms/stitchkit.txt +260 -0
- package/llms/telegram.txt +30 -0
- package/llms/testing.txt +676 -0
- package/llms/tools-connections.txt +45 -0
- package/llms/tools-contract.txt +9 -0
- package/llms/tools-invoker.txt +9 -0
- package/llms/tools.2.txt +838 -0
- package/llms/tools.txt +1004 -0
- package/llms/tracking-server.txt +32 -0
- package/llms/tracking.txt +263 -0
- package/llms/upgrading-0.37.0-0.50.0.txt +947 -0
- package/llms/upgrading-0.53.0-0.67.0.txt +1174 -0
- package/llms/upgrading-0.68.0-0.94.0.txt +1159 -0
- package/llms/upgrading.txt +215 -0
- package/llms-full.txt +214 -75
- package/llms.txt +54 -5
- package/package.json +84 -83
- package/dist/agent-runtime/durability-contract.d.ts.map +0 -1
- package/dist/agent-runtime/durability-json.d.ts.map +0 -1
- package/dist/agent-runtime/durability-ledger.d.ts.map +0 -1
- package/dist/agent-runtime/durability-scheduler.d.ts.map +0 -1
- package/dist/agent-runtime/durability.d.ts.map +0 -1
- package/dist/agent-runtime/store-driver.d.ts.map +0 -1
- package/dist/agent-runtime-browser.d.ts +0 -5
- package/dist/agent-runtime-browser.d.ts.map +0 -1
- package/dist/agent-runtime-coding-tools.d.ts.map +0 -1
- package/dist/agent-runtime-coding-tools.js +0 -23
- package/dist/agent-runtime-harness.d.ts.map +0 -1
- package/dist/agent-runtime-openrouter.d.ts.map +0 -1
- package/dist/agent-runtime-sandbox.d.ts.map +0 -1
- package/dist/agent-runtime-sqlite-bun.d.ts.map +0 -1
- package/dist/agent-runtime-sqlite-node.d.ts.map +0 -1
- package/dist/agent-runtime-testing.d.ts +0 -3
- package/dist/agent-runtime-testing.d.ts.map +0 -1
- package/dist/agent-runtime-testing.js +0 -156
- package/dist/agent-runtime.d.ts.map +0 -1
- package/dist/application/channel.d.ts.map +0 -1
- package/dist/application-diagnostic-journal.d.ts.map +0 -1
- package/dist/application-grammy.d.ts.map +0 -1
- package/dist/application-opentelemetry.d.ts.map +0 -1
- package/dist/application-schemas.d.ts.map +0 -1
- package/dist/application.d.ts.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/contract/index.d.ts +0 -8
- package/dist/contract/index.d.ts.map +0 -1
- package/dist/declaration.d.ts.map +0 -1
- package/dist/files.d.ts.map +0 -1
- package/dist/geo.d.ts.map +0 -1
- package/dist/google.d.ts.map +0 -1
- package/dist/index-0j3vcp5m.js +0 -28
- package/dist/index-a11g2kry.js +0 -866
- package/dist/index-cnyk6te3.js +0 -77
- package/dist/index-fadnxwna.js +0 -1046
- package/dist/index-hrbajay7.js +0 -1196
- package/dist/index-s8cnx02s.js +0 -90
- package/dist/index-v5573g1k.js +0 -142
- package/dist/index-vk3kq9sb.js +0 -3344
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/errors.d.ts.map +0 -1
- package/dist/internal/observability-sink.d.ts.map +0 -1
- package/dist/live.d.ts.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/oauth.d.ts.map +0 -1
- package/dist/observability/index.d.ts.map +0 -1
- package/dist/primitives.d.ts +0 -3
- package/dist/primitives.d.ts.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/release.d.ts.map +0 -1
- package/dist/remote.d.ts.map +0 -1
- package/dist/remote.js +0 -88
- package/dist/retained.d.ts.map +0 -1
- package/dist/server/file-state-store.d.ts.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/request.d.ts.map +0 -1
- package/dist/telegram.d.ts.map +0 -1
- package/dist/testing/agent-store-conformance.d.ts.map +0 -1
- package/dist/testing.d.ts +0 -39
- package/dist/testing.d.ts.map +0 -1
- package/dist/tool-invoker.d.ts.map +0 -1
- package/dist/tool-invoker.js +0 -21
- package/dist/tools/async-operation-contract.d.ts.map +0 -1
- package/dist/tools/async-operation.d.ts +0 -332
- package/dist/tools/async-operation.d.ts.map +0 -1
- package/dist/tools/cli-args.d.ts.map +0 -1
- package/dist/tools/cli-checkpoint.d.ts.map +0 -1
- package/dist/tools/cli-command.d.ts.map +0 -1
- package/dist/tools/cli-format.d.ts.map +0 -1
- package/dist/tools/cli-installer.d.ts.map +0 -1
- package/dist/tools/cli-invoke.d.ts.map +0 -1
- package/dist/tools/cli-manifest.d.ts.map +0 -1
- package/dist/tools/cli-policy.d.ts.map +0 -1
- package/dist/tools/cli-profile.d.ts.map +0 -1
- package/dist/tools/cli-signature.d.ts.map +0 -1
- package/dist/tools/cli-stream.d.ts.map +0 -1
- package/dist/tools/cli-update.d.ts.map +0 -1
- package/dist/tools/cli-view.d.ts.map +0 -1
- package/dist/tools/cli-wait.d.ts.map +0 -1
- package/dist/tools/cli.d.ts.map +0 -1
- package/dist/tools/coerce.d.ts.map +0 -1
- package/dist/tools/connections/index.d.ts.map +0 -1
- package/dist/tools/define-download-tool.d.ts.map +0 -1
- package/dist/tools/define-upload-tool.d.ts.map +0 -1
- package/dist/tools/define-view-file-tool.d.ts.map +0 -1
- package/dist/tools/define-wait-tool.d.ts.map +0 -1
- package/dist/tools/download-core.d.ts.map +0 -1
- package/dist/tools/flatten-join.d.ts.map +0 -1
- package/dist/tools/flatten.d.ts.map +0 -1
- package/dist/tools/json-schema-dialect.d.ts.map +0 -1
- package/dist/tools/json-schema.d.ts.map +0 -1
- package/dist/tools/managed-file-error.d.ts.map +0 -1
- package/dist/tools/mcp-app.d.ts.map +0 -1
- package/dist/tools/mcp-catalog.d.ts.map +0 -1
- package/dist/tools/mcp-handler.d.ts.map +0 -1
- package/dist/tools/mcp-prepare.d.ts.map +0 -1
- package/dist/tools/mcp-progress.d.ts.map +0 -1
- package/dist/tools/mcp-round-policy.d.ts.map +0 -1
- package/dist/tools/mcp-round.d.ts.map +0 -1
- package/dist/tools/mcp-stdio-signals.d.ts.map +0 -1
- package/dist/tools/mcp-stdio.d.ts.map +0 -1
- package/dist/tools/mcp-trace.d.ts.map +0 -1
- package/dist/tools/mcp.d.ts.map +0 -1
- package/dist/tools/mount-download.d.ts.map +0 -1
- package/dist/tools/mount-upload.d.ts.map +0 -1
- package/dist/tools/mount-wait.d.ts.map +0 -1
- package/dist/tools/native-mcp.d.ts +0 -20
- package/dist/tools/native-mcp.d.ts.map +0 -1
- package/dist/tools/native-result.d.ts.map +0 -1
- package/dist/tools/oauth-metadata.d.ts.map +0 -1
- package/dist/tools/oauth-provider.d.ts.map +0 -1
- package/dist/tools/portable-formats.d.ts.map +0 -1
- package/dist/tools/presentation.d.ts.map +0 -1
- package/dist/tools/schema.d.ts.map +0 -1
- package/dist/tools/untyped-properties.d.ts.map +0 -1
- package/dist/tools/upload-core.d.ts.map +0 -1
- package/dist/tools/view-file-contract.d.ts.map +0 -1
- package/dist/tools/view-file.d.ts.map +0 -1
- package/dist/tools/wait-core.d.ts.map +0 -1
- package/dist/tools-contract.d.ts.map +0 -1
- package/dist/tools.d.ts +0 -50
- package/dist/tools.d.ts.map +0 -1
- package/dist/tracking-server.d.ts.map +0 -1
- package/dist/tracking.d.ts.map +0 -1
- package/dist/upgrade-cli.d.ts.map +0 -1
- package/dist/{retained.d.ts → browser/retained.d.ts} +0 -0
- package/dist/{declaration.d.ts → declaration/declaration.d.ts} +0 -0
- package/dist/{upgrade-cli.d.ts → entrypoints/bin/upgrade-cli.d.ts} +0 -0
- package/dist/{tracking-server.js → entrypoints/tracking/server.js} +0 -0
- package/dist/{google.d.ts → google/google.d.ts} +0 -0
- package/dist/{index-pq95qkbf.js → index-5jm36zv1.js} +0 -0
- package/dist/{index-actkayzn.js → index-70yj2ebz.js} +3 -3
- /package/dist/{application → internal}/channel.d.ts +0 -0
- /package/dist/{logger.d.ts → internal/logger.d.ts} +0 -0
- /package/dist/{server → internal}/request.d.ts +0 -0
- /package/dist/{tools → json-schema}/json-schema.d.ts +0 -0
- /package/dist/tools/{async-operation-contract.d.ts → operations/async-operation-contract.d.ts} +0 -0
- /package/dist/tools/{mount-wait.d.ts → operations/mount-wait.d.ts} +0 -0
- /package/dist/tools/{wait-core.d.ts → operations/wait-core.d.ts} +0 -0
- /package/dist/tools/{coerce.d.ts → schema/coerce.d.ts} +0 -0
- /package/dist/tools/{flatten-join.d.ts → schema/flatten-join.d.ts} +0 -0
- /package/dist/tools/{flatten.d.ts → schema/flatten.d.ts} +0 -0
- /package/dist/tools/{portable-formats.d.ts → schema/portable-formats.d.ts} +0 -0
- /package/dist/tools/{schema.d.ts → schema/schema.d.ts} +0 -0
- /package/dist/tools/{untyped-properties.d.ts → schema/untyped-properties.d.ts} +0 -0
- /package/dist/tools/{native-result.d.ts → transfer/native-result.d.ts} +0 -0
- /package/dist/tools/{view-file-contract.d.ts → transfer/view-file-contract.d.ts} +0 -0
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
# stitchkit/application — stitchkit docs slice, part 1 of 2
|
|
2
|
+
|
|
3
|
+
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
|
+
> `llms.txt` in this package.
|
|
5
|
+
> Continues in `llms/application.2.txt`.
|
|
6
|
+
|
|
7
|
+
Parts of this slice:
|
|
8
|
+
- `llms/application.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md) · Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
9
|
+
- `llms/application.2.txt`: API reference: `stitchkit/application` (docs/api/reference.md)
|
|
10
|
+
|
|
11
|
+
==============================================================================
|
|
12
|
+
# Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
13
|
+
==============================================================================
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
title: Managed application kernel
|
|
17
|
+
description: Compose process-local resources, schedules, readiness, drain and optional provider adapters without building a second job platform.
|
|
18
|
+
type: architecture
|
|
19
|
+
status: active
|
|
20
|
+
created: 2026-08-23
|
|
21
|
+
updated: 2026-09-04 14:11 +07:00
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Managed application kernel
|
|
25
|
+
|
|
26
|
+
> **Maturity: evolving.** This surface is still finding its shape and may be
|
|
27
|
+
> redefined in any minor release — always with a `### ⚠️ Breaking changes` entry
|
|
28
|
+
> and a migration section, never silently. The server, contract and client
|
|
29
|
+
> surfaces it composes are stable.
|
|
30
|
+
|
|
31
|
+
Use `stitchkit/application` when several process-local resources must become
|
|
32
|
+
ready and shut down as one application. Keep using the lower-level server and
|
|
33
|
+
signal APIs when one managed server is already the complete lifecycle boundary.
|
|
34
|
+
|
|
35
|
+
The neutral entrypoint is server-only and works on Bun and Node ≥ 22. It does
|
|
36
|
+
not import provider SDKs.
|
|
37
|
+
|
|
38
|
+
For complete database, poller, queue-consumer and operational-publisher
|
|
39
|
+
cutovers, continue with the executable
|
|
40
|
+
[application migration recipes](./application-migration-recipes.md).
|
|
41
|
+
|
|
42
|
+
## Minimal composition
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import {
|
|
46
|
+
createApplication,
|
|
47
|
+
createManagedSchedule,
|
|
48
|
+
defineManagedResource,
|
|
49
|
+
managedServerResource,
|
|
50
|
+
} from 'stitchkit/application'
|
|
51
|
+
import { bindProcessSignals } from 'stitchkit/server'
|
|
52
|
+
|
|
53
|
+
const database = defineManagedResource({
|
|
54
|
+
id: 'database',
|
|
55
|
+
required: true,
|
|
56
|
+
start: async ({ signal, reportHealth }) => {
|
|
57
|
+
await db.connect({ signal })
|
|
58
|
+
reportHealth('healthy')
|
|
59
|
+
},
|
|
60
|
+
close: () => db.disconnect(),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const cleanup = createManagedSchedule({
|
|
64
|
+
id: 'cleanup',
|
|
65
|
+
dependsOn: ['database'],
|
|
66
|
+
everyMs: 60_000,
|
|
67
|
+
startAfterMs: 60_000,
|
|
68
|
+
overlap: { mode: 'skip' },
|
|
69
|
+
errorPolicy: 'continue',
|
|
70
|
+
run: ({ signal }) => removeExpiredRecords(signal),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const http = managedServerResource({
|
|
74
|
+
id: 'http',
|
|
75
|
+
dependsOn: [database],
|
|
76
|
+
// Called during `start`, after `database` is ready. The factory receives the
|
|
77
|
+
// same declared-dependency and startup-signal context as any resource.
|
|
78
|
+
server: (context) => {
|
|
79
|
+
const db = context.use(database)
|
|
80
|
+
return createServer({ port: env.PORT, services: createServices(db) })
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const app = createApplication({
|
|
85
|
+
id: 'service',
|
|
86
|
+
resources: [database, http, cleanup],
|
|
87
|
+
// One number for how long this application may take to stop. `shutdown()`
|
|
88
|
+
// with no options and the signal path both spend it.
|
|
89
|
+
shutdown: { gracePeriodMs: 30_000, forceTimeoutMs: 5_000 },
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const signals = bindProcessSignals(app, {
|
|
93
|
+
onComplete: (result) => {
|
|
94
|
+
process.exitCode = result.outcome === 'clean' ? 0 : 1
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
await app.start()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Bind before you start, and the order is load-bearing.** Reading it the other way
|
|
102
|
+
round — start serving, then arrange for shutdown — leaves a window between the
|
|
103
|
+
first accepted request and the first listener, and a supervisor that sends
|
|
104
|
+
`SIGTERM` inside that window kills the process outright: exit 143, no shutdown
|
|
105
|
+
chain, no `onComplete`, nothing drained. The window is small and opens exactly
|
|
106
|
+
when the machine is busiest, which is when a supervisor is most likely to be
|
|
107
|
+
restarting something. `bindProcessSignals` does not start anything, so there is
|
|
108
|
+
no cost to binding first.
|
|
109
|
+
|
|
110
|
+
The exact resource callbacks are typed by their public configuration. The
|
|
111
|
+
important ownership rule is stable: the application decides what the
|
|
112
|
+
database/server/provider objects are and how they are configured; Stitchkit
|
|
113
|
+
orders their process-local lifecycle and decides *when* they come into
|
|
114
|
+
existence.
|
|
115
|
+
|
|
116
|
+
## Resource authoring
|
|
117
|
+
|
|
118
|
+
Give every resource a stable, bounded ID and list its dependencies explicitly.
|
|
119
|
+
The graph is validated before any start. Required resources may depend only on
|
|
120
|
+
required resources; an optional integration cannot silently become a required
|
|
121
|
+
dependency.
|
|
122
|
+
|
|
123
|
+
Start and readiness are different. A connection can finish setup and return a
|
|
124
|
+
ready handle. A poller can return a handle immediately, settle `ready` later,
|
|
125
|
+
and keep a long-lived `completion` promise. Stitchkit observes both.
|
|
126
|
+
|
|
127
|
+
Once Stitchkit invokes start/activation, cleanup is guaranteed to be attempted.
|
|
128
|
+
Write `close` so it is safe when setup was partial:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
let cacheClient: CacheClient | undefined
|
|
132
|
+
|
|
133
|
+
const cache = defineManagedResource({
|
|
134
|
+
id: 'cache',
|
|
135
|
+
required: false,
|
|
136
|
+
start: async ({ signal, reportHealth }) => {
|
|
137
|
+
cacheClient = new CacheClient()
|
|
138
|
+
await cacheClient.connect(signal)
|
|
139
|
+
reportHealth('healthy')
|
|
140
|
+
},
|
|
141
|
+
close: async () => {
|
|
142
|
+
await cacheClient?.close()
|
|
143
|
+
cacheClient = undefined
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Invoking `start` makes the descriptor rollback-eligible immediately. Its
|
|
149
|
+
`close` callback must therefore clean side effects even when `start` rejects
|
|
150
|
+
before returning a runtime handle. The kernel calls cleanup once and continues
|
|
151
|
+
cleaning other attempted resources even if one close fails.
|
|
152
|
+
|
|
153
|
+
### Handing a resource to the resources that depend on it
|
|
154
|
+
|
|
155
|
+
`dependsOn` says *when*. To say *what*, return a `value` from `start` and read
|
|
156
|
+
it with `context.use(...)`:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const database = defineManagedResource({
|
|
160
|
+
id: 'database',
|
|
161
|
+
start: async ({ signal }) => ({ value: await connect(env.DATABASE_URL, signal) }),
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const worker = defineManagedResource({
|
|
165
|
+
id: 'worker',
|
|
166
|
+
dependsOn: [database],
|
|
167
|
+
start: (context) => {
|
|
168
|
+
const db = context.use(database) // Connection — not Connection | null
|
|
169
|
+
return { completion: consume(db, context.signal) }
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Declare the dependency with the **resource**, not its id, whenever you intend to
|
|
175
|
+
read from it: that is the form `use` can type, and it keeps the declaration and
|
|
176
|
+
the read from drifting apart. A string still works when all you need is order.
|
|
177
|
+
|
|
178
|
+
`managedServerResource` follows the same rule. Its factory receives this context
|
|
179
|
+
during `start`, so constructing routes from a database/service/socket value does
|
|
180
|
+
not need an outer mutable handoff or a custom `start` override. A zero-argument
|
|
181
|
+
factory remains valid when it needs only ordering, and an already-running handle
|
|
182
|
+
can still be passed directly. → ADR 0121.
|
|
183
|
+
|
|
184
|
+
The value is published when `start` resolves and stays readable for the rest of
|
|
185
|
+
the application's life — from `activate` and from the shutdown phases too, where
|
|
186
|
+
a dependant may still need the handle it was given.
|
|
187
|
+
|
|
188
|
+
`use` refuses two things, both loudly: a resource that was never declared in
|
|
189
|
+
`dependsOn` (it happens to work whenever declaration order is lucky), and a
|
|
190
|
+
resource that published nothing. The second is refused by the compiler as well —
|
|
191
|
+
reading a value off a resource with no `value` in its `start` does not type.
|
|
192
|
+
|
|
193
|
+
## Readiness and health
|
|
194
|
+
|
|
195
|
+
`await app.start()` resolves only after every required resource is ready and
|
|
196
|
+
every required post-ready activation has succeeded. Lifecycle `ready` is
|
|
197
|
+
published before activation so schedules never arm during `starting`;
|
|
198
|
+
application admission opens only after successful activation. Optional failure
|
|
199
|
+
may retain lifecycle `ready`, but snapshot health is `degraded`, never
|
|
200
|
+
`healthy`.
|
|
201
|
+
|
|
202
|
+
Readiness is not hidden polling. A resource reports health changes through its
|
|
203
|
+
lifecycle context; the application decides when a database/provider probe runs.
|
|
204
|
+
|
|
205
|
+
**A `reportHealth` call inside `start` is kept.** A resource that says nothing
|
|
206
|
+
is assumed healthy once it is ready; one that reports its own health has already
|
|
207
|
+
answered the question, and the answer stands. (It used to be overwritten — and
|
|
208
|
+
the example above hides that, because `healthy` is the same value that
|
|
209
|
+
overwrote it.)
|
|
210
|
+
|
|
211
|
+
**A resource is required unless you write `required: false`.** That default is
|
|
212
|
+
what makes the next sentence bite.
|
|
213
|
+
|
|
214
|
+
**Readiness requires every required resource to be healthy**, so "ready but
|
|
215
|
+
degraded" is unreachable for a required resource by construction: an
|
|
216
|
+
application whose required resource reports anything but `healthy` refuses to
|
|
217
|
+
start, and says which resource and in what state. The refusal distinguishes the
|
|
218
|
+
two ways to get there — a resource that was never healthy is pointed at
|
|
219
|
+
`required: false`; one that was healthy and stopped is pointed at
|
|
220
|
+
`onResourceFailure`. A resource that is *expected* to start
|
|
221
|
+
degraded — up, but still dialling something external — belongs behind
|
|
222
|
+
`required: false`, where it keeps its own health and does not gate the
|
|
223
|
+
application:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
defineManagedResource({
|
|
227
|
+
id: 'dialling',
|
|
228
|
+
required: false,
|
|
229
|
+
start: ({ reportHealth }) => { reportHealth('degraded') },
|
|
230
|
+
})
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
An optional resource reporting non-healthy does not gate readiness, but it does
|
|
234
|
+
move the application **aggregate** to `degraded` — which a readiness endpoint
|
|
235
|
+
mapping `degraded` to non-200 will notice.
|
|
236
|
+
|
|
237
|
+
If startup fails, every resource that was already started is closed in reverse
|
|
238
|
+
order. The rollback runs **one** phase — `close` — not the five a real shutdown
|
|
239
|
+
runs, and `close` receives the same deadlines a shutdown would give it, so a
|
|
240
|
+
server drains what is in flight instead of aborting it.
|
|
241
|
+
|
|
242
|
+
Those deadlines come from the application's declared budget, and it is worth
|
|
243
|
+
knowing what they cost. With nothing in flight the rollback returns at once: a
|
|
244
|
+
grace period is a ceiling, not a sleep. With something in flight it waits for
|
|
245
|
+
it — that is the point — and with something that **never finishes**, a hung
|
|
246
|
+
upstream or a client that ignores a close frame, it waits out the whole budget
|
|
247
|
+
before forcing. Under the default 30s+5s that turns a failed `start()` that used
|
|
248
|
+
to reject in milliseconds into one that can take 35 seconds to reject.
|
|
249
|
+
|
|
250
|
+
An application that would rather hear about a broken start immediately says so,
|
|
251
|
+
in the one place both stopping paths read:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
createApplication({
|
|
255
|
+
id: 'app',
|
|
256
|
+
resources,
|
|
257
|
+
// Applies to `shutdown()` called with no options AND to the rollback of a
|
|
258
|
+
// failed `start()`, which has no call site of its own to be told.
|
|
259
|
+
shutdown: { gracePeriodMs: 5_000, forceTimeoutMs: 1_000 },
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The budget is a real bound, not just a number handed to each resource: a `close`
|
|
264
|
+
that never returns is abandoned when the budget runs out, reported as a `close`
|
|
265
|
+
failure, and the startup error stays the `cause` of the `AggregateError` that
|
|
266
|
+
`start()` rejects with. Without that, one unresponsive resource could keep a
|
|
267
|
+
failed startup from ever reporting why it failed.
|
|
268
|
+
|
|
269
|
+
A required long-lived completion that rejects after startup makes readiness
|
|
270
|
+
false and health unhealthy. Stitchkit records the failure but does not restart
|
|
271
|
+
the resource or process.
|
|
272
|
+
|
|
273
|
+
`ApplicationResourceShutdown.failures` names the phase that failed — `start`,
|
|
274
|
+
`ready`, `completion`, `admission`, `drain`, `close`, `force` — and nothing
|
|
275
|
+
else. To learn *why*, pass `onResourceFailure`:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
createApplication({
|
|
279
|
+
id: 'app',
|
|
280
|
+
resources,
|
|
281
|
+
onResourceFailure: ({ resourceId, phase, error }) => {
|
|
282
|
+
log.error({ resourceId, phase, err: error }, 'managed resource failed')
|
|
283
|
+
},
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
It fires for every phase, including the one place the cause would otherwise be
|
|
288
|
+
lost entirely: an **optional** resource failing to start. A required one
|
|
289
|
+
rethrows, so its cause reaches you anyway; an optional one is swallowed on
|
|
290
|
+
purpose, because the application keeps running. A throwing observer cannot
|
|
291
|
+
break the lifecycle it observes.
|
|
292
|
+
|
|
293
|
+
`createApplicationHealthHandler` publishes an `ApplicationStatusProjection`
|
|
294
|
+
through a Fetch-compatible handler, suitable for a raw route on Bun or Node.
|
|
295
|
+
The projection carries the verdict — the application's own `id`, `lifecycle`,
|
|
296
|
+
`health`, `ready`, `capturedAt` and resource **counts** — and never the internal
|
|
297
|
+
topology: no per-resource ids, no `dependsOn` edges, no process `epoch`, no
|
|
298
|
+
admission counters.
|
|
299
|
+
Those stay in `getSnapshot()`, which never leaves the process, and in whatever
|
|
300
|
+
telemetry you wire to it. Product-specific probes may be composed beside it; do
|
|
301
|
+
not put secrets or raw provider failures in the response.
|
|
302
|
+
|
|
303
|
+
For the conventional three-route surface, reuse the same semantics instead of
|
|
304
|
+
copying them into the application:
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
const operational = createApplicationOperationalHandlers(app)
|
|
308
|
+
|
|
309
|
+
const rawRoutes = [
|
|
310
|
+
{ method: 'GET', path: '/status', handler: operational.status },
|
|
311
|
+
{ method: 'GET', path: '/ready', handler: operational.readiness },
|
|
312
|
+
{ method: 'GET', path: '/live', handler: operational.liveness },
|
|
313
|
+
]
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`status` always returns the current published projection with HTTP 200,
|
|
317
|
+
including while starting, draining or stopped. To read the full snapshot — every
|
|
318
|
+
resource, its dependency edges and the live counters — call `app.getSnapshot()`
|
|
319
|
+
in-process; there is no option that publishes it over HTTP, because these routes
|
|
320
|
+
are meant to be reachable. The two probes retain the existing
|
|
321
|
+
readiness/liveness status and `Retry-After` policy.
|
|
322
|
+
|
|
323
|
+
Applications that already own an OpenTelemetry SDK may inject its `Meter` into
|
|
324
|
+
the isolated adapter:
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
import { createApplicationOpenTelemetry } from 'stitchkit/application/opentelemetry'
|
|
328
|
+
|
|
329
|
+
const telemetry = createApplicationOpenTelemetry({
|
|
330
|
+
meter,
|
|
331
|
+
application: app,
|
|
332
|
+
activities: [activity],
|
|
333
|
+
schedules: [schedule],
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// During application cleanup:
|
|
337
|
+
telemetry.close()
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
The adapter registers fixed observable gauges and pulls the latest canonical
|
|
341
|
+
snapshots on every collection. It owns no exporter, SDK lifecycle, cache,
|
|
342
|
+
subscription or polling loop. Attributes are limited to declared application,
|
|
343
|
+
resource, activity, stage and schedule IDs plus bounded framework states;
|
|
344
|
+
epoch, revision, timestamps, failures and product/provider identities are never
|
|
345
|
+
metric attributes. Install `@opentelemetry/api` only when this entrypoint is
|
|
346
|
+
used; the neutral `stitchkit/application` graph remains peer-free.
|
|
347
|
+
|
|
348
|
+
Every instrument uses unit `1` and reports an absolute current/lifetime value:
|
|
349
|
+
|
|
350
|
+
| Instruments | Meaning |
|
|
351
|
+
|---|---|
|
|
352
|
+
| `stitchkit.application.lifecycle`, `.ready` | current lifecycle/health fact and readiness |
|
|
353
|
+
| `stitchkit.application.admission.accepting`, `.accepted`, `.completed`, `.pending` | current gate and absolute lifetime admission counts |
|
|
354
|
+
| `stitchkit.application.resource.ready` | readiness for each declared resource with required/state/health attributes |
|
|
355
|
+
| `stitchkit.application.schedule.accepting`, `.active`, `.queued`, `.runs_started`, `.runs_completed`, `.runs_failed`, `.ticks_skipped` | current schedule state and absolute run/tick counts |
|
|
356
|
+
| `stitchkit.application.activity.active`, `.queued`, `.completed`, `.failed` | absolute stage projections for declared activity sources |
|
|
357
|
+
|
|
358
|
+
## Replacing part of the graph without stopping the process
|
|
359
|
+
|
|
360
|
+
`restart` takes down one resource **and everything that depends on it**, then
|
|
361
|
+
brings that subtree back. Everything else keeps running, and the process epoch
|
|
362
|
+
does not move.
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
const result = await app.restart({ resourceId: 'database' })
|
|
366
|
+
|
|
367
|
+
result.outcome // 'restarted' | 'failed' | 'refused'
|
|
368
|
+
result.affected // ['database', 'repository', 'api'] — in start order
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The dependants come down with it because they are holding what the resource
|
|
372
|
+
published. A repository that kept running across a database replacement holds a
|
|
373
|
+
pool that has been closed: it still typechecks, still has methods, and fails at
|
|
374
|
+
whatever moment the first call happens. There is no version of this where a
|
|
375
|
+
dependant keeps a live handle, so the subtree — not the resource — is the unit.
|
|
376
|
+
|
|
377
|
+
A leaf restart affects one resource, and an independent neighbour is never
|
|
378
|
+
touched:
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
await app.restart({ resourceId: 'cache' })
|
|
382
|
+
// mailer and database were not stopped, not started, not activated
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Refused is not failed.** An unknown id, a restart during shutdown, and a
|
|
386
|
+
restart before the application is ready all return `refused` with a reason and
|
|
387
|
+
touch nothing. `failed` means the subtree came down and the new generation did
|
|
388
|
+
not come up — and the result agrees with the snapshot: if any affected resource
|
|
389
|
+
ends the restart in `failed`, so does the restart, including an optional one
|
|
390
|
+
whose failure the start loop does not re-throw.
|
|
391
|
+
|
|
392
|
+
**The close phase is bounded**, by the application's shutdown budget or by one
|
|
393
|
+
this call names:
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
await app.restart({ resourceId: 'database', gracePeriodMs: 5_000, forceTimeoutMs: 2_000 })
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**A resource has to be able to start twice.** The kernel calls `start` again, so
|
|
400
|
+
a resource holding state across its own lifetime rebuilds it there. The ones this
|
|
401
|
+
framework ships do — a schedule re-arms, a keyspace opens a new generation and
|
|
402
|
+
re-loads, a managed server built from a factory gets a fresh server. A managed
|
|
403
|
+
server given a server *instance* cannot, and says so by name rather than
|
|
404
|
+
republishing something it has shut down. Give a keyspace a
|
|
405
|
+
`backend: () => …` factory when its backend cannot be re-opened after `close()`.
|
|
406
|
+
|
|
407
|
+
→ [ADR 0157](../decisions/0157-a-restartable-resource-begins-a-generation.md).
|
|
408
|
+
|
|
409
|
+
Restarts of overlapping subtrees queue behind each other rather than being
|
|
410
|
+
refused. Two callers asking at once is ordinary; two generations of one resource
|
|
411
|
+
alive at once is the thing this must make impossible.
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
// Both succeed, one complete pass after the other.
|
|
415
|
+
await Promise.all([
|
|
416
|
+
app.restart({ resourceId: 'database' }),
|
|
417
|
+
app.restart({ resourceId: 'database' }),
|
|
418
|
+
])
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
What a restart is **not** is a process restart: it replaces resources, and does
|
|
422
|
+
not re-read configuration the kernel captured when it was constructed.
|
|
423
|
+
|
|
424
|
+
→ [ADR 0154](../decisions/0154-the-unit-of-a-restart-is-the-subtree.md).
|
|
425
|
+
|
|
426
|
+
## Decisions a policy set makes together
|
|
427
|
+
|
|
428
|
+
`createDecisionPipeline` runs an ordered list of policies that each vote
|
|
429
|
+
`allow`, `deny` (with a reason) or `defer`. The first terminal verdict wins and
|
|
430
|
+
the rest do not run.
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
import { createDecisionPipeline } from 'stitchkit/application'
|
|
434
|
+
|
|
435
|
+
const pipeline = createDecisionPipeline<{ userId: string; scope: string }>([
|
|
436
|
+
{ id: 'banned', decide: (r) => (isBanned(r.userId)
|
|
437
|
+
? { outcome: 'deny', reason: 'account suspended' }
|
|
438
|
+
: { outcome: 'defer' }) },
|
|
439
|
+
{ id: 'scope', decide: (r) => (r.scope === 'admin'
|
|
440
|
+
? { outcome: 'allow' }
|
|
441
|
+
: { outcome: 'defer' }) },
|
|
442
|
+
{ id: 'default', decide: () => ({ outcome: 'deny', reason: 'no policy allowed this' }) },
|
|
443
|
+
], { policyTimeoutMs: 2_000 })
|
|
444
|
+
|
|
445
|
+
const result = await pipeline.decide(request)
|
|
446
|
+
result.outcome // 'allow' | 'deny' — never 'defer'
|
|
447
|
+
result.trace // the policies that actually ran, in order
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Three things are deliberate.
|
|
451
|
+
|
|
452
|
+
**A deny carries a reason, by schema.** `{ outcome: 'deny' }` alone does not
|
|
453
|
+
typecheck. A refusal whose cause exists only in the log of whoever refused is a
|
|
454
|
+
support ticket.
|
|
455
|
+
|
|
456
|
+
**The trace is what ran, not what was declared.** It stops at the terminal
|
|
457
|
+
verdict. When something was denied the question is which policy denied it and
|
|
458
|
+
what the ones before it said, and a trace listing policies that never ran would
|
|
459
|
+
answer that wrongly while looking complete.
|
|
460
|
+
|
|
461
|
+
**Every policy deferring raises.** `DecisionUndecidedError`, not a default —
|
|
462
|
+
defaulting to `allow` turns an incomplete policy set into an open door, and
|
|
463
|
+
defaulting to `deny` turns it into an outage whose cause reads as a legitimate
|
|
464
|
+
refusal.
|
|
465
|
+
|
|
466
|
+
**A policy that does not answer raises too.** A non-decision, a throw, and running
|
|
467
|
+
past `policyTimeoutMs` are one error — `DecisionPolicyError`, naming the policy
|
|
468
|
+
and carrying the trace so far — because they are indistinguishable downstream and
|
|
469
|
+
handling one but not the others is how a broken policy becomes a skipped one. The
|
|
470
|
+
chain stops there rather than falling through to whatever the next policy would
|
|
471
|
+
have said.
|
|
472
|
+
|
|
473
|
+
`policyTimeoutMs` is required and has no default. Pick a number your slowest
|
|
474
|
+
policy comfortably beats. The framework will not choose it for you: a policy that
|
|
475
|
+
never settles hangs every caller of the operation it guards, and a default here
|
|
476
|
+
is a number nobody chose applied to code the framework has never seen.
|
|
477
|
+
|
|
478
|
+
The same `allow`/`deny`/`defer` type is what an event topic declared
|
|
479
|
+
`mode: 'decision'` uses in `stitchkit/live` — one vocabulary, because a listener
|
|
480
|
+
voting on an event and a policy voting on a request are answering the same
|
|
481
|
+
question.
|
|
482
|
+
|
|
483
|
+
→ [ADR 0155](../decisions/0155-one-decision-vocabulary-and-an-unanswered-question-is-an-error.md).
|
|
484
|
+
|
|
485
|
+
## Admission and graceful shutdown
|
|
486
|
+
|
|
487
|
+
Use the application operation lease for work that is not already counted by a
|
|
488
|
+
managed resource:
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
const operation = app.admission.acquire()
|
|
492
|
+
if (!operation) return Response.json({ error: 'unavailable' }, { status: 503 })
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
await performAcceptedWork()
|
|
496
|
+
} finally {
|
|
497
|
+
operation.release()
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
`release()` is idempotent. Admission and counter increment are atomic, so work
|
|
502
|
+
cannot slip between the shutdown check and drain accounting.
|
|
503
|
+
|
|
504
|
+
`managedServerResource` already owns contract/streaming-route response lifetimes. It cancels
|
|
505
|
+
their supplied signals at admission close and awaits source cleanup before dependent resources
|
|
506
|
+
close. Do not add application admission leases or a second cancellation registry for those
|
|
507
|
+
streams. A source that ignores cancellation or fails its cleanup prevents a clean shutdown;
|
|
508
|
+
the existing grace and force budgets still bound the result.
|
|
509
|
+
|
|
510
|
+
### Bounded operation admission
|
|
511
|
+
|
|
512
|
+
Compose `createBoundedAdmission` when accepted work also competes for a finite
|
|
513
|
+
process-local resource:
|
|
514
|
+
|
|
515
|
+
```ts
|
|
516
|
+
const generations = createBoundedAdmission({
|
|
517
|
+
upstream: app.admission,
|
|
518
|
+
policy: {
|
|
519
|
+
global: { maxConcurrent: 8, rate: { limit: 120, intervalMs: 60_000 } },
|
|
520
|
+
perKey: { maxConcurrent: 1, maxKeys: 2_000 },
|
|
521
|
+
},
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
await generations.run(accountId, ({ signal }) => generate({ signal }), {
|
|
525
|
+
signal: request.signal,
|
|
526
|
+
timeoutMs: 30_000,
|
|
527
|
+
})
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Acquisition is no-queue and atomic across every configured budget. Refusal names
|
|
531
|
+
the exact bound; only a rate refusal carries `retryAfterMs`. `maxKeys` keeps the
|
|
532
|
+
per-key registry finite, and expired idle entries are retired.
|
|
533
|
+
|
|
534
|
+
The caller timeout is a wait budget, not proof that the resource stopped. It
|
|
535
|
+
aborts the signal and settles the caller, but the lease remains active until the
|
|
536
|
+
underlying Promise actually settles. `drain()` therefore reports real work;
|
|
537
|
+
`force()` closes admission and reports remaining work without claiming to have
|
|
538
|
+
terminated it. → ADR 0118.
|
|
539
|
+
|
|
540
|
+
### Bounded revision wake-up
|
|
541
|
+
|
|
542
|
+
Use `createRevisionSignal` when several operations need to wait for the same
|
|
543
|
+
process-local fact to change, but the change itself is not a queue item:
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
const connectionsChanged = createRevisionSignal({ maxWaiters: 256 })
|
|
547
|
+
|
|
548
|
+
const observed = connectionsChanged.getSnapshot().revision
|
|
549
|
+
const result = await connectionsChanged.wait(observed, {
|
|
550
|
+
signal: request.signal,
|
|
551
|
+
timeoutMs: 10_000,
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
// After changing the canonical connection state:
|
|
555
|
+
connectionsChanged.advance()
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
`wait(after)` resolves immediately when the signal is already newer than
|
|
559
|
+
`after`; this closes the race between reading a snapshot and registering the
|
|
560
|
+
wait. Waiting on the current revision parks one operation, up to `maxWaiters`.
|
|
561
|
+
Every terminal result carries the revision it observed and distinguishes
|
|
562
|
+
`changed`, `timed-out`, `aborted`, `closed` and `capacity`. A future `after` is
|
|
563
|
+
a caller error rather than a revision the signal could honestly promise to
|
|
564
|
+
reach.
|
|
565
|
+
|
|
566
|
+
One advance wakes every older waiter. It is not consumed by the first reader,
|
|
567
|
+
does not retain event payloads and has no replay history. A bounded channel is
|
|
568
|
+
for delivering items to one reader; a credit window grants a finite resource.
|
|
569
|
+
|
|
570
|
+
The signal is already its whole lifecycle handle. Call its idempotent `close()`
|
|
571
|
+
from the `close` phase of the managed resource that owns the changing fact;
|
|
572
|
+
there is no second managed-resource wrapper around the same state. → ADR 0163.
|
|
573
|
+
|
|
574
|
+
### Bounded delivery and byte credit
|
|
575
|
+
|
|
576
|
+
`createBoundedChannel` is for one asynchronous reader when an event bus is not a
|
|
577
|
+
queue:
|
|
578
|
+
|
|
579
|
+
```ts
|
|
580
|
+
const output = createBoundedChannel<string>({
|
|
581
|
+
policy: 'ordered',
|
|
582
|
+
maxItems: 64,
|
|
583
|
+
maxBytes: 256 * 1024,
|
|
584
|
+
sizeOf: (value) => new TextEncoder().encode(value).byteLength,
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
const progress = createBoundedChannel<{ revision: number }>({
|
|
588
|
+
policy: 'latest',
|
|
589
|
+
maxItems: 1,
|
|
590
|
+
maxBytes: 128,
|
|
591
|
+
sizeOf: () => 128,
|
|
592
|
+
})
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
`ordered` never overwrites accepted values; overflow is a reasoned refusal.
|
|
596
|
+
`latest` retains exactly one pending replaceable value and reports
|
|
597
|
+
`coalesced`. Offers never create a hidden writer queue, and only one `next()` may
|
|
598
|
+
wait. Close chooses `drain` (default) or `discard`; abort discards; failure
|
|
599
|
+
rejects the parked and all later reads.
|
|
600
|
+
|
|
601
|
+
`createCreditWindow({ capacityBytes })` is the smaller primitive for a protocol
|
|
602
|
+
that already owns its queue but needs exact byte permission. Each credit lease
|
|
603
|
+
replenishes once; it is flow-control credit, not a durable acknowledgement. The
|
|
604
|
+
application snapshot sink now shares the same latest-value mechanics without
|
|
605
|
+
changing its revision or status contract. → ADR 0119.
|
|
606
|
+
|
|
607
|
+
### Bounded local diagnostic journal
|
|
608
|
+
|
|
609
|
+
Use `createDiagnosticJournal` when a process needs finite, ordered local metadata evidence and the
|
|
610
|
+
deployment log pipeline is not the right boundary:
|
|
611
|
+
|
|
612
|
+
It lives at `stitchkit/application/diagnostic-journal`, not in the main barrel:
|
|
613
|
+
it is the one part of the kernel that spawns, locks and writes files, and while it
|
|
614
|
+
was exported from `stitchkit/application` that single line made the whole
|
|
615
|
+
entrypoint unusable in a browser bundle. Its schemas stay in
|
|
616
|
+
`stitchkit/application`.
|
|
617
|
+
|
|
618
|
+
```ts
|
|
619
|
+
import { createDiagnosticJournal } from 'stitchkit/application/diagnostic-journal'
|
|
620
|
+
import { z } from 'zod'
|
|
621
|
+
|
|
622
|
+
const journal = await createDiagnosticJournal({
|
|
623
|
+
eventSchema: z.object({
|
|
624
|
+
kind: z.enum(['resource_failed', 'recovery_started']),
|
|
625
|
+
resource: z.string().max(80),
|
|
626
|
+
}).strict(),
|
|
627
|
+
path: '/var/lib/example/diagnostic.jsonl', // operator configuration, never request data
|
|
628
|
+
limits: {
|
|
629
|
+
maxEventBytes: 4 * 1024,
|
|
630
|
+
maxPendingItems: 128,
|
|
631
|
+
maxPendingBytes: 512 * 1024,
|
|
632
|
+
maxFileBytes: 8 * 1024 * 1024,
|
|
633
|
+
maxFiles: 4,
|
|
634
|
+
},
|
|
635
|
+
onFailure: (failure) => internalLogger.error(failure),
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
const result = journal.submit({ kind: 'recovery_started', resource: 'database' })
|
|
639
|
+
if (result.outcome === 'refused') internalCounter.add(1, { reason: result.reason })
|
|
640
|
+
|
|
641
|
+
// During managed-resource close. Timeout ends this wait, not the physical append.
|
|
642
|
+
await journal.close({ timeoutMs: 5_000 })
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
The owner schema and JSON serialization run synchronously before admission. Accepted frames carry
|
|
646
|
+
a process epoch and contiguous sequence and retain their complete bytes inside both pending limits
|
|
647
|
+
until their append attempt settles. Capacity, invalid, oversized, closed and terminal-failure
|
|
648
|
+
refusals are explicit; accepted ordered frames are never evicted.
|
|
649
|
+
|
|
650
|
+
The absolute path's parent must already exist and be operator-controlled. One manager owns it via
|
|
651
|
+
an exclusive `.lock`; new files use mode `0600` by default. `maxFiles` includes the active file,
|
|
652
|
+
and a non-newline startup tail is rotated intact rather than guessed or repaired. An abrupt process
|
|
653
|
+
death may leave the lock for an operator to remove only after proving the former owner is gone.
|
|
654
|
+
|
|
655
|
+
`flush()` means every accepted append through that call's boundary settled. It is not `fsync`, a
|
|
656
|
+
durable receipt, exactly-once execution or remote delivery. Timeout/cancellation bound only the
|
|
657
|
+
waiter; the writer retains physical capacity until settlement. There is no reader or upload API.
|
|
658
|
+
Use a durable application store or deployment-owned log collector when restart recovery, replay or
|
|
659
|
+
aggregation is required. → [ADR 0134](../decisions/0134-diagnostic-journal-is-bounded-local-evidence.md).
|
|
660
|
+
|
|
661
|
+
Shutdown performs one phase barrier at a time: stop admission everywhere,
|
|
662
|
+
cancel future schedules, drain admitted work, then close in reverse stable
|
|
663
|
+
topological order. Every hook shares the same grace deadline. Forced cleanup
|
|
664
|
+
shares one force deadline. Repeated `shutdown()` calls return the cached promise;
|
|
665
|
+
a repeated process signal forces that same chain.
|
|
666
|
+
|
|
667
|
+
Do not add a second `process.on('SIGTERM')` handler around the application.
|
|
668
|
+
`bindProcessSignals(app)` is the force/escalation owner. Exit code and hard-exit
|
|
669
|
+
policy remain application/supervisor choices.
|
|
670
|
+
|
|
671
|
+
**Where the budget comes from on the signal path.** `bindProcessSignals(app)`
|
|
672
|
+
with no `shutdown` forwards nothing, so the application spends the budget it
|
|
673
|
+
declared in `createApplication({ shutdown })`. Pass `shutdown` to the binding
|
|
674
|
+
only to override that budget for signals specifically; passing one key overrides
|
|
675
|
+
that key alone and leaves the other at the declaration. Declare the budget once,
|
|
676
|
+
on the application — repeating it in both places is how the two numbers start to
|
|
677
|
+
disagree, and the operator's supervisor timeout is calculated from the one they
|
|
678
|
+
can read.
|
|
679
|
+
|
|
680
|
+
## Managed schedules
|
|
681
|
+
|
|
682
|
+
Schedules activate only after top-level readiness:
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
createManagedSchedule({
|
|
686
|
+
id: 'reconcile',
|
|
687
|
+
everyMs: 5_000,
|
|
688
|
+
startAfterMs: 0,
|
|
689
|
+
overlap: { mode: 'queue-one' },
|
|
690
|
+
errorPolicy: 'continue',
|
|
691
|
+
onError: (error) => internalLogger.error(error),
|
|
692
|
+
run: ({ signal }) => reconcile(signal),
|
|
693
|
+
})
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
- `skip` ignores a due tick while one execution is active.
|
|
697
|
+
- `queue-one` retains one successor; additional due ticks coalesce.
|
|
698
|
+
- `parallel` requires `maxConcurrent`; overflow skips rather than creating a
|
|
699
|
+
hidden queue.
|
|
700
|
+
|
|
701
|
+
The callback receives the application shutdown signal. Normal shutdown waits
|
|
702
|
+
for admitted executions; force aborts the shared signal. There are no retries,
|
|
703
|
+
cron/timezone rules, persisted cursors, backfill or cross-process locks. Put
|
|
704
|
+
those semantics in a durable application scheduler when they are required.
|
|
705
|
+
Schedule cadence stays monotonic, while status snapshots expose ISO wall-clock
|
|
706
|
+
`capturedAt`, `changedAt` and next/last-run timestamps for portable observation.
|
|
707
|
+
|
|
708
|
+
## Operational projection
|
|
709
|
+
|
|
710
|
+
Declare aggregate activity stages, then update them with anonymous handles:
|
|
711
|
+
|
|
712
|
+
```ts
|
|
713
|
+
const generations = createActivityProjection({
|
|
714
|
+
id: 'generation',
|
|
715
|
+
stages: ['queued', 'running', 'finalizing'],
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
const activity = generations.open('queued', 'queued')
|
|
719
|
+
generations.transition(activity, { stage: 'running', state: 'active' })
|
|
720
|
+
generations.transition(activity, { stage: 'finalizing', state: 'active' })
|
|
721
|
+
generations.complete(activity)
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
Item identity is deliberately absent from the snapshot. Observers receive
|
|
725
|
+
aggregate counts with application identity, process epoch, revision and
|
|
726
|
+
timestamps. Calling a terminal method twice does not double-count.
|
|
727
|
+
|
|
728
|
+
```ts
|
|
729
|
+
const sink = createApplicationSnapshotSink({
|
|
730
|
+
write: (snapshot) => publishOperationalSnapshot(snapshot),
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
const unsubscribe = generations.subscribe((snapshot) => {
|
|
734
|
+
sink.publish(snapshot)
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
// During application cleanup:
|
|
738
|
+
unsubscribe()
|
|
739
|
+
await sink.close()
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
The sink replays the current absolute value and coalesces obsolete pending
|
|
743
|
+
revisions behind a slow write. Sink failure is observable but cannot fail the
|
|
744
|
+
application operation that changed the projection.
|
|
745
|
+
|
|
746
|
+
## Optional grammY adapter
|
|
747
|
+
|
|
748
|
+
Install grammY only in an application that imports the adapter:
|
|
749
|
+
|
|
750
|
+
```ts
|
|
751
|
+
import { Bot } from 'grammy'
|
|
752
|
+
import {
|
|
753
|
+
createGrammyWebhookResource,
|
|
754
|
+
grammyPollingResource,
|
|
755
|
+
} from 'stitchkit/application/grammy'
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
For simple long polling, pass an already-configured bot:
|
|
759
|
+
|
|
760
|
+
```ts
|
|
761
|
+
const bot = new Bot(env.BOT_TOKEN)
|
|
762
|
+
bot.command('start', (ctx) => ctx.reply('Hello'))
|
|
763
|
+
bot.catch(({ error }) => internalLogger.error(error))
|
|
764
|
+
|
|
765
|
+
const telegram = grammyPollingResource({
|
|
766
|
+
id: 'telegram',
|
|
767
|
+
bot,
|
|
768
|
+
required: true,
|
|
769
|
+
})
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
Readiness follows grammY's `onStart`. Shutdown calls `bot.stop()` once and then
|
|
773
|
+
awaits the retained `bot.start()` promise, because grammY documents that
|
|
774
|
+
`stop()` alone does not wait for middleware completion.
|
|
775
|
+
|
|
776
|
+
For webhook ingress, `createGrammyWebhookResource` wraps provider-owned update
|
|
777
|
+
handling with admission and drain. The application still mounts the HTTP route,
|
|
778
|
+
verifies/configures its webhook and chooses the grammY framework adapter. An
|
|
779
|
+
update accepted before shutdown may finish; a later update is rejected.
|
|
780
|
+
|
|
781
|
+
The adapter never reads the token/env, installs commands, changes `bot.catch`,
|
|
782
|
+
chooses retry plugins, drops pending updates, sends outbound messages or stores
|
|
783
|
+
updates. A durable inbox/retry worker remains an application component, not a
|
|
784
|
+
mode of this adapter.
|
|
785
|
+
|
|
786
|
+
## What disappears from an application
|
|
787
|
+
|
|
788
|
+
A typical small service can replace roughly 120–220 lines of generic signal,
|
|
789
|
+
timer, admission and close bookkeeping with resource declarations. A larger
|
|
790
|
+
multi-resource service commonly carries 250–450 generic lifecycle lines plus
|
|
791
|
+
15–30 lines for each periodic timer. These are directional review estimates,
|
|
792
|
+
not an API guarantee.
|
|
793
|
+
|
|
794
|
+
The responsibility change is more important than line count:
|
|
795
|
+
|
|
796
|
+
| Before | After |
|
|
797
|
+
|---|---|
|
|
798
|
+
| hand-written phase enum and readiness waiters | `createApplication` snapshot |
|
|
799
|
+
| `process.on` signal loop and escalation | `bindProcessSignals(app)` |
|
|
800
|
+
| interval/timeout handles and overlap flag | `createManagedSchedule` |
|
|
801
|
+
| global in-flight counter and drain waiter set | application operation leases |
|
|
802
|
+
| manual stop/close fan-out | resource graph + bounded shutdown |
|
|
803
|
+
| progress delta events | absolute activity projection |
|
|
804
|
+
|
|
805
|
+
Durable job tables, lifecycle journals/outboxes, provider inboxes and business
|
|
806
|
+
retry rules do **not** disappear. They were never process-local glue and remain
|
|
807
|
+
application-owned.
|
|
808
|
+
|
|
809
|
+
## Durable process facts and owner notifications
|
|
810
|
+
|
|
811
|
+
`createProcessLifecycleLedger` records a bounded versioned list of process runs.
|
|
812
|
+
Start, readiness and shutdown target both `runId` and `pid`, so hot reload and
|
|
813
|
+
PID reuse cannot close the wrong generation. A start classifies what happened to
|
|
814
|
+
the newest run before it, and the classification is decided by three facts —
|
|
815
|
+
whether that run recorded its own exit, whether the pid is the same, and whether
|
|
816
|
+
the version changed:
|
|
817
|
+
|
|
818
|
+
| newest run | same pid | version | `previousExit` | predecessor's `termination` |
|
|
819
|
+
|---|---|---|---|---|
|
|
820
|
+
| none | — | — | `first-boot` | — |
|
|
821
|
+
| recorded `stoppedAt` | — | — | `clean` / `forced` / `abnormal` as recorded | unchanged |
|
|
822
|
+
| still `active` | yes | — | `hot-reload` | `hot-reload`, closed at the new start |
|
|
823
|
+
| still `active` | no | changed | `handoff` | stays `active`; it records its own stop later |
|
|
824
|
+
| still `active` | no | same | `abnormal` (default) | `abnormal`, closed at the new start — an upper bound, the crash time is unknown |
|
|
825
|
+
|
|
826
|
+
A version of `unknown` on either side is never a version change, so a dev build
|
|
827
|
+
after a crashed release reads `abnormal`, not `handoff`. `forced` means the
|
|
828
|
+
process itself acknowledged a kill; `abnormal` means a successor found it dead.
|
|
829
|
+
|
|
830
|
+
The last row is a choice, and the default is the single-process deployment:
|
|
831
|
+
one process per build, a new pid of the same build means the old one stopped
|
|
832
|
+
answering. Where two processes of **one** build overlap on purpose — a
|
|
833
|
+
cluster, a zero-downtime reload of the same build — pass
|
|
834
|
+
`sameVersionOverlap: 'handoff'` to the ledger, and the predecessor stays
|
|
835
|
+
`active` until it records its own shutdown. The cost of that setting is
|
|
836
|
+
symmetric: a real crash under it is reported as a handoff and the dead run
|
|
837
|
+
stays `active` in the ledger until retention drops it. The list is kept in the
|
|
838
|
+
order the transitions wrote it — every write goes through one atomic update,
|
|
839
|
+
so that order is the causal one, and a successor whose clock lags its
|
|
840
|
+
predecessor still finds it at the head; `startedAt` is data, not the sort key.
|
|
841
|
+
Retention (`retain`, default 20) drops finished runs first and an active one —
|
|
842
|
+
a live handoff predecessor — only when nothing finished is left. Facts are
|
|
843
|
+
published through the ledger's own subscription and resource value;
|
|
844
|
+
`ApplicationEventSink` remains the strict application-state stream.
|
|
845
|
+
|
|
846
|
+
`createNotificationOutbox` is the transport-neutral durable delivery side. It
|
|
847
|
+
persists before send, claims an item with an expiring lease, carries one stable
|
|
848
|
+
idempotency key into the transport, retries under an injected clock and records
|
|
849
|
+
terminal drops. The guarantee is at-least-once: a crash after remote acceptance
|
|
850
|
+
but before the receipt is persisted may redeliver, so transports should use the
|
|
851
|
+
key when they support deduplication. The retry budget is sized for the outage
|
|
852
|
+
an owner notification has to outlive, not for a flaky call: the default backoff
|
|
853
|
+
doubles from one second and caps at sixty (`backoffDelay`, the one formula the
|
|
854
|
+
client's resumable streams also use, with jitter 0), and the 99 waits between
|
|
855
|
+
the default `maxAttempts` of 100 sum to 1+2+4+8+16+32 s plus 93 × 60 s — about
|
|
856
|
+
94 minutes — before `onDropped` sees `attempt-limit`.
|
|
857
|
+
`state()` is a read and never fails on the bounds a transition enforces — a file
|
|
858
|
+
that grew past `maxStateBytes` under an older limit is inspectable and is
|
|
859
|
+
trimmed by the next transition. Two bounds do fail loudly: `enqueue` past
|
|
860
|
+
`maxQueue` throws rather than dropping silently, and a `backoffMs` that returns
|
|
861
|
+
`NaN`, a negative or an infinite delay rejects the flush. `stop()` lets the
|
|
862
|
+
send in flight finish and claims nothing more; a `send` without its own
|
|
863
|
+
deadline holds `stop()` — and so the resource's `force()` — for that one call,
|
|
864
|
+
so give the transport a timeout. Superseding a key that is being sent right now
|
|
865
|
+
does not recall it: the send completes, and only its receipt is not written.
|
|
866
|
+
|
|
867
|
+
Both primitives depend on the structural `StateStore`. On a server,
|
|
868
|
+
`createFileStateStore` supplies the shared Zod-validated JSON adapter with an
|
|
869
|
+
inter-process lock, unique temporary file, fsync and atomic rename. The lock
|
|
870
|
+
is a file with a heartbeat: the holder refreshes its mtime every third of
|
|
871
|
+
`staleLockMs` (default 3 s), a contender waits up to `lockTimeoutMs` (default
|
|
872
|
+
10 s), and the stale bound must sit inside the timeout — otherwise a crashed
|
|
873
|
+
holder blocks every update until the lock ages out, which the constructor
|
|
874
|
+
refuses. A lock whose heartbeat is stale is reclaimed once its recorded pid is
|
|
875
|
+
gone; a live or unverifiable pid (a reused number, another user's process)
|
|
876
|
+
keeps it for ten stale bounds — and never fewer than thirty missed heartbeats,
|
|
877
|
+
so a scheduler stall on a loaded host cannot pull a live lock from under its
|
|
878
|
+
holder — after which the heartbeat wins and the lock is abandoned. Temporary files a crashed writer left beside the state are swept on
|
|
879
|
+
the store's first update. Ledger corruption may be declared reconstructable;
|
|
880
|
+
an outbox must fail closed rather than silently discard pending delivery.
|
|
881
|
+
|
|
882
|
+
==============================================================================
|
|
883
|
+
# Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
884
|
+
==============================================================================
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
title: Application migration recipes
|
|
888
|
+
description: Executable patterns for moving database, poller, queue and operational publishing lifecycle into the managed application kernel.
|
|
889
|
+
type: architecture
|
|
890
|
+
status: active
|
|
891
|
+
created: 2026-08-24
|
|
892
|
+
updated: 2026-08-24
|
|
893
|
+
---
|
|
894
|
+
|
|
895
|
+
# Application migration recipes
|
|
896
|
+
|
|
897
|
+
These recipes are the migration companion to the
|
|
898
|
+
[managed application kernel](./application-kernel.md). Their canonical source is
|
|
899
|
+
the packed-consumer fixture
|
|
900
|
+
[`application-migration-recipes.ts`](../../packages/core/scripts/consumer-lane/fixtures/minimal/src/application-migration-recipes.ts):
|
|
901
|
+
the consumer lane installs the package tarball, typechecks that file and runs it
|
|
902
|
+
only through `stitchkit/application` exports.
|
|
903
|
+
|
|
904
|
+
## Database connection
|
|
905
|
+
|
|
906
|
+
Wrap connection allocation and readiness in `start`, and make `close` safe after
|
|
907
|
+
partial setup. Stitchkit invokes cleanup for every attempted start, including a
|
|
908
|
+
start that allocates a client and then rejects.
|
|
909
|
+
|
|
910
|
+
```ts
|
|
911
|
+
const database = defineManagedResource({
|
|
912
|
+
id: 'database',
|
|
913
|
+
async start({ signal, reportHealth }) {
|
|
914
|
+
await databaseClient.connect(signal)
|
|
915
|
+
await databaseClient.assertReady()
|
|
916
|
+
reportHealth('healthy')
|
|
917
|
+
},
|
|
918
|
+
close: () => databaseClient.close(),
|
|
919
|
+
})
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
The application still owns the client, ORM configuration, transactions,
|
|
923
|
+
migrations and reconnect policy. The executable recipe deliberately fails after
|
|
924
|
+
allocation and proves one rollback close.
|
|
925
|
+
|
|
926
|
+
## Long-running poller
|
|
927
|
+
|
|
928
|
+
Return separate readiness and completion promises. Readiness says dependants may
|
|
929
|
+
start; completion represents the whole background lifetime.
|
|
930
|
+
|
|
931
|
+
```ts
|
|
932
|
+
const poller = defineManagedResource({
|
|
933
|
+
id: 'poller',
|
|
934
|
+
start: ({ signal }) => ({
|
|
935
|
+
ready: providerPoller.ready,
|
|
936
|
+
completion: providerPoller.run(signal),
|
|
937
|
+
}),
|
|
938
|
+
stopAdmission: () => providerPoller.stop(),
|
|
939
|
+
drain: () => providerPoller.completion,
|
|
940
|
+
close: () => providerPoller.close(),
|
|
941
|
+
})
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
Completion before readiness fails startup and rolls back. Completion after
|
|
945
|
+
readiness removes application readiness. Provider cursor persistence, retry,
|
|
946
|
+
backoff and restart policy remain application/provider concerns.
|
|
947
|
+
|
|
948
|
+
## Queue consumer
|
|
949
|
+
|
|
950
|
+
Acquire the application lease only after provider delivery/claim. If admission
|
|
951
|
+
is already closed, reject the delivery through the provider's own nack/requeue
|
|
952
|
+
primitive. Work admitted before shutdown releases its lease after ack/nack and
|
|
953
|
+
therefore participates in application drain.
|
|
954
|
+
|
|
955
|
+
```ts
|
|
956
|
+
async function handleDelivery(delivery: Delivery) {
|
|
957
|
+
const lease = app.admission.acquire()
|
|
958
|
+
if (!lease) return delivery.nack({ requeue: true })
|
|
959
|
+
try {
|
|
960
|
+
await processClaim(delivery.claim)
|
|
961
|
+
await delivery.ack()
|
|
962
|
+
} catch (error) {
|
|
963
|
+
await delivery.nack({ requeue: shouldRetry(error) })
|
|
964
|
+
throw error
|
|
965
|
+
} finally {
|
|
966
|
+
lease.release()
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
Stitchkit owns only process-local admission accounting and drain. Durable claims,
|
|
972
|
+
visibility timeouts, deduplication, idempotency and retry classification remain
|
|
973
|
+
in the queue/product layer.
|
|
974
|
+
|
|
975
|
+
## Operational publisher
|
|
976
|
+
|
|
977
|
+
Project anonymous aggregate activity, then feed absolute snapshots into the
|
|
978
|
+
latest-value sink. A slow transport holds one write plus one replaceable latest
|
|
979
|
+
revision rather than an unbounded event queue.
|
|
980
|
+
|
|
981
|
+
```ts
|
|
982
|
+
const publisher = createApplicationSnapshotSink({
|
|
983
|
+
write: (snapshot) => monitoring.publish(snapshot),
|
|
984
|
+
})
|
|
985
|
+
const activity = createActivityProjection({
|
|
986
|
+
id: 'generation',
|
|
987
|
+
stages: ['queued', 'running'],
|
|
988
|
+
})
|
|
989
|
+
const unsubscribe = activity.subscribe((snapshot) => {
|
|
990
|
+
publisher.publish(snapshot)
|
|
991
|
+
})
|
|
992
|
+
|
|
993
|
+
// cleanup boundary
|
|
994
|
+
unsubscribe()
|
|
995
|
+
publisher.publish(activity.getSnapshot())
|
|
996
|
+
await publisher.close()
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
The monitoring backend, transport retry and cross-process aggregation remain
|
|
1000
|
+
outside Stitchkit. The executable recipe blocks revision 0, coalesces
|
|
1001
|
+
intermediate revisions, explicitly admits the final absolute snapshot after
|
|
1002
|
+
unsubscribe and proves that `close()` flushes revision 3. Do not rely on an
|
|
1003
|
+
asynchronous subscriber callback racing cleanup: publish `getSnapshot()` before
|
|
1004
|
+
closing the outer sink, so any older or duplicate late delivery is rejected as
|
|
1005
|
+
stale instead of dropping the final state.
|
|
1006
|
+
|
|
1007
|
+
## Bound a handler and a local worker with one lease policy
|
|
1008
|
+
|
|
1009
|
+
Use a bounded admission when two entry paths consume the same finite local
|
|
1010
|
+
capacity. Composing it with `application.admission` keeps readiness and shutdown
|
|
1011
|
+
as the upstream gate:
|
|
1012
|
+
|
|
1013
|
+
```ts
|
|
1014
|
+
const work = createBoundedAdmission({
|
|
1015
|
+
upstream: app.admission,
|
|
1016
|
+
policy: {
|
|
1017
|
+
global: { maxConcurrent: 4 },
|
|
1018
|
+
perKey: { maxConcurrent: 1, maxKeys: 1_000 },
|
|
1019
|
+
},
|
|
1020
|
+
})
|
|
1021
|
+
|
|
1022
|
+
const fromHttp = (key: string, signal: AbortSignal) =>
|
|
1023
|
+
work.run(key, (context) => render(context.signal), { signal, timeoutMs: 20_000 })
|
|
1024
|
+
|
|
1025
|
+
const fromWorker = (key: string) =>
|
|
1026
|
+
work.run(key, (context) => reconcile(context.signal))
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
A timeout in `fromHttp` does not free a permit while `render` remains active.
|
|
1030
|
+
At shutdown call `work.stopAdmission()` with the other admission owners and
|
|
1031
|
+
await `work.drain(...)`; the result reports the actual remainder.
|
|
1032
|
+
|
|
1033
|
+
## Replace ad-hoc output and progress queues
|
|
1034
|
+
|
|
1035
|
+
Ordered output and replaceable progress are separate declarations:
|
|
1036
|
+
|
|
1037
|
+
```ts
|
|
1038
|
+
const lines = createBoundedChannel<string>({
|
|
1039
|
+
policy: 'ordered', maxItems: 100, maxBytes: 1_000_000,
|
|
1040
|
+
sizeOf: (line) => new TextEncoder().encode(line).byteLength,
|
|
1041
|
+
})
|
|
1042
|
+
|
|
1043
|
+
const state = createBoundedChannel<{ revision: number; percent: number }>({
|
|
1044
|
+
policy: 'latest', maxItems: 1, maxBytes: 64,
|
|
1045
|
+
sizeOf: () => 64,
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
const lineResult = lines.offer('one durable-in-process ordering unit')
|
|
1049
|
+
const stateResult = state.offer({ revision: 2, percent: 50 })
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
Handle `refused` from `lines` at the protocol boundary; do not turn it into
|
|
1053
|
+
implicit loss. A `coalesced` state result is expected latest-value behaviour,
|
|
1054
|
+
not evidence that an ordered event was delivered. Neither channel is durable;
|
|
1055
|
+
persist first when restart replay is required.
|
|
1056
|
+
|
|
1057
|
+
## Handing a handle to the resources that depend on it
|
|
1058
|
+
|
|
1059
|
+
`dependsOn` carries ordering. To carry the object as well, return a `value` from
|
|
1060
|
+
`start` and read it with `context.use(...)`. This replaces the module-local
|
|
1061
|
+
`let handle: T | null` with the guard the graph makes unreachable.
|
|
1062
|
+
|
|
1063
|
+
```ts
|
|
1064
|
+
const database = defineManagedResource({
|
|
1065
|
+
id: 'database',
|
|
1066
|
+
start: async () => ({ value: await connect(env.DATABASE_URL) }),
|
|
1067
|
+
})
|
|
1068
|
+
|
|
1069
|
+
const worker = defineManagedResource({
|
|
1070
|
+
id: 'worker',
|
|
1071
|
+
dependsOn: [database],
|
|
1072
|
+
start(context) {
|
|
1073
|
+
const db = context.use(database) // Connection — not Connection | null
|
|
1074
|
+
},
|
|
1075
|
+
})
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
Declare the dependency with the **resource** whenever you intend to read from
|
|
1079
|
+
it: that is the form `use` can type, and it stops the declaration and the read
|
|
1080
|
+
from drifting. A string still expresses order on its own.
|
|
1081
|
+
|
|
1082
|
+
The value is published when `start` resolves and stays readable from `activate`
|
|
1083
|
+
and from the shutdown phases. `use` refuses a resource that was not declared in
|
|
1084
|
+
`dependsOn` — that only ever worked by luck of declaration order — and refuses a
|
|
1085
|
+
resource that published nothing, which the compiler refuses too. The executable
|
|
1086
|
+
recipe proves both the read and the refusal.
|
|
1087
|
+
|
|
1088
|
+
## Managed HTTP server
|
|
1089
|
+
|
|
1090
|
+
The main resource of a web backend, and the one whose ownership rule is
|
|
1091
|
+
counter-intuitive: give `managedServerResource` a **thunk** and it creates the
|
|
1092
|
+
server during `start`, after its dependencies are ready. Give it an
|
|
1093
|
+
already-listening server and it adopts that one instead.
|
|
1094
|
+
|
|
1095
|
+
```ts
|
|
1096
|
+
const http = managedServerResource({
|
|
1097
|
+
id: 'http',
|
|
1098
|
+
dependsOn: [database],
|
|
1099
|
+
server: (context) => {
|
|
1100
|
+
const db = context.use(database)
|
|
1101
|
+
return createServer({ port: env.PORT, services: createServices(db) })
|
|
1102
|
+
},
|
|
1103
|
+
})
|
|
1104
|
+
|
|
1105
|
+
const app = createApplication({ id: 'service', resources: [database, http] })
|
|
1106
|
+
await app.start() // the port is bound; the snapshot means it
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
The server resource publishes its `ManagedServerHandle`, so anything that needs
|
|
1110
|
+
the running server — a Socket.IO attachment, a URL to log, a probe — reads it
|
|
1111
|
+
with `context.use(http)` rather than through a module-local.
|
|
1112
|
+
|
|
1113
|
+
The factory itself receives `ManagedResourceContext` too. It can read only the
|
|
1114
|
+
dependencies declared on the server resource and shares the graph's startup
|
|
1115
|
+
signal. A dependency failure prevents the factory from binding; a factory
|
|
1116
|
+
rejection is rolled back without invoking the factory a second time.
|
|
1117
|
+
|
|
1118
|
+
Do not spread this resource over your own `start` to control creation order:
|
|
1119
|
+
that shape exists only as a workaround for the version whose `start` was empty,
|
|
1120
|
+
and `dependsOn` now expresses the order directly. The executable recipe binds a
|
|
1121
|
+
real port, proves a request reaches it after `start()` resolves, and proves the
|
|
1122
|
+
port is closed after `shutdown()`.
|
|
1123
|
+
|
|
1124
|
+
## Deletion checklist
|
|
1125
|
+
|
|
1126
|
+
After the cutover, remove the old generic lifecycle path completely:
|
|
1127
|
+
|
|
1128
|
+
- duplicate `process.on(...)` handlers and shutdown promise caches;
|
|
1129
|
+
- manual readiness waiters and resource close fan-out;
|
|
1130
|
+
- raw interval handles, overlap flags and timer drain code;
|
|
1131
|
+
- global in-flight counters and waiter sets replaced by application leases;
|
|
1132
|
+
- progress delta queues replaced by absolute snapshot publishing.
|
|
1133
|
+
|
|
1134
|
+
Keep the product code that still owns durable state and policy:
|
|
1135
|
+
|
|
1136
|
+
- database schema, transactions and migrations;
|
|
1137
|
+
- queue claims, ack/nack, deduplication and external-effect idempotency;
|
|
1138
|
+
- provider cursor, retry/backoff and credentials;
|
|
1139
|
+
- monitoring transport configuration and durable retention;
|
|
1140
|
+
- process exit code, hard-exit and deployment/supervisor policy.
|
|
1141
|
+
|
|
1142
|
+
Run the old and new lifecycle paths separately during development if needed,
|
|
1143
|
+
but do not ship both. Before cutover, verify there is exactly one signal binding,
|
|
1144
|
+
one timer owner, one admission gate and one resource cleanup chain.
|