@polpo-ai/connect-server 0.15.143 → 0.16.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 (83) hide show
  1. package/README.md +352 -1
  2. package/dist/custom-tool-runner-broker.d.ts +29 -0
  3. package/dist/custom-tool-runner-broker.d.ts.map +1 -0
  4. package/dist/custom-tool-runner-broker.js +140 -0
  5. package/dist/custom-tool-runner-broker.js.map +1 -0
  6. package/dist/definition-store.d.ts +10 -0
  7. package/dist/definition-store.d.ts.map +1 -0
  8. package/dist/definition-store.js +25 -0
  9. package/dist/definition-store.js.map +1 -0
  10. package/dist/index.d.ts +8 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +8 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/mcp-capability-resolver.d.ts +22 -0
  15. package/dist/mcp-capability-resolver.d.ts.map +1 -0
  16. package/dist/mcp-capability-resolver.js +110 -0
  17. package/dist/mcp-capability-resolver.js.map +1 -0
  18. package/dist/mcp-discovery.d.ts +7 -0
  19. package/dist/mcp-discovery.d.ts.map +1 -0
  20. package/dist/mcp-discovery.js +27 -0
  21. package/dist/mcp-discovery.js.map +1 -0
  22. package/dist/mcp-execution.d.ts +13 -0
  23. package/dist/mcp-execution.d.ts.map +1 -0
  24. package/dist/mcp-execution.js +14 -0
  25. package/dist/mcp-execution.js.map +1 -0
  26. package/dist/mcp-oauth-redirect.d.ts +4 -0
  27. package/dist/mcp-oauth-redirect.d.ts.map +1 -0
  28. package/dist/mcp-oauth-redirect.js +25 -0
  29. package/dist/mcp-oauth-redirect.js.map +1 -0
  30. package/dist/mcp-oauth.d.ts +12 -9
  31. package/dist/mcp-oauth.d.ts.map +1 -1
  32. package/dist/mcp-oauth.js +18 -6
  33. package/dist/mcp-oauth.js.map +1 -1
  34. package/dist/mcp-runner-broker.d.ts +29 -0
  35. package/dist/mcp-runner-broker.d.ts.map +1 -0
  36. package/dist/mcp-runner-broker.js +181 -0
  37. package/dist/mcp-runner-broker.js.map +1 -0
  38. package/dist/mcp-setup-clients.d.ts +24 -0
  39. package/dist/mcp-setup-clients.d.ts.map +1 -0
  40. package/dist/mcp-setup-clients.js +113 -0
  41. package/dist/mcp-setup-clients.js.map +1 -0
  42. package/dist/mcp-transport.d.ts +20 -0
  43. package/dist/mcp-transport.d.ts.map +1 -0
  44. package/dist/mcp-transport.js +163 -0
  45. package/dist/mcp-transport.js.map +1 -0
  46. package/dist/memory-store.d.ts +11 -2
  47. package/dist/memory-store.d.ts.map +1 -1
  48. package/dist/memory-store.js +81 -6
  49. package/dist/memory-store.js.map +1 -1
  50. package/dist/oauth-identity.d.ts +6 -0
  51. package/dist/oauth-identity.d.ts.map +1 -0
  52. package/dist/oauth-identity.js +30 -0
  53. package/dist/oauth-identity.js.map +1 -0
  54. package/dist/oauth-token-transport.d.ts +19 -0
  55. package/dist/oauth-token-transport.d.ts.map +1 -0
  56. package/dist/oauth-token-transport.js +133 -0
  57. package/dist/oauth-token-transport.js.map +1 -0
  58. package/dist/oauth.js +1 -1
  59. package/dist/oauth.js.map +1 -1
  60. package/dist/public-network.d.ts +11 -0
  61. package/dist/public-network.d.ts.map +1 -0
  62. package/dist/public-network.js +49 -0
  63. package/dist/public-network.js.map +1 -0
  64. package/dist/refresh-lineage.d.ts +21 -0
  65. package/dist/refresh-lineage.d.ts.map +1 -0
  66. package/dist/refresh-lineage.js +83 -0
  67. package/dist/refresh-lineage.js.map +1 -0
  68. package/dist/secrets.d.ts +2 -0
  69. package/dist/secrets.d.ts.map +1 -1
  70. package/dist/secrets.js.map +1 -1
  71. package/dist/service.d.ts +78 -8
  72. package/dist/service.d.ts.map +1 -1
  73. package/dist/service.js +1090 -403
  74. package/dist/service.js.map +1 -1
  75. package/dist/setup-lifecycle.d.ts +5 -0
  76. package/dist/setup-lifecycle.d.ts.map +1 -0
  77. package/dist/setup-lifecycle.js +36 -0
  78. package/dist/setup-lifecycle.js.map +1 -0
  79. package/dist/verification.d.ts +4 -0
  80. package/dist/verification.d.ts.map +1 -0
  81. package/dist/verification.js +76 -0
  82. package/dist/verification.js.map +1 -0
  83. package/package.json +4 -3
package/README.md CHANGED
@@ -12,4 +12,355 @@ This package implements the backend lifecycle for API-key and OAuth2 connections
12
12
  - policy checks before token release
13
13
  - revocation and secret deletion
14
14
 
15
- It is intentionally framework-agnostic. Cloud/OSS servers can mount it through Hono, Express, Workers, or another transport without coupling the core lifecycle to HTTP routes.
15
+ It is independent of the HTTP framework. Node hosts can mount it through Hono,
16
+ Express, or another transport. Its default DNS and crypto adapters use Node APIs;
17
+ do not infer direct Workers compatibility without a tested host adapter.
18
+
19
+ ## Host responsibilities
20
+
21
+ Pass legacy providers or version 2 Connector definitions to
22
+ `createConnectService`. `listCatalog()` returns a normalized asynchronous
23
+ catalog including registered custom definitions; `listProviders()` retains the
24
+ static compatibility catalog. Configure these ports as needed:
25
+
26
+ - `store`, `secrets`, `links`, `setupSessions`: durable account, encrypted
27
+ credential, project-link and single-use setup/state storage.
28
+ - `oauthClients`: explicit operator/customer/managed registration resolution.
29
+ A missing selected client must fail, not fall back to another owner.
30
+ - `definitions`: tenant-scoped `ConnectorDefinitionStore`; duplicate creation
31
+ must be atomic and IDs cannot be reused after disablement.
32
+ - `verifyMcp`: a host adapter that initializes the native MCP client and lists
33
+ tools using the resolved credential and supplied abort signal. Use
34
+ `discoverRemoteMcpTools({ url, transport, headers, signal })` after resolving
35
+ headers inside the trusted host. It supports HTTPS Streamable HTTP and SSE,
36
+ refuses cross-origin credential forwarding, pins public DNS and bounds time,
37
+ response size and pagination. It throws on failure; an empty array means a
38
+ successful `tools/list` response with no tools. It never calls a tool.
39
+ - `verifications`: optional persistence for sanitized verification results.
40
+ - `policy` and the trusted capability resolver: authorization checks before
41
+ execution, including user/tenant binding and operation restrictions.
42
+ - `refreshCoordinator`: distributed coordination for multi-replica hosts.
43
+
44
+ Multi-replica OAuth refresh also requires a `VersionedConnectionSecretStore`
45
+ with atomic compare-and-set, as supplied by the Memory and Drizzle adapters.
46
+ Before an API or MCP refresh consumes a potentially single-use token, the service
47
+ stores a private intent inside the encrypted credential. A replica acquiring an
48
+ expired coordinator lease cannot reuse a token whose attempt is still pending.
49
+ The result is committed against that intent, including a receipt for recovering
50
+ a lost storage acknowledgement. This state is never public Connection metadata.
51
+
52
+ A lost provider response or crash after dispatch leaves the outcome uncertain.
53
+ Automatic retries of that old refresh token are denied; a still-running owner
54
+ can finish its commit, otherwise the account needs a new authorization. Replacing
55
+ the credential creates a new lineage. Unversioned secret adapters retain their
56
+ single-process coordinator behavior and do not provide this cross-replica fence.
57
+ All refresh writers must be upgraded before enabling this guarantee: older
58
+ binaries do not understand the intent, and must be drained during cutover.
59
+
60
+ Memory adapters are for tests or ephemeral hosts. They do not make setup state,
61
+ credentials or custom definitions durable across process restarts. For durable
62
+ self-hosting, `@polpo-ai/drizzle/connect` provides SQLite and PostgreSQL adapters
63
+ for these ports, with encrypted payloads and atomic state/secret updates.
64
+ See [self-hosted setup](../../drizzle/README.md#connect-storage).
65
+
66
+ The Node `PolpoServer` and `createApp` accept an explicit `connectService` and
67
+ `connectionCapabilityResolver`. Management routes mount at `/api/v1/connect`
68
+ behind the normal server authentication. The public browser callback is
69
+ `/api/v1/connect/oauth/callback`; register that exact canonical URL at the
70
+ provider. It exposes no credential or account record, and redirects only to a
71
+ return URL previously validated during setup. Installing packages alone does
72
+ not configure providers, OAuth apps, grants or a Cloud dashboard.
73
+
74
+ ## Lifecycle guarantees
75
+
76
+ If a reference write loses its acknowledgement, the service recovers only a
77
+ positively confirmed matching credential generation or OAuth state. A failed
78
+ read or absent row does not prove rollback: the service preserves the staged
79
+ secret and reports the original error. Cleanup is immediate only before a
80
+ reference write is attempted or after an explicit reconnect CAS miss. Hosts
81
+ must reconcile uncertain writes before reclaiming genuinely unreferenced
82
+ credentials; the included adapters currently provide no orphan-secret GC/TTL.
83
+ Durable secret stores encrypt retained material; memory stores are ephemeral.
84
+
85
+ Static API/public/MCP creation and MCP OAuth share `ConnectionCreationContext`:
86
+ explicit audience, owner and optional trusted binding. The default remains
87
+ shared for compatibility. Personal and end-user owners produce a matching
88
+ principal restriction; conflicting, missing or null constraints fail before
89
+ secret persistence or provider discovery. Hosts authorize the caller before
90
+ accepting these control-plane inputs. They must not populate them from model
91
+ arguments or unverified browser identity.
92
+
93
+ The OSS HTTP surface includes `POST /connections/mcp`, `/mcp/inspect` and
94
+ `/mcp/oauth/start` under the authenticated Connect mount. API/public creation
95
+ preserves the same context. MCP OAuth state and callback preserve namespace,
96
+ audience and bindings. A selected Connector's auth method remains authoritative;
97
+ generic MCP header injection is validated and persisted in the initial write.
98
+ Creation strips host-observed discovery metadata rather than accepting a caller's
99
+ tool inventory. Durable hosts must condition discovery persistence on unchanged
100
+ account, credential generation, scopes, binding and endpoint metadata, so an old
101
+ network response cannot replace newer configuration or discovery evidence.
102
+
103
+ The selected authentication method is persisted through setup and callback.
104
+ New authorizations pin the registered OAuth client ID and endpoints; replacing
105
+ the app requires reconnect, while rotating its secret does not. Store adapters
106
+ must preserve `authenticationId`, `oauthClientFingerprint` and
107
+ `credentialVersion`. Existing records without those fields remain compatible
108
+ and need an explicit migration/reconnect strategy at the host boundary.
109
+
110
+ Code exchange and refresh use bounded HTTPS requests with public-address checks,
111
+ no redirects, validated token responses and sanitized errors. Gateway deadlines
112
+ cover DNS, redirects and streamed response bodies. Hosts still need appropriate
113
+ network egress controls. The default Node transport validates DNS in the socket
114
+ connector and supplies those exact public addresses to TLS, preventing a second
115
+ unvalidated DNS resolution. A custom `fetch` adapter must enforce the equivalent
116
+ socket/egress policy itself. MCP OAuth discovery, registration and token bodies
117
+ are bounded before the protocol SDK parses them.
118
+
119
+ `verifyConnection` runs configured read probes through the same gateway and
120
+ scope policy. Results report the checked generation, never response bodies or
121
+ credentials. Hosts must rotate `credentialVersion` on credential replacement
122
+ and disregard cached checks after relevant configuration or credential changes.
123
+ Verified account identities populate `Connection.providerAccountId`.
124
+
125
+ `getSetupReadiness` is a separate, local configuration check. It resolves the
126
+ chosen OAuth Client from the same resolver used by setup and validates the
127
+ exact callback. A `passed` / `consent_required` result means authorization can
128
+ be attempted; it cannot certify the app's provider publication or approval.
129
+ Static-key setup still needs the account key; MCP requires discovery of the
130
+ actual server. No readiness check calls the provider or exposes client secrets.
131
+
132
+ An OAuth provider can return scopes granted by a previous consent to the same
133
+ app. Actual token scopes remain in encrypted token storage; a Connection keeps
134
+ only its explicitly requested scope ceiling. Neither extra scopes nor refresh
135
+ can silently widen the Connection or a narrower Polpo grant.
136
+
137
+ Both API and MCP OAuth refreshes preserve that ceiling, recheck revocation and
138
+ use versioned secret writes when supplied. Multi-process hosts must also supply
139
+ a shared refresh coordinator. Revocation first denies access, then removes the
140
+ credential; if vault deletion fails, repeating revocation retries cleanup.
141
+
142
+ ## Connection-backed MCP execution
143
+
144
+ `createConnectionMcpCapabilityResolver` uses the same exact-account selection
145
+ and live authorization as HTTP capabilities. The host supplies `resolveSpec`,
146
+ `resolveEndpoint` and `resolveHeaders`; the endpoint comes from the installed
147
+ Connection, and credential resolution/refresh stays in the Connect service.
148
+ The MCP SDK does not start OAuth or write tokens during a tool call.
149
+
150
+ Each resolved capability authorizes one named operation. Its deadline includes
151
+ account acquisition, host policy and credential refresh. Before every HTTP
152
+ send it rechecks the account, binding, project link, grant, credential generation
153
+ and endpoint. Cancellation cannot fall back to another account or transport.
154
+ The native transport checks public HTTPS destinations, refuses redirects, bounds
155
+ response bodies and terminates stateful Streamable HTTP sessions. Cleanup is
156
+ bounded and cannot turn an already completed operation into a retryable failure.
157
+
158
+ Agent runtimes use `ResolveMcpRuntimeCapabilities` from `@polpo-ai/core` and
159
+ `resolveRuntimeMcpTools` from `@polpo-ai/tools`. A host returns a non-secret,
160
+ verified inventory with a resolver for each server namespace. Tool execution
161
+ acquires capabilities with the actual immutable invocation and tool-call ID;
162
+ model arguments never select credentials or account identity. The optional
163
+ `onError(error, input)` hook is host-only. Model-visible errors are redacted.
164
+
165
+ Node hosts inject `resolveMcpCapabilities` through server options or the
166
+ in-process run dependencies. Chat and task/Loop execution share the adapter,
167
+ tool policy and cancellation semantics. Connection-backed configuration requires
168
+ that host port; `connectionId` survives API serialization and cannot downgrade
169
+ to an authored URL when the port is unavailable. The standalone subprocess
170
+ runner can bootstrap this port through the broker contract below; without an
171
+ explicit host-issued lease it rejects Connection-backed configuration. Task
172
+ engines keep legacy stdio/URL loading disabled; standalone
173
+ chat retains its explicit compatibility path for unclaimed namespaces.
174
+
175
+ ### Subprocess broker contract
176
+
177
+ Core exports `createRemoteMcpRuntimeCapabilities` and strict schemas for a
178
+ versioned `McpRunnerLeasePayload`, inventory and requests. Connect Server exports
179
+ `createMcpRunnerBroker`, an HTTP-neutral Request/Response handler. Hosts supply
180
+ opaque lease storage, immutable invocation and inventory, a native capability
181
+ factory restricted to the minted grants, live run/generation authorization and
182
+ an atomic per-lease `claimCall` callback. Repeated call IDs must return 409 even
183
+ if the first operation failed or its response was lost. There is no automatic
184
+ retry of provider mutations.
185
+
186
+ Only `inventory` and `call { capability, toolCallId, arguments }` cross the
187
+ runner boundary. The runner cannot submit identity, destination, credentials or
188
+ permission overrides. The host checks the inventory against current tool
189
+ descriptors, then uses the existing native MCP resolver. Its authorization must
190
+ also recheck lease/run/rollout during every native transport send. Requests have
191
+ a 30-second total deadline, 256-KiB input and 2-MiB response limits. Disposal is
192
+ bounded, including cancellation during acquisition. Responses and errors never
193
+ include the host's raw diagnostics.
194
+
195
+ The Node runner accepts `--capabilities-stdin`. Before loading configuration it
196
+ emits `POLPO_RUNNER_CAPABILITIES_READY_V3`, consumes one newline-delimited JSON
197
+ envelope (16-KiB maximum, 30-second timeout), pauses stdin and removes listeners.
198
+ The version-3 envelope carries a shared agent/run/sandbox identity and optional
199
+ `mcp`, `data` and `customTools` leases; at least one must exist. MCP and custom
200
+ tools bind the captured agent incarnation as well as its name. It validates every
201
+ nested binding before constructing ports. Absent Data and custom-tool bindings
202
+ explicitly disable ambient Data and local tool-import fallbacks. The remote
203
+ protocols remain independent (MCP lease version 2, custom-tool lease version 1).
204
+ Old bootstrap versions are rejected; deploy the host and runner together. The host must
205
+ disable input echo, wait for readiness, send the frame once, and revoke it on
206
+ every exit. Do not put the bearer in argv, shell commands, persistent sandbox
207
+ environment, RunnerConfig or RunStore. A late or uncertain delivery requires
208
+ revocation and process termination, not retransmission. This is a host adapter
209
+ contract; providing these OSS primitives does not itself configure Cloud lease
210
+ storage or a remote sandbox lifecycle.
211
+
212
+ ### Remote custom tools
213
+
214
+ `ResolveCustomTools` lets a trusted host own custom-tool loading and isolated
215
+ execution. The Node runtime keeps exact-name/`*` assignment and narrower tool
216
+ policies. A configured host returning no tools or throwing never enables local
217
+ bundle imports; an unconfigured standalone OSS runtime retains its local loader.
218
+
219
+ `createRemoteCustomTools` implements this port using a run lease, and
220
+ `createCustomToolRunnerBroker` supplies its HTTP-neutral host endpoint. The wire
221
+ contains only `inventory` and `call { toolName, toolCallId, arguments }`.
222
+ The host supplies a frozen inventory, immutable invocation, absolute lease expiry, atomic call-ID
223
+ consumption, live lease/run authorization and an isolated tool resolver. The
224
+ broker compares current public metadata with the issued inventory before
225
+ execution. The host must recheck authorization after asynchronous preparation
226
+ and immediately before delivering credentials, and clean up on cancellation.
227
+ Provider/account choice, Connection slots and gateway/direct credential mode
228
+ remain host-owned. This is an agent-run endpoint, not a public integration API.
229
+
230
+ The transport never retries calls or follows redirects, strips raw diagnostic
231
+ errors, and limits requests to 2 MiB and results to 8 MiB. Inventory has a
232
+ 30-second deadline; execution allows the existing 30-minute tool ceiling plus
233
+ 30 seconds for setup/cleanup, bounded by lease expiry in both client and server
234
+ even when a caller ignores client limits. The host applies
235
+ each tool's narrower timeout. Neither bearer tokens nor provider credentials
236
+ appear in the model-visible inventory.
237
+
238
+ ### OAuth host authorization
239
+
240
+ `createConnectService({ authorizeOAuth })` accepts an optional host-only hook.
241
+ It receives a frozen `OAuthOperationContext`: `operation` (`setup`, `authorize`,
242
+ `callback`, `credential`, `refresh`), `protocol` (`api` or `mcp`), and Connector,
243
+ Connection and tenant identifiers. It never receives OAuth codes, tokens,
244
+ client secrets or metadata. Throw to deny the operation. Without the hook,
245
+ self-hosted operation is unchanged.
246
+
247
+ The shared service checks before onboarding side effects, before exchanging a
248
+ callback code, before reading OAuth credentials, inside the refresh coordinator,
249
+ and before releasing a credential or sending an API request. Disabling the host
250
+ policy during provider exchange prevents activation. If a provider has already
251
+ rotated tokens, their encrypted replacement is retained, but disabled callers
252
+ receive no credential. Configuration reads and revocation remain available;
253
+ static API keys, static MCP tokens and public MCP do not use this OAuth hook.
254
+
255
+ Hosts must bind runtime evaluation to the **invoking** project. A shared
256
+ Connection's original `projectId` is not the execution context. This hook is
257
+ additional host authorization; it does not replace Connection selection,
258
+ tenant visibility, grants or provider scopes. It cannot recall credentials that
259
+ a host previously exported to a legacy consumer.
260
+
261
+ ### Setup status, cancellation and receipt recovery
262
+
263
+ `getSetupStatus(id)` returns a public projection without owner, binding, OAuth
264
+ client, callback metadata or private persistence references. `cancelSetupSession`
265
+ competes atomically with authorization start; cancellation is idempotent but
266
+ cannot undo an authorization already started. Legacy rows without an explicit
267
+ status derive pending/started from `consumedAt`. Setup-link expiry is the start
268
+ window; OAuth state expiry becomes the status deadline after start.
269
+
270
+ Memory and Drizzle stores implement status, cancellation and terminal receipts.
271
+ For separate account/link/receipt writes, they also persist a private completion
272
+ intent before account persistence. A subsequent status read reconciles only the
273
+ exact recorded account generation and active project link. It does not exchange
274
+ tokens, search caller metadata, create links or reactivate revoked access. Drizzle
275
+ persists the intent encrypted and supports recovery after process restart.
276
+
277
+ Hosts with an atomic credential/account/link/receipt transaction may instead
278
+ implement `finishConnectionSetupSession` by verifying that transaction's receipt.
279
+ Custom adapters must implement the optional cancellation/completion ports to
280
+ provide the full lifecycle, and either atomic completion or the preparation and
281
+ reconciliation ports for durable receipt recovery. A missing or modified account
282
+ or link is never reported as a recovered success. Older interrupted flows without
283
+ a prepared intent cannot be reconstructed safely and require operator review.
284
+
285
+
286
+ ### Reusable MCP client configuration contract
287
+
288
+ `McpOAuthClientResolver` resolves host-owned configurations independently of
289
+ user consent. Ownership (`managed`, `customer`, `instance`) is distinct from
290
+ MCP client registration (`dynamic`, `metadata_document`, `pre_registered`). A
291
+ configuration pins its Connector/authentication, owner, MCP resource/transport,
292
+ callback and registration identity. A backend may select a configuration ID;
293
+ the host must reject an out-of-scope or ambiguous selection.
294
+
295
+ `resolveMcpOAuthSetupClient` validates a scoped selection and returns the private
296
+ resolved client plus a non-secret reference/fingerprint. Only that reference
297
+ belongs in a setup session. `verifyMcpOAuthSetupClient` re-resolves it and refuses
298
+ identity, owner, endpoint or callback changes; secret-only rotation is permitted.
299
+ Supply the resolver as `mcpOAuthClients` to enable MCP on `createSetupSession`.
300
+ `configurationId` selects an approved configuration; identity comes from the
301
+ authenticated application backend. `startOAuthSetup` and `completeOAuthCallback`
302
+ use the same setup lifecycle for all three registration methods and ownership
303
+ modes. The account belongs to the saved subject and is linked to the initiating
304
+ project. Neither caller metadata nor direct MCP start can create setup authority.
305
+
306
+ MCP DCR adapters receive `registrationKey`. When present they must use it and must
307
+ not fall back to old unkeyed entries. It incorporates the private configuration
308
+ namespace, resource, authorization/registration/token endpoints, callback, client
309
+ branding and requested scopes. Reusing the same MCP URL is insufficient evidence
310
+ that its authorization server or client identity is unchanged. User tokens remain
311
+ separate from this client-registration cache.
312
+
313
+ ### Atomic MCP OAuth callback activation
314
+
315
+ MCP OAuth hosts must implement `ConnectStore.commitMcpOAuthConnection` and the
316
+ versioned secret store contract. The runtime refuses new consent before discovery
317
+ if either is missing; it never falls back to separate account/receipt writes.
318
+ The commit inserts a new account and completes the current callback claim in one
319
+ atomic operation. Expired/replaced claims and conflicting account IDs are denied,
320
+ and a delayed token exchange cannot overwrite a newer secret generation.
321
+ Memory and the encrypted SQLite/PostgreSQL adapters implement this contract.
322
+ Custom adapters must implement it before adopting this runtime version.
323
+ Embedded MCP hosts instead need `commitMcpOAuthSetup`, `failMcpOAuthSetup`, private
324
+ setup-reference lookup, setup completion, link storage, callback claims and
325
+ versioned secrets. Creation checks these capabilities before issuing a session.
326
+ Activation commits the account, project link, setup receipt and OAuth receipt in
327
+ one operation. Provider denial conditionally ends both receipts under the current
328
+ unexpired claim; a stale callback cannot fail a newer attempt. Completed callbacks
329
+ can be retried after a lost acknowledgement, but never recreate revoked links.
330
+
331
+ The generic resolver is rechecked before consent, exchange, activation and
332
+ credential delivery. Its last successful lookup is the configuration authorization
333
+ boundary in OSS: an external resolver cannot participate atomically in the generic
334
+ store's transaction. Hosts requiring atomic configuration revocation must also
335
+ check/lock the live configuration in their `commitMcpOAuthSetup` implementation.
336
+ Project authorization and approved return-origin policy likewise belong to the
337
+ host. These contracts do not imply a deployed managed implementation.
338
+
339
+ ### Host dispatch budgets
340
+
341
+ `createConnectService({ beforeDispatch })` accepts an optional trusted host
342
+ quota callback. It runs before each API send, including allowed redirects and
343
+ API verification probes. A runtime may provide the request-level `beforeDispatch`
344
+ instead to use its authenticated invocation principal; the service calls one
345
+ quota callback per send, never both. Authorization and credential preflight do
346
+ not consume quota. After the asynchronous callback, the service rechecks the
347
+ account generation, granted scopes and invocation authorization before sending.
348
+ An authorization change can consume budget without sending; budget is not a
349
+ billing record and is not refunded after uncertain writes.
350
+
351
+ Native MCP accepts the corresponding transport callback for every HTTP request
352
+ (initialization, notifications, discovery, calls, listening and cleanup). The
353
+ Connection capability resolver also rechecks the selected account, current grant,
354
+ project visibility and endpoint after waiting. A custom `verifyMcp` adapter must
355
+ call its supplied `beforeDispatch` immediately before every provider request;
356
+ `discoverRemoteMcpTools` accepts it directly. Do not expose these callbacks or
357
+ quota identity to authored tools or model arguments.
358
+
359
+ Hosts may throw `ConnectError("rate_limited", ..., { details: {
360
+ retryAfterSeconds } })` for a quota refusal (HTTP 429), or `dispatch_unavailable`
361
+ for unavailable quota infrastructure (HTTP 503). The MCP broker/subprocess/tool
362
+ path preserves only the allowlisted quota code and a bounded integer retry time,
363
+ using a canonical message; arbitrary provider errors stay redacted. Neither
364
+ path retries mutations automatically. Cleanup failures cannot turn an already
365
+ completed tool operation into an apparent failure. Self-hosted instances may
366
+ omit quotas; distributed persistence and tenant budgets belong to the host.
@@ -0,0 +1,29 @@
1
+ import { type CustomToolRunnerInventory, type ResolveCustomTools, type ToolInvocationContext } from "@polpo-ai/core";
2
+ export declare class CustomToolRunnerBrokerError extends Error {
3
+ readonly status: 400 | 401 | 403 | 408 | 409 | 413 | 429 | 503;
4
+ constructor(status: 400 | 401 | 403 | 408 | 409 | 413 | 429 | 503);
5
+ }
6
+ /** Host-owned lease, loaded using the opaque bearer and sandbox identity.
7
+ * No provider secret or tenant executable belongs in this broker. */
8
+ export interface AuthorizedCustomToolRunnerLease {
9
+ readonly agentName: string;
10
+ readonly invocation: ToolInvocationContext;
11
+ readonly inventory: Readonly<CustomToolRunnerInventory>;
12
+ /** Absolute host-stored expiry in epoch milliseconds, never caller input. */
13
+ readonly expiresAt: number;
14
+ /** The host owns isolated execution and cancellation/cleanup. Its resolver
15
+ * must also call assertActive after asynchronous preparation, immediately
16
+ * before handing any credentials to tool code. */
17
+ readonly resolveTools: ResolveCustomTools;
18
+ /** Live expiry, run state, agent incarnation, lease generation and rollout. */
19
+ assertActive(): Promise<void>;
20
+ /** Atomically consumes the ID for the entire lease lifetime, including failed
21
+ * calls. A repeated ID throws 409: mutation delivery is never retried. */
22
+ claimCall(toolCallId: string): Promise<void>;
23
+ }
24
+ /** HTTP-neutral agent-run endpoint for OSS and managed hosts. This does not
25
+ * expose a general integration API; only the host's fixed inventory is callable. */
26
+ export declare function createCustomToolRunnerBroker(options: {
27
+ authorize(token: string, sandboxId: string): Promise<AuthorizedCustomToolRunnerLease>;
28
+ }): (request: Request) => Promise<Response>;
29
+ //# sourceMappingURL=custom-tool-runner-broker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-tool-runner-broker.d.ts","sourceRoot":"","sources":["../src/custom-tool-runner-broker.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,yBAAyB,EAAE,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,EACpF,MAAM,gBAAgB,CAAC;AAGxB,qBAAa,2BAA4B,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;gBAArD,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;CAI3E;AAED;qEACqE;AACrE,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,yBAAyB,CAAC,CAAC;IACxD,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;sDAEkD;IAClD,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAC1C,+EAA+E;IAC/E,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B;8EAC0E;IAC1E,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;oFACoF;AACpF,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,+BAA+B,CAAC,CAAC;CACvF,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA6F1C"}
@@ -0,0 +1,140 @@
1
+ import { createToolInvocationContext, CustomToolRunnerRequestSchema, CustomToolRunnerInventorySchema, CustomToolRunnerResultSchema, MAX_CUSTOM_TOOL_RUNNER_REQUEST_BYTES, MAX_CUSTOM_TOOL_RUNNER_RESPONSE_BYTES, CUSTOM_TOOL_RUNNER_INVENTORY_TIMEOUT_MS, CUSTOM_TOOL_RUNNER_CALL_TIMEOUT_MS, } from "@polpo-ai/core";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ export class CustomToolRunnerBrokerError extends Error {
4
+ status;
5
+ constructor(status) {
6
+ super("Custom tool broker request failed");
7
+ this.status = status;
8
+ this.name = "CustomToolRunnerBrokerError";
9
+ }
10
+ }
11
+ /** HTTP-neutral agent-run endpoint for OSS and managed hosts. This does not
12
+ * expose a general integration API; only the host's fixed inventory is callable. */
13
+ export function createCustomToolRunnerBroker(options) {
14
+ const json = (data, status = 200) => {
15
+ const body = JSON.stringify(data);
16
+ if (new TextEncoder().encode(body).byteLength > MAX_CUSTOM_TOOL_RUNNER_RESPONSE_BYTES)
17
+ throw new CustomToolRunnerBrokerError(413);
18
+ return new Response(body, { status, headers: { "content-type": "application/json", "cache-control": "no-store" } });
19
+ };
20
+ return async (request) => {
21
+ const denied = (status) => json({ ok: false, error: { code: "custom_tool_broker_denied", message: "Custom tool broker request failed" } }, status);
22
+ if (request.method !== "POST")
23
+ return denied(405);
24
+ const controller = new AbortController();
25
+ const startedAt = Date.now();
26
+ const abort = () => controller.abort();
27
+ request.signal.addEventListener("abort", abort, { once: true });
28
+ if (request.signal.aborted)
29
+ abort();
30
+ let timer = setTimeout(abort, CUSTOM_TOOL_RUNNER_INVENTORY_TIMEOUT_MS);
31
+ const bounded = (work) => new Promise((resolve, reject) => {
32
+ if (controller.signal.aborted) {
33
+ reject(new CustomToolRunnerBrokerError(408));
34
+ return;
35
+ }
36
+ const cancelled = () => reject(new CustomToolRunnerBrokerError(408));
37
+ controller.signal.addEventListener("abort", cancelled, { once: true });
38
+ Promise.resolve().then(() => {
39
+ if (controller.signal.aborted)
40
+ throw new CustomToolRunnerBrokerError(408);
41
+ return work();
42
+ }).then(resolve, reject).finally(() => controller.signal.removeEventListener("abort", cancelled));
43
+ });
44
+ try {
45
+ const token = /^Bearer ([A-Za-z0-9_-]+)$/.exec(request.headers.get("authorization") ?? "")?.[1];
46
+ const sandboxId = request.headers.get("x-polpo-sandbox-id");
47
+ if (!token || token.length > 512 || !sandboxId || sandboxId.length > 512)
48
+ throw new CustomToolRunnerBrokerError(401);
49
+ const lease = await bounded(() => options.authorize(token, sandboxId));
50
+ if (!Number.isFinite(lease.expiresAt) || lease.expiresAt <= Date.now())
51
+ throw new CustomToolRunnerBrokerError(401);
52
+ clearTimeout(timer);
53
+ timer = setTimeout(abort, Math.max(0, Math.min(lease.expiresAt - Date.now(), CUSTOM_TOOL_RUNNER_INVENTORY_TIMEOUT_MS - (Date.now() - startedAt))));
54
+ await bounded(() => lease.assertActive());
55
+ if (Number(request.headers.get("content-length")) > MAX_CUSTOM_TOOL_RUNNER_REQUEST_BYTES)
56
+ throw new CustomToolRunnerBrokerError(413);
57
+ if (!request.body)
58
+ throw new CustomToolRunnerBrokerError(400);
59
+ const reader = request.body.getReader();
60
+ const chunks = [];
61
+ let size = 0;
62
+ try {
63
+ while (true) {
64
+ const chunk = await bounded(() => reader.read());
65
+ if (chunk.done)
66
+ break;
67
+ size += chunk.value.byteLength;
68
+ if (size > MAX_CUSTOM_TOOL_RUNNER_REQUEST_BYTES)
69
+ throw new CustomToolRunnerBrokerError(413);
70
+ chunks.push(chunk.value);
71
+ }
72
+ }
73
+ finally {
74
+ void reader.cancel().catch(() => undefined);
75
+ }
76
+ const bytes = new Uint8Array(size);
77
+ let offset = 0;
78
+ for (const chunk of chunks) {
79
+ bytes.set(chunk, offset);
80
+ offset += chunk.byteLength;
81
+ }
82
+ let raw;
83
+ try {
84
+ raw = JSON.parse(new TextDecoder().decode(bytes));
85
+ }
86
+ catch {
87
+ throw new CustomToolRunnerBrokerError(400);
88
+ }
89
+ const parsed = CustomToolRunnerRequestSchema.safeParse(raw);
90
+ if (!parsed.success)
91
+ throw new CustomToolRunnerBrokerError(400);
92
+ // TypeBox schemas may carry internal symbol keys. Only their JSON schema
93
+ // representation is part of the wire inventory or equality contract.
94
+ const inventory = CustomToolRunnerInventorySchema.safeParse(JSON.parse(JSON.stringify(lease.inventory)));
95
+ if (!inventory.success)
96
+ throw new CustomToolRunnerBrokerError(403);
97
+ const invocation = createToolInvocationContext(lease.invocation);
98
+ if (!invocation.agent || invocation.agent.name !== lease.agentName)
99
+ throw new CustomToolRunnerBrokerError(403);
100
+ const operation = parsed.data;
101
+ if (operation.operation === "inventory")
102
+ return json({ ok: true, data: inventory.data });
103
+ const selected = inventory.data.find(tool => tool.name === operation.toolName);
104
+ if (!selected)
105
+ throw new CustomToolRunnerBrokerError(403);
106
+ await bounded(() => lease.claimCall(operation.toolCallId));
107
+ clearTimeout(timer);
108
+ timer = setTimeout(abort, Math.max(0, Math.min(CUSTOM_TOOL_RUNNER_CALL_TIMEOUT_MS, lease.expiresAt - Date.now())));
109
+ const tools = await bounded(() => lease.resolveTools({ agentName: lease.agentName, invocation,
110
+ allowedTools: [selected.name], signal: controller.signal }));
111
+ const candidates = tools.filter(tool => tool.name === selected.name);
112
+ if (candidates.length !== 1)
113
+ throw new CustomToolRunnerBrokerError(403);
114
+ const tool = candidates[0];
115
+ const current = CustomToolRunnerInventorySchema.safeParse(JSON.parse(JSON.stringify([{
116
+ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters,
117
+ ...(tool.requiresSandbox === undefined ? {} : { requiresSandbox: tool.requiresSandbox }),
118
+ }])));
119
+ if (!current.success || !isDeepStrictEqual(current.data[0], selected))
120
+ throw new CustomToolRunnerBrokerError(403);
121
+ await bounded(() => lease.assertActive());
122
+ const result = await bounded(() => tool.execute(operation.toolCallId, operation.arguments, controller.signal));
123
+ await bounded(() => lease.assertActive());
124
+ const valid = CustomToolRunnerResultSchema.safeParse(result);
125
+ if (!valid.success)
126
+ throw new CustomToolRunnerBrokerError(503);
127
+ return json({ ok: true, data: valid.data });
128
+ }
129
+ catch (error) {
130
+ // Do not send host errors, raw transport causes, credentials or schemas.
131
+ return denied(error instanceof CustomToolRunnerBrokerError ? error.status : 503);
132
+ }
133
+ finally {
134
+ clearTimeout(timer);
135
+ request.signal.removeEventListener("abort", abort);
136
+ controller.abort();
137
+ }
138
+ };
139
+ }
140
+ //# sourceMappingURL=custom-tool-runner-broker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-tool-runner-broker.js","sourceRoot":"","sources":["../src/custom-tool-runner-broker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type ConnectorDefinitionStore, type StoredConnectorDefinition } from "@polpo-ai/connect";
2
+ /** In-memory host adapter for tests and ephemeral self-hosted deployments. */
3
+ export declare class MemoryConnectorDefinitionStore implements ConnectorDefinitionStore {
4
+ private readonly definitions;
5
+ listConnectorDefinitions(): Promise<StoredConnectorDefinition[]>;
6
+ getConnectorDefinition(id: string): Promise<StoredConnectorDefinition | null>;
7
+ createConnectorDefinition(record: StoredConnectorDefinition): Promise<void>;
8
+ disableConnectorDefinition(id: string, disabledAt: string): Promise<StoredConnectorDefinition>;
9
+ }
10
+ //# sourceMappingURL=definition-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition-store.d.ts","sourceRoot":"","sources":["../src/definition-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,wBAAwB,EAAE,KAAK,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAEhH,8EAA8E;AAC9E,qBAAa,8BAA+B,YAAW,wBAAwB;IAC7E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgD;IAEtE,wBAAwB,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC;IAGhE,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAG7E,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3E,0BAA0B,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;CAMrG"}
@@ -0,0 +1,25 @@
1
+ import { ConnectError } from "@polpo-ai/connect";
2
+ /** In-memory host adapter for tests and ephemeral self-hosted deployments. */
3
+ export class MemoryConnectorDefinitionStore {
4
+ definitions = new Map();
5
+ async listConnectorDefinitions() {
6
+ return structuredClone([...this.definitions.values()]);
7
+ }
8
+ async getConnectorDefinition(id) {
9
+ return structuredClone(this.definitions.get(id) ?? null);
10
+ }
11
+ async createConnectorDefinition(record) {
12
+ if (this.definitions.has(record.definition.id))
13
+ throw new ConnectError("invalid_provider", "Connector id is already registered");
14
+ this.definitions.set(record.definition.id, structuredClone(record));
15
+ }
16
+ async disableConnectorDefinition(id, disabledAt) {
17
+ const record = this.definitions.get(id);
18
+ if (!record)
19
+ throw new ConnectError("provider_not_found", "Custom Connector not found");
20
+ if (!record.disabledAt)
21
+ record.disabledAt = disabledAt;
22
+ return structuredClone(record);
23
+ }
24
+ }
25
+ //# sourceMappingURL=definition-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition-store.js","sourceRoot":"","sources":["../src/definition-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiE,MAAM,mBAAmB,CAAC;AAEhH,8EAA8E;AAC9E,MAAM,OAAO,8BAA8B;IACxB,WAAW,GAAG,IAAI,GAAG,EAAqC,CAAC;IAE5E,KAAK,CAAC,wBAAwB;QAC5B,OAAO,eAAe,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,KAAK,CAAC,sBAAsB,CAAC,EAAU;QACrC,OAAO,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3D,CAAC;IACD,KAAK,CAAC,yBAAyB,CAAC,MAAiC;QAC/D,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,kBAAkB,EAAE,oCAAoC,CAAC,CAAC;QACjI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,KAAK,CAAC,0BAA0B,CAAC,EAAU,EAAE,UAAkB;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,YAAY,CAAC,oBAAoB,EAAE,4BAA4B,CAAC,CAAC;QACxF,IAAI,CAAC,MAAM,CAAC,UAAU;YAAE,MAAM,CAAC,UAAU,GAAG,UAAU,CAAC;QACvD,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -3,4 +3,12 @@ export * from "./oauth.js";
3
3
  export * from "./secrets.js";
4
4
  export * from "./service.js";
5
5
  export * from "./mcp-oauth.js";
6
+ export * from "./definition-store.js";
7
+ export * from "./mcp-discovery.js";
8
+ export * from "./mcp-execution.js";
9
+ export * from "./mcp-capability-resolver.js";
10
+ export * from "./mcp-runner-broker.js";
11
+ export * from "./custom-tool-runner-broker.js";
12
+ export { observeConnectionSetupSession, cancelConnectionSetupSession } from "./setup-lifecycle.js";
13
+ export { resolveMcpOAuthSetupClient, verifyMcpOAuthSetupClient, type McpOAuthSetupClientReference } from "./mcp-setup-clients.js";
6
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAE/C,OAAO,EAAE,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,KAAK,4BAA4B,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,12 @@ export * from "./oauth.js";
3
3
  export * from "./secrets.js";
4
4
  export * from "./service.js";
5
5
  export * from "./mcp-oauth.js";
6
+ export * from "./definition-store.js";
7
+ export * from "./mcp-discovery.js";
8
+ export * from "./mcp-execution.js";
9
+ export * from "./mcp-capability-resolver.js";
10
+ export * from "./mcp-runner-broker.js";
11
+ export * from "./custom-tool-runner-broker.js";
12
+ export { observeConnectionSetupSession, cancelConnectionSetupSession } from "./setup-lifecycle.js";
13
+ export { resolveMcpOAuthSetupClient, verifyMcpOAuthSetupClient } from "./mcp-setup-clients.js";
6
14
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAE/C,OAAO,EAAE,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAqC,MAAM,wBAAwB,CAAC"}