@zackbart/connecta 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/bin/connecta.mjs +26 -17
  3. package/bin/meta-tool-names.d.mts +3 -1
  4. package/bin/meta-tool-names.mjs +4 -1
  5. package/bin/version.mjs +1 -1
  6. package/dist/activity.d.ts +32 -5
  7. package/dist/activity.js +32 -6
  8. package/dist/advertised-schema.js +15 -0
  9. package/dist/agent-observation.js +24 -2
  10. package/dist/agent-routes.js +33 -44
  11. package/dist/auth/clerk.d.ts +6 -1
  12. package/dist/auth/clerk.js +16 -0
  13. package/dist/auth/cloudflare-access.js +49 -7
  14. package/dist/auth/downstream-oauth/consent.js +79 -29
  15. package/dist/auth/downstream-oauth/grant-store.js +58 -7
  16. package/dist/auth/downstream-oauth/refresh-coordinator.js +4 -1
  17. package/dist/auth/downstream-oauth/refresh-records.js +6 -5
  18. package/dist/auth/downstream-oauth.js +15 -4
  19. package/dist/auth/oauth-request-not-sent.js +4 -0
  20. package/dist/auth/static-oauth.js +77 -39
  21. package/dist/auth-elicitation.js +118 -61
  22. package/dist/authorization-handoff.js +36 -3
  23. package/dist/catalog-cache.js +352 -43
  24. package/dist/catalog-freshness.js +25 -0
  25. package/dist/catalog-service.js +408 -78
  26. package/dist/catalog.js +216 -62
  27. package/dist/config-defaults.d.ts +2 -1
  28. package/dist/config-defaults.js +4 -1
  29. package/dist/config.d.ts +53 -21
  30. package/dist/config.js +56 -17
  31. package/dist/connectors/remote-mcp/call.js +1 -1
  32. package/dist/connectors/remote-mcp/catalog.js +52 -28
  33. package/dist/connectors/remote-mcp/connection.js +113 -22
  34. package/dist/connectors/remote-mcp/oauth.js +2 -2
  35. package/dist/connectors/remote-mcp/resources.js +5 -2
  36. package/dist/connectors/remote-mcp/transport.js +24 -6
  37. package/dist/connectors/remote-mcp.js +14 -5
  38. package/dist/credential-contract.d.ts +3 -1
  39. package/dist/describe-config.d.ts +4 -2
  40. package/dist/describe-config.js +1 -0
  41. package/dist/discovery-health.js +26 -11
  42. package/dist/downstream-input.js +11 -2
  43. package/dist/errors.d.ts +60 -4
  44. package/dist/errors.js +88 -3
  45. package/dist/execute.js +491 -202
  46. package/dist/executor-admission.js +2 -0
  47. package/dist/executor-contract.js +6 -0
  48. package/dist/executor-result.js +3 -3
  49. package/dist/executors/quickjs-child.js +14 -5
  50. package/dist/executors/quickjs-runtime.d.ts +2 -0
  51. package/dist/executors/quickjs-runtime.js +37 -20
  52. package/dist/executors/quickjs.js +24 -7
  53. package/dist/guarded-transport.js +2 -0
  54. package/dist/guest-functions.js +5 -4
  55. package/dist/guest-runtime.js +37 -1
  56. package/dist/guest-types.d.ts +18 -0
  57. package/dist/http/client.js +14 -2
  58. package/dist/http/errors.js +9 -4
  59. package/dist/http/fields.js +17 -5
  60. package/dist/http/hatch.js +11 -3
  61. package/dist/http/node-transport.js +110 -4
  62. package/dist/http/oauth.js +17 -6
  63. package/dist/http/paging.js +1 -1
  64. package/dist/http/provider.js +2 -6
  65. package/dist/http/vendor.js +6 -1
  66. package/dist/index.d.ts +1 -1
  67. package/dist/index.js +15 -21
  68. package/dist/invocation-failure.js +28 -0
  69. package/dist/invocation.js +167 -64
  70. package/dist/membership.d.ts +1 -1
  71. package/dist/meta-output.js +24 -18
  72. package/dist/meta-tool-names.d.ts +1 -1
  73. package/dist/meta-tool-names.js +1 -1
  74. package/dist/meta-tools.js +286 -73
  75. package/dist/module-contracts.d.ts +3 -1
  76. package/dist/native-recovery.js +4 -1
  77. package/dist/native-tools.js +177 -55
  78. package/dist/node.d.ts +7 -0
  79. package/dist/node.js +85 -4
  80. package/dist/oauth-handoff.js +53 -43
  81. package/dist/operation-activity.js +104 -0
  82. package/dist/operation-vocabulary.js +6 -0
  83. package/dist/operator-record.d.ts +9 -1
  84. package/dist/operator-record.js +16 -0
  85. package/dist/operator-ui/generated.js +2 -2
  86. package/dist/program-result.js +2 -0
  87. package/dist/program-source.js +1 -1
  88. package/dist/program-writes.js +112 -19
  89. package/dist/provider.d.ts +1 -14
  90. package/dist/provider.js +0 -3
  91. package/dist/providers/_shared/google/kit.js +2 -2
  92. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  93. package/dist/providers/breeze/api.js +2 -2
  94. package/dist/providers/breeze/index.js +1 -1
  95. package/dist/providers/breeze/skill.generated.js +1 -1
  96. package/dist/providers/ccb/api.js +2 -2
  97. package/dist/providers/ccb/index.js +1 -1
  98. package/dist/providers/ccb/skill.generated.js +1 -1
  99. package/dist/providers/cloudflare/api.js +2 -2
  100. package/dist/providers/cloudflare/index.js +3 -1
  101. package/dist/providers/cloudflare/skill.generated.js +1 -1
  102. package/dist/providers/docs/index.js +1 -1
  103. package/dist/providers/docs/skill.generated.js +1 -1
  104. package/dist/providers/drive/index.js +1 -1
  105. package/dist/providers/drive/skill.generated.js +1 -1
  106. package/dist/providers/forms/index.js +2 -1
  107. package/dist/providers/forms/skill.generated.js +1 -1
  108. package/dist/providers/github/api.js +98 -6
  109. package/dist/providers/github/index.js +8 -3
  110. package/dist/providers/github/lib/auth.d.ts +3 -1
  111. package/dist/providers/github/lib/auth.js +43 -12
  112. package/dist/providers/github/lib/discovery.js +11 -1
  113. package/dist/providers/github/lib/operations.js +8 -0
  114. package/dist/providers/github/safety.js +39 -0
  115. package/dist/providers/github/skill.generated.js +1 -1
  116. package/dist/providers/github/tools.js +12 -5
  117. package/dist/providers/gmail/index.js +2 -1
  118. package/dist/providers/gmail/skill.generated.js +1 -1
  119. package/dist/providers/granola/index.js +1 -1
  120. package/dist/providers/granola/skill.generated.js +1 -1
  121. package/dist/providers/overflow/index.js +3 -1
  122. package/dist/providers/overflow/skill.generated.js +1 -1
  123. package/dist/providers/planning-center/index.js +12 -6
  124. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  125. package/dist/providers/planning-center/skill.generated.js +1 -1
  126. package/dist/providers/quickbooks/api.js +28 -25
  127. package/dist/providers/quickbooks/index.js +3 -6
  128. package/dist/providers/quickbooks/lib/writes.js +55 -0
  129. package/dist/providers/quickbooks/safety.js +17 -5
  130. package/dist/providers/quickbooks/skill.generated.js +1 -1
  131. package/dist/providers/sheets/index.js +2 -1
  132. package/dist/providers/sheets/skill.generated.js +1 -1
  133. package/dist/providers/slides/index.js +3 -3
  134. package/dist/providers/slides/skill.generated.js +1 -1
  135. package/dist/providers/stripe/index.js +2 -2
  136. package/dist/providers/stripe/skill.generated.js +1 -1
  137. package/dist/providers/tithely/index.js +2 -1
  138. package/dist/providers/tithely/skill.generated.js +1 -1
  139. package/dist/providers/vercel/api.js +3 -2
  140. package/dist/providers/vercel/skill.generated.js +1 -1
  141. package/dist/registry.d.ts +36 -4
  142. package/dist/registry.js +142 -30
  143. package/dist/resource-read.js +21 -2
  144. package/dist/result-preview.js +210 -0
  145. package/dist/result-seal.js +168 -0
  146. package/dist/route-names.js +53 -0
  147. package/dist/routes/activity-context.js +1 -0
  148. package/dist/routes/activity-disclosure.js +26 -4
  149. package/dist/routes/activity.js +2 -1
  150. package/dist/routes/connect.js +21 -13
  151. package/dist/routes/mcp.js +83 -35
  152. package/dist/routes/shared.d.ts +6 -0
  153. package/dist/routes/ui-config.js +1 -1
  154. package/dist/routes/ui-data.js +3 -2
  155. package/dist/runtime/call.js +15 -10
  156. package/dist/runtime/native-storage.js +12 -0
  157. package/dist/runtime/request-reads.js +66 -14
  158. package/dist/runtime/request-scope.js +38 -2
  159. package/dist/runtime/run.js +4 -1
  160. package/dist/sent-secrets.d.ts +20 -0
  161. package/dist/sent-secrets.js +49 -1
  162. package/dist/skill-origin.js +16 -0
  163. package/dist/skills.js +213 -57
  164. package/dist/storage/d1-driver.js +33 -5
  165. package/dist/storage/keys.js +16 -7
  166. package/dist/storage/sql-native.js +60 -3
  167. package/dist/storage/sql.js +6 -0
  168. package/dist/timing-trace.js +84 -0
  169. package/dist/tool-safety.d.ts +33 -0
  170. package/dist/tool-safety.js +51 -15
  171. package/dist/types.d.ts +36 -0
  172. package/dist/url-safety.js +38 -20
  173. package/dist/usage-guide.js +14 -13
  174. package/dist/utility-recovery.js +33 -0
  175. package/dist/validate.js +29 -10
  176. package/dist/version.d.ts +1 -1
  177. package/dist/version.js +1 -1
  178. package/dist/worker.js +60 -15
  179. package/documentation/architecture.md +152 -43
  180. package/documentation/auth.md +160 -53
  181. package/documentation/code-mode.md +396 -119
  182. package/documentation/deploying.md +35 -10
  183. package/documentation/hosted-mcp-migration.md +9 -3
  184. package/documentation/integrating.md +27 -5
  185. package/documentation/meta-tools.md +761 -157
  186. package/documentation/operating.md +91 -4
  187. package/documentation/operator-ui.md +51 -2
  188. package/documentation/providers.md +34 -20
  189. package/examples/worker/README.md +3 -3
  190. package/guides/how-it-works.md +2 -1
  191. package/guides/safety-and-trust.md +1 -3
  192. package/package.json +10 -8
  193. package/templates/node/AGENTS.md +3 -3
  194. package/templates/node/README.md +1 -1
  195. package/templates/node/package.json +1 -1
@@ -240,7 +240,7 @@ An admitted non-preflight `/mcp` request then takes five steps in
240
240
  5. **Serve.** Refuse `?toolkit=` with a 404 — the toolkits are gone
241
241
  ([#178](https://github.com/zackbart/connecta/issues/178)) but their URLs were
242
242
  handed out, and retiring a scoping boundary into fail-open is worse than any
243
- 404 — then register the six meta-tools on a fresh `McpServer`
243
+ 404 — then register the meta-tools on a fresh `McpServer`
244
244
  (`test/server.test.ts`).
245
245
 
246
246
  ### Modern request metadata and transport refusals
@@ -358,6 +358,13 @@ reader. `program-result.ts` owns executor-independent byte limits;
358
358
  values through provider preludes. Cursors hold only an ID and offset for the
359
359
  current run. Result bytes, expiry, authority and capacity charges stay in the
360
360
  existing storage and ledger; no executor-specific store or storage key is added.
361
+ `result-seal.ts` keeps a run's results unreadable to other requests until they pass
362
+ its final secret set ([`R4a`](./code-mode.md#results-and-projection)): chunk 0 waits in a
363
+ request-owned ledger, and absence of chunk 0 is the refusal, so every exit leaves
364
+ unpublished entries unreadable with no further write. Sealing waits for the work that can
365
+ add to the set (`HeldResults.track` for the run's host calls, `RequestOwner.credentialReads`
366
+ for credential reads, which outlast a caller that stopped waiting), then publishes each
367
+ entry that passes. Stored bytes are never rewritten.
361
368
 
362
369
  ## Admission, in two places
363
370
 
@@ -749,8 +756,8 @@ the search returns, never raced or detached, since a storage write cannot be
749
756
  withdrawn once sent. The budget bounds when health work starts, not when it
750
757
  finishes, so slow storage can carry a search past it by one operation's
751
758
  latency. A scoped search,
752
- `connecta.describe`, native `tools/list` and tool calls keep the full
753
- `discovery.probeTimeoutMs`. A probe that times out on its own deadline, or a
759
+ `connecta.describe` and tool calls keep the full `discovery.probeTimeoutMs`,
760
+ as does a native `tools/list` for a connector with no stale catalog. A probe that times out on its own deadline, or a
754
761
  retryable failure that itself took at least one second or named its own wait,
755
762
  writes a record in the `connector-health:v1:` family: the connecta
756
763
  classification code, observation time, retry time (60 seconds later, or the
@@ -800,7 +807,56 @@ retain a complete private fallback, and static catalogs remain
800
807
  in configuration. After a transient first-page tool listing failure, ordinary calls
801
808
  may use a complete catalog for up to five minutes after its expiry in the same
802
809
  auth partition. Partial walks, permission and auth failures do not use this
803
- fallback. Resources require unexpired inventories. There is no deferred refresh.
810
+ fallback, and a refusal keeps later failures from using it until a listing succeeds.
811
+ Resources require unexpired inventories.
812
+
813
+ **Stale listings.** Discovery alone may list a tool catalog up to 24 hours past
814
+ its expiry (`STALE_DISCOVERY_MS`), so chunks and manifests keep that storage TTL
815
+ and a publish deletes the revision it supersedes. Each discovery first peeks the
816
+ entry without a connection (`Connector.peekCatalog`, or the retained custom
817
+ catalog once its listing has failed): storage reads only, through the listing's
818
+ address, fingerprint, generation and credential fences (INV-5, INV-8, INV-10). A
819
+ peek takes no probe slot, and a listing that follows it in the request takes what
820
+ it read, so a resolver still runs once and the entry is read once. A current peek
821
+ skips the probe queue. A stale one stands in for a probe that did not answer, on
822
+ the failures [meta-tools](./meta-tools.md#lexical-discovery) lists, marked in host-only
823
+ provenance (`markCatalogStale`). It stays out of the request's retained catalogs,
824
+ so `resolve` and every call read the live one. A probe that exhausts its own
825
+ deadline hands one refresh to `defer`: a listing in a new request scope, with its
826
+ own signal and transport, closed in that scope and settling the health record it
827
+ leased. Nothing of the original request reaches it (INV-7). One clock, started as
828
+ it is scheduled, bounds its whole tail at 20 seconds, inside Workers' `waitUntil`:
829
+ queueing and the claim spend it, the listing gets what remains less a second for
830
+ settlement and two for cleanup, and a claim that spends it all dispatches nothing.
831
+ Only the request whose settlement wrote the health record takes that refresh;
832
+ settlement and the claim are compare-and-set against the record the request read,
833
+ including creating an absent one, so concurrent failures start one refresh.
834
+
835
+ A peek read before a wait is not served after it. Once a probe or a slot has kept
836
+ the request waiting, the entry is read again through every fence, so an invalidation
837
+ or a refusal in the meantime retires it, in this isolate or another.
838
+
839
+ **Refusal fences.** `auth_required`, `downstream_oauth_required` or
840
+ `provider_permission_denied` from a listing or its handshake writes one key
841
+ (`fenceTools`) in the namespace of the catalog's current generation, addressed by
842
+ the owner partition and credential identity, or by the auth partition alone on a
843
+ shared connector, whose credential gets one answer whoever asks. A tool listing that
844
+ sees it misses, fresh or stale, so `peekTools` and the five-minute fallback have
845
+ nothing to serve; both also read it fresh before serving an expired entry. A listing
846
+ that succeeds lifts by compare-and-set only the fence it first saw, so a refusal
847
+ that lands while it runs stays. The key outlives every entry it guards
848
+ (`CATALOG_FENCE_TTL_SECONDS`) and the generation's rotation discards it with them.
849
+ It adds no round trip to a read: it travels in the catalog snapshot or the manifest
850
+ read. Timeouts and retryable failures write nothing here, and neither does a
851
+ caller-local refusal repeated within one request.
852
+
853
+ The 24-hour limit stays because of these. Everything that can end a caller's access
854
+ and that Connecta can observe (a refusal, a credential change, a disconnect, an
855
+ invalidation) now retires the catalog at once. What remains is a revocation behind a
856
+ connector that has not answered since: tool names and schemas that caller was
857
+ already shown, marked stale, granting nothing, for at most a day. The first answer
858
+ ends it.
859
+
804
860
  Host-only provenance carries each catalog's original fetch time, TTL and expiry,
805
861
  and whether it was a fallback, through classification and request-local copies.
806
862
  Zero TTL and stale fallbacks cannot authorize post-entry auth recovery.
@@ -808,8 +864,8 @@ Zero TTL and stale fallbacks cannot authorize post-entry auth recovery.
808
864
  A manifest is published by CAS only after every UTF-8 chunk is written. It
809
865
  records the original fetch time, absolute expiry, revision, fingerprint, and
810
866
  monotonic stamp. Readers verify all chunks and fingerprint before accepting it.
811
- Freshness expires at the bounded TTL; tool facts remain for the five-minute
812
- fallback window, and generations expire after 48 hours; invalidation
867
+ Freshness expires at the bounded TTL; tool facts remain for the 24-hour stale
868
+ listing limit, of which calls may use five minutes, and generations expire after 48 hours; invalidation
813
869
  rotates the generation, and old chunks expire without becoming reachable
814
870
  again. Remote clients pin that generation before listing, including SDK cache
815
871
  refreshes. Each new listing pins the current generation, so a live client can
@@ -1042,7 +1098,15 @@ run of K = 8 consecutive UTF-16 units of a registered form of a value at least K
1042
1098
  long, in the raw text or any JSON-decoded layer; text cut where the cut can end
1043
1099
  inside a form loses that unfinished tail, however short.** Runs formed from
1044
1100
  placeholder text and kept text or numbers are covered too; the placeholder
1045
- itself is reserved replacement text. Registered forms are
1101
+ itself is reserved replacement text. The guarantee excludes an occurrence made
1102
+ entirely of reserved replacement text, including a run across the seam between
1103
+ adjacent markers, whether or not the registered form contains a complete
1104
+ `[redacted]`. For example, register `secretsecret` and `ted][red`, then redact
1105
+ `["secretsecret", "secretsecret"]`: the returned leaves may be
1106
+ `["[redacted]", "[redacted]"]`. Their joined projection spells the second
1107
+ registered value using only reserved replacement text; no original secret
1108
+ units survive. An occurrence that includes retained original text stays covered.
1109
+ Registered forms are
1046
1110
  the raw value, its URL/form encodings (either percent hex case), base64 and
1047
1111
  base64url, and the `Bearer `/`token ` framings. The framing words are public, so
1048
1112
  no indexed run starts inside them; a run reaching K units of the value is
@@ -1344,15 +1408,15 @@ guide (G) violations before writing. It derives `drift.json` version 2's
1344
1408
  `generated` block from kit operations and evidence digests, and the guide's
1345
1409
  `## Conventions` from definition declarations into `skill.generated.ts`.
1346
1410
  The generator requires `fixtures.ts` to export `createAuthored()` with a
1347
- nonempty offline `staticTools` catalog. Its input/output schemas derive one
1348
- paging convention, including `get_*`, hatch search and details, excluding
1349
- tools whose `page.hasMore` is always false. The convention distinguishes
1350
- top-level `cursor` from `query[page.param]` or `body[page.param]` continuation
1351
- and tells agents to return `page.nextCursor` before the call that would exceed
1352
- the per-program call limit, then continue in the next program. The Hatch line
1353
- reports a nonzero explicit refusal count with singular/plural wording and
1354
- points to the actual prefixed details tool with method/path lookup; it never
1355
- embeds the refusal table. Unreviewed operations remain refused.
1411
+ nonempty offline `staticTools` catalog, which the installed inventory lists.
1412
+ Conventions render only provider-specific declarations: the vendor envelope
1413
+ root, the hatch's tools and refusal count, the `idempotencyKey` rule where a
1414
+ provider has one, and the Errors line. Paging, `fields`, withheld-value and
1415
+ write-once rules live at the agent's point of use, and
1416
+ `test/provider-conventions.test.ts` asserts each paged tool's continuation there.
1417
+ The Hatch line reports a nonzero explicit refusal count with singular/plural
1418
+ wording and points to the actual prefixed details tool with method/path lookup;
1419
+ it never embeds the refusal table. Unreviewed operations remain refused.
1356
1420
  `ProviderContext.usageGuide` selects a guide's `<!-- when: <key> -->` blocks
1357
1421
  with the definition's `blocks`. Maintained drift records use version 2;
1358
1422
  `providers:check` retains version-1 endpoint/manual record parsing for historical
@@ -1712,9 +1776,13 @@ a table.
1712
1776
  Remote MCP tools classify from server annotations and deployment policy.
1713
1777
  Only `readOnlyHint === true` with `destructiveHint !== true` establishes a
1714
1778
  source read; missing or contradictory hints mean write. Deployment overrides
1715
- in `ConnectaConfig.classification` choose effective `read`, `write`, or `hidden`.
1716
- They cannot relax source replay safety. Effective or source write makes a call
1717
- write-like on every resend and recovery path. Source classifications travel in
1779
+ in `ConnectaConfig.classification` choose effective `read`, `write`, or `hidden`,
1780
+ except that `read` cannot change a tool the source explicitly declares a write:
1781
+ a host may resend a read-annotated call, so that tool stays a write unless the
1782
+ deployment accepts that with `read-repeatable`. They cannot
1783
+ relax source replay safety. Effective or source write makes a call
1784
+ write-like on every resend and recovery path. Source facts, including whether a
1785
+ source write was declared or only silent, travel in
1718
1786
  private metadata with host-owned tool clones and stay out of persisted catalogs.
1719
1787
  A leftover `classify` throws and names `ConnectaConfig.classification`.
1720
1788
  There are no hosted review maps, pins, drift comparisons or review UI fields.
@@ -2005,18 +2073,18 @@ storage is the root's namespaced to its principal.
2005
2073
 
2006
2074
  ### What runs on Effect
2007
2075
 
2008
- | Area | Shape |
2009
- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2010
- | Requests (`src/server.ts`, `src/routes/mcp.ts`, `src/routes/oauth.ts`) | One fiber per `fetch`, tied to `request.signal`. An `/mcp` request's admission permit and every `McpServer` it builds live in a Scope the response carries out, closed when the body ends, fails, or is cancelled, when the signal aborts, when its admitted-request lifetime ends in that request, or at once if the handler fails first. If workerd runs none of those callbacks, the next request reclaims only the expired admission record; it cannot close another request's Scope. The OAuth callback is uninterruptible: a single-use code's exchange and catalog invalidation are one commitment. |
2011
- | Admission (`executor-admission.ts`, `call-admission.ts`) | Queued waiters are Deferreds settled by whoever removes them from the queue; a wait is one flat race of grant, Clock timeout, and signal. The uncontended path stays synchronous. Two controllers, as [#453](https://github.com/zackbart/connecta/issues/453) requires. |
2012
- | One tool call (`invocation.ts`) | One fiber: resolution, the read-only and schema refusals, admission, and the downstream attempt sit under a single `withDeadlineEffect`, whose expiry interrupts the call wherever it is. The permit is an `acquireRelease`; the connector call is `Effect.tryPromise` over the unchanged `Connector`. |
2013
- | `execute_code` (`execute.ts`) | One fiber whose Scope owns the run's signal and executor lease, so a result, a throw, the watchdog, and cancellation all release the lease and abort the signal the same way. Public executor methods stay Promise-shaped; registered built-in execution composes directly as an Effect under the signal and `execute.watchdogMs`. Queued acquisition retains its Promise request handoff. Each guest host call is a fiber of its own. |
2014
- | Discovery (`catalog-service.ts`) | A request-scoped cache: one shared read per connector (`runtime/shared-read.ts`), settled by the read itself and carrying its own signal and the probe timeout whichever asker starts it. Each asker waits under its own deadline and signal, so one that times out or is cancelled fails alone, and the read is cancelled only once every asker has gone. Fan-out is `Effect.forEach` under the discovery concurrency; unscoped search adds the discovery budget and health record (`discovery-health.ts`). |
2015
- | Registry (`registry.ts`) | The result stash is a program over `Storage`. Same-request catalog loads share one read the way discovery's do. Remote MCP caching uses the SDK ResponseCacheStore adapter over SQL-backed KV, with no cross-request catalog flight. |
2016
- | Remote MCP (`connectors/remote-mcp.ts`, `connectors/remote-mcp/`) | Each request scope's state holds a Scope, each connection is a lease forked from it, and a connect in flight is a Deferred carrying the client it connected. Closing a session and the transport are each bounded to a second. |
2017
- | Downstream OAuth (`auth/downstream-oauth*`, `auth/static-oauth.ts`) | A refresh flight is a Deferred. Before the first durable coordinator write (claim CAS) is submitted, call/request closure stops preparation without a CAS, token request or state change. Submission transfers renewal to the storage coordinator under its own 35-second preparation deadline and 20-second token-request/body deadline, followed by the fenced rotation commit through the runtime deferred-work hook. A closed caller promptly fails without consuming the result, marking its ledger renewed or sending/resending a resource request. |
2018
- | Operator and activity data (`routes/operator.ts`) | Each JSON route is one program run by `serveOperator` behind the Promise `handle()`. Reads run under the request's signal; writes do not, so a vault write or OAuth disconnect that started reaches its cache invalidation. |
2019
- | QuickJS pool (`executors/quickjs.ts`, Node only) | Each child is a scoped resource whose release sends SIGTERM, then SIGKILL after a second; crash respawn backoff is a `Schedule`. |
2076
+ | Area | Shape |
2077
+ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2078
+ | Requests (`src/server.ts`, `src/routes/mcp.ts`, `src/routes/oauth.ts`) | One fiber per `fetch`, tied to `request.signal`. An `/mcp` request's admission permit and every `McpServer` it builds live in a Scope the response carries out, closed when the body ends, fails, or is cancelled, when the signal aborts, when its admitted-request lifetime ends in that request, or at once if the handler fails first. If workerd runs none of those callbacks, the next request reclaims only the expired admission record; it cannot close another request's Scope. The OAuth callback is uninterruptible: a single-use code's exchange and catalog invalidation are one commitment. |
2079
+ | Admission (`executor-admission.ts`, `call-admission.ts`) | Queued waiters are Deferreds settled by whoever removes them from the queue; a wait is one flat race of grant, Clock timeout, and signal. The uncontended path stays synchronous. Two controllers, as [#453](https://github.com/zackbart/connecta/issues/453) requires. |
2080
+ | One tool call (`invocation.ts`) | One fiber: resolution, the read-only and schema refusals, admission, and the downstream attempt sit under a single `withDeadlineEffect`, whose expiry interrupts the call wherever it is. The permit is an `acquireRelease`; the connector call is `Effect.tryPromise` over the unchanged `Connector`. |
2081
+ | `execute_code` (`execute.ts`) | One fiber whose Scope owns the run's signal and executor lease, so a result, a throw, the watchdog, and cancellation all release the lease and abort the signal the same way. Public executor methods stay Promise-shaped; registered built-in execution composes directly as an Effect under the signal and `execute.watchdogMs`. Queued acquisition retains its Promise request handoff. Each guest host call is a fiber of its own. |
2082
+ | Discovery (`catalog-service.ts`) | A request-scoped cache: one shared read per connector (`runtime/shared-read.ts`), settled by the read itself and carrying its own signal and the probe timeout whichever asker starts it. Each asker waits under its own deadline and signal, so one that times out or is cancelled fails alone, and the read is cancelled only once every asker has gone. Fan-out is `Effect.forEach` under the discovery concurrency, which an unscoped search or native listing applies only to connectors it must ask; those add a cached-catalog peek and stale listing, and unscoped search the discovery budget and health record (`discovery-health.ts`). |
2083
+ | Registry (`registry.ts`) | The result stash is a program over `Storage`. Same-request catalog loads share one read the way discovery's do. Remote MCP caching uses the SDK ResponseCacheStore adapter over SQL-backed KV, with no cross-request catalog flight. |
2084
+ | Remote MCP (`connectors/remote-mcp.ts`, `connectors/remote-mcp/`) | Each request scope's state holds a Scope, each connection is a lease forked from it, and a connect in flight is a Deferred carrying the client it connected. Closing a session and the transport are each bounded to a second. |
2085
+ | Downstream OAuth (`auth/downstream-oauth*`, `auth/static-oauth.ts`) | A refresh flight is a Deferred. Before the first durable coordinator write (claim CAS) is submitted, call/request closure stops preparation without a CAS, token request or state change. Submission transfers renewal to the storage coordinator under its own 35-second preparation deadline and 20-second token-request/body deadline, followed by the fenced rotation commit through the runtime deferred-work hook. A closed caller promptly fails without consuming the result, marking its ledger renewed or sending/resending a resource request. |
2086
+ | Operator and activity data (`routes/operator.ts`) | Each JSON route is one program run by `serveOperator` behind the Promise `handle()`. Reads run under the request's signal; writes do not, so a vault write or OAuth disconnect that started reaches its cache invalidation. |
2087
+ | QuickJS pool (`executors/quickjs.ts`, Node only) | Each child is a scoped resource whose release sends SIGTERM, then SIGKILL after a second; crash respawn backoff is a `Schedule`. |
2020
2088
 
2021
2089
  ### Native registry and request services (contributor)
2022
2090
 
@@ -2336,8 +2404,11 @@ late publication from an invalidated request unreachable. Missing chunks,
2336
2404
  expired entries, storage errors, or a failed page trigger a fresh complete
2337
2405
  listing. A transient first-page tool-listing failure may use a complete catalog
2338
2406
  within five minutes of expiry in the same auth partition. Partial walks and
2339
- auth or permission failures refuse fallback; resources require unexpired
2340
- inventories, and stale fallback cannot authorize post-entry auth recovery.
2407
+ auth or permission failures refuse fallback, and a refusal fences later fallback
2408
+ until a listing succeeds; resources require unexpired inventories, and stale
2409
+ fallback cannot authorize post-entry auth recovery.
2410
+ Discovery's [stale listing](#storage-credentials-and-connectors) reads the same
2411
+ entries and answers no call.
2341
2412
 
2342
2413
  **Repeated reads share one request's memo.** `src/runtime/request-reads.ts`
2343
2414
  gives each request owner a memo of storage reads, emptied when the request
@@ -2596,11 +2667,9 @@ this declaration keep parameter-based continuation.
2596
2667
  An explicit initialAnchor adapter on parameter-based paging can retain a numeric
2597
2668
  timestamp as a first-page time-window anchor; opaque string continuations still
2598
2669
  require a signed token.
2599
- The generated Paging convention follows the read tool's input schema: tools
2600
- that take `cursor` restore `page.nextCursor` as that top-level argument; tools
2601
- without it restore the token in `query[page.param]` or `body[page.param]`, as
2602
- `page.in` specifies. `kit.hatch.paging` still validates declared mode values as
2603
- a sanity check; it does not select the generated continuation instruction.
2670
+ A read tool's input schema states how to continue: tools that take `cursor`
2671
+ restore `page.nextCursor` as that top-level argument; tools without it restore
2672
+ the token in `query[page.param]` or `body[page.param]`, as `page.in` specifies.
2604
2673
  An unsafe continuation keeps the safe page data with `hasMore: true` and no
2605
2674
  `nextCursor`.
2606
2675
  A reviewed `HatchBinding` can supply vendor encoding/origin routing through
@@ -2649,7 +2718,8 @@ contracts, `Summary` annotations and lenient success schemas become the
2649
2718
  index's parameters, bodies, summaries and declared fields (optional
2650
2719
  `OpenApiData.fields`). Kit facts carry the vendor's declared
2651
2720
  `VendorErrors.meanings` and an `idempotencyKey` checked against the write
2652
- builders at construction, which the generated Conventions state.
2721
+ builders at construction; the generated Conventions state the key rule only where
2722
+ a provider has one, and the Errors line always.
2653
2723
 
2654
2724
  Hatch request paths and query keys/values refuse any containment of the shared
2655
2725
  `[redacted]` marker, including decoded spellings. Body fields and kit write
@@ -2660,8 +2730,7 @@ Admission iteratively visits keys and values before the write program runs;
2660
2730
  it checks agent arguments, not whole bodies later composed from internal
2661
2731
  reads. The kit connection also checks actual typed path parameters and query
2662
2732
  values before transport. Legacy non-kit tools retain their link-only admission;
2663
- arbitrary binary/base64 bytes are not decoded by this scalar rule. The kit
2664
- sets `refusesWithheld` for the standard hatch, enabling the generated convention.
2733
+ arbitrary binary/base64 bytes are not decoded by this scalar rule.
2665
2734
  Declared error-code facts use the closed `ConnectorCallErrorCode` set at
2666
2735
  construction (INV-11); classification callbacks are not run at construction.
2667
2736
 
@@ -3362,7 +3431,7 @@ compiling and configuring the real thing.
3362
3431
  is not a declared refusal standing for the whole call makes the call
3363
3432
  unknown, never retryable, with no wait and no resend or key-reuse advice;
3364
3433
  while any such 401 is unrenewed the verdict keeps an authorization code,
3365
- so invocation keeps the `authorize_connector` handoff and picks its
3434
+ so invocation keeps the authorization handoff and picks its
3366
3435
  reconcile-first words. Its code and words are the failure's when that is an
3367
3436
  authorization verdict, else the 401's own recorded verdict, else
3368
3437
  `auth_required`, with a sentence saying the write was sent before the
@@ -3772,6 +3841,16 @@ change. All 88 declarations still exclude Effect and have complete references.
3772
3841
 
3773
3842
  ### Native remote MCP programs (contributor)
3774
3843
 
3844
+ Legacy session termination may read a token before rotation and send its DELETE
3845
+ on the wire after rotation. This is accepted only for bounded, headers-only
3846
+ termination of the old session, never for application traffic (`tools/call`,
3847
+ `tools/list`, or other RPCs). Connection scope close retains the same cleanup
3848
+ completion after revocation; later connector close joins it so the runtime's
3849
+ deferred hook (Workers `waitUntil`) owns the bounded tail. Abandoned leases from
3850
+ rotation and failed connects, and authorization exchange transports, retain their
3851
+ cleanup in that same request state. Termination and local close keep their
3852
+ existing one-second budgets; the caller-facing close window remains 100 ms.
3853
+
3775
3854
  Remote MCP connection joins, credential and epoch checks, catalog intake,
3776
3855
  negotiation, calls, resources, Skills, auth and status compose native programs.
3777
3856
  `connectors/remote-mcp.ts` keeps the published options, the redirect guard and
@@ -3908,7 +3987,8 @@ physical-attempt and body/admission suites remain compatibility evidence.
3908
3987
 
3909
3988
  The standard hatch privately registers bounded vendor operation text against its
3910
3989
  ApiTool identity; `apiConnector` transfers those facts to the constructed
3911
- connector. `operation-vocabulary.ts` contains only pure lexical/cache logic and
3990
+ connector, and a provider that wraps that connector carries them to its outer
3991
+ object with `carryConnectorVocabulary`. `operation-vocabulary.ts` contains only pure lexical/cache logic and
3912
3992
  types. Catalog discovery consumes those facts after loading and classifying a
3913
3993
  complete visible catalog. It never imports a provider or an operation index,
3914
3994
  parses operation contracts, invokes a handler, or authenticates. Matching requires
@@ -3918,3 +3998,32 @@ cache. Exact authored names and complete authored matches precede API pointers;
3918
3998
  ordinary result limits, offsets, catalog failure disclosure and byte bounds apply.
3919
3999
  The published discovery contract states compilation, query and cache bounds.
3920
4000
  No request identity, signal, credential or promise enters a vocabulary cache.
4001
+
4002
+ SQL catalog locating snapshots derive at most two manifest candidates from the
4003
+ stored generation alongside negotiation and generation rows. These candidates
4004
+ are request-local hints with exact config, owner and credential partitions.
4005
+ Complete chunk verification and a separate later primary generation and identity
4006
+ fence decide acceptance. OAuth invocation and dispatch reads stay separate.
4007
+
4008
+ SQL result stashes write at most 64 trailing rows per awaited transaction, with one
4009
+ expiry sweep per batch. Header publication follows confirmed trailing writes in a
4010
+ separate transaction; uncertain writes retain their ledger charge until expiry.
4011
+ Custom storage keeps sequential writes.
4012
+
4013
+ Admission observation sampling retains at most 256 scalar deadlines per live
4014
+ physical storage identity in an isolate. Only a recent valid stored timestamp
4015
+ or confirmed CAS populates it, for at most one minute. This telemetry cache
4016
+ never answers token admission; every request still checks current credentials.
4017
+
4018
+ ### Lexical token reuse (contributor)
4019
+
4020
+ Search may retain four complete accepted catalog token indexes, bounded by an
4021
+ 8 MiB estimate including key text, token strings, postings and structural slots.
4022
+ Identity includes complete served content (including schemas, classification and
4023
+ order), connector, physical store, owner partition/revisions and the downstream catalog
4024
+ generation observed by the current successful listing. The generation marker
4025
+ is request-local primitive evidence; it grants no cache acceptance or authority. Only immutable
4026
+ strings and numeric positions cross requests. Search views map positions to the
4027
+ current request's definitions after owner visibility, classification, redaction
4028
+ and endpoint restrictions; no query results are retained. Partial failed
4029
+ catalogs cannot enter this cache. Eviction changes cost only, never results.