@zackbart/connecta 0.28.0 → 0.29.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 +961 -5
- package/README.md +62 -39
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1632 -1509
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +526 -0
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +220 -0
- package/dist/connectors/api-connector.js +214 -0
- package/dist/connectors/api.d.ts +8 -71
- package/dist/connectors/api.js +13 -99
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +108 -25
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1780 -351
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +25 -257
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-partition.js +17 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/basecamp/index.d.ts +43 -0
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/breeze/index.d.ts +34 -0
- package/dist/providers/breeze/index.js +1446 -0
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/ccb/index.d.ts +103 -0
- package/dist/providers/ccb/index.js +1725 -0
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/overflow/index.d.ts +60 -0
- package/dist/providers/overflow/index.js +1535 -0
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/planning-center/index.d.ts +59 -0
- package/dist/providers/planning-center/index.js +2199 -0
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/tithely/index.d.ts +49 -0
- package/dist/providers/tithely/index.js +1003 -0
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +136 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +665 -230
- package/documentation/auth.md +962 -458
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +125 -35
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,962 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.29.0 — 2026-10-08
|
|
6
|
+
|
|
7
|
+
Connecta 0.29.0 adopts the MCP 2026-07-28 request envelope, binding client capabilities and client identity to each request. `server/discover` advertises the extensions served and private cache hints. Connecta-owned access, pool and deadline refusals return JSON-RPC errors; admission codes move to `-33001` and `-33002`. Modern clients must preserve the protocol-version and declared argument headers. `tools.listChanged` is false, and `subscriptions/listen` remains unsupported.
|
|
8
|
+
|
|
9
|
+
The tool list is six: `execute_code`, `call_tool`, `call_destructive_tool`, `search_tools`, `authorize_connector` and `skills`. `get_result` is removed; programs page retained direct-call results with `connecta.result(id, options)`. `resume_execution` remains removed, with no paused programs to resume. Guest calls return `{ data, format: "json" | "text" }`, programs report host-call counts, and `connecta.emit` delivers media without requiring `await`. All six tools advertise output schemas.
|
|
10
|
+
|
|
11
|
+
One fail-closed classifier decides `read` or `write`, with exact deployment overrides ahead of provider review and downstream annotations. Per-tool approval exemptions are gone. Root and named pools independently default to `read-only`; `trusted` pools allow program writes and annotate `execute_code` as a write. Approval belongs to the MCP host. Trust widens no connector or identity grant. Dispatched writes are never automatically replayed, and unknown outcomes require reconciliation.
|
|
12
|
+
|
|
13
|
+
Errors distinguish host authentication, downstream OAuth and provider permissions. Routing, argument and recoverable authentication failures carry a repair `nextAction`; other failures retain typed retry or reconciliation guidance. Argument refusals include bounded schema repair details. Discovery returns one flat page with catalog failures before tools, ranks exact tool names and addresses first, and reports endpoint-scoped service absences without substituting lookalikes. Program discovery defaults to JSON Schema; compact schemas are labeled as text.
|
|
14
|
+
|
|
15
|
+
Humans authenticate through Clerk or Cloudflare Access. Access requires trusted direct Worker context; Node uses Clerk. Machines use stored `cta_` tokens, and the generic static bearer adapter is retired. Downstream OAuth starts through an identity-checked, single-use `/connect/<id>` page. Capable hosts receive MRTR URL elicitation; other hosts receive a connection handoff. Public HTTPS deployments default to self-hosted CIMD where the authorization server supports it, with DCR fallback and issuer-bound static clients available. Disconnect removes local authorization and attempts revocation once when the saved server advertises it. State is one grant record per connector and owner, plus one expiring flow record per consent.
|
|
16
|
+
|
|
17
|
+
Direct calls relay downstream `input_required` through connecta's sealed state, bound to the principal, pool, target and original arguments. Only declared form and URL input capabilities are forwarded. Programs receive direct-call repair guidance instead. A consumed continuation cannot be resent; continuation failures are not retryable. The Skills extension serves connecta guides, connector guides and opted-in downstream skills through one caller-scoped registry, shared with `skills` and `connecta.skill()`. Upward resource reads remain skill-only.
|
|
18
|
+
|
|
19
|
+
Downstream clients reuse SDK negotiation verdicts and complete SQL-backed catalog caches. Host-computed identity, credential and pool partitions bound sharing; downstream cache hints cannot widen it. Programs read advertised downstream resources with `connecta.read("resource://<connectorId>/<encodedUri>")`. URI template matching uses bounded forward scans and refuses ambiguous matches, authority changes and unsafe paths before dispatch.
|
|
20
|
+
|
|
21
|
+
Persistent storage is one SQL key-value store with D1 on Workers and SQLite on Node. Workers KV, the JSON file store and optional compare-and-set paths are removed. Result-stash bounds now apply across processes or isolates sharing the store. One configuration schema supplies types, validation and defaults; unknown keys and invalid values fail construction. `describeConfig()` supplies a secret-free snapshot for the operator UI and doctor.
|
|
22
|
+
|
|
23
|
+
Maintained providers use `defineProvider` and folders containing implementation, `SKILL.md`, drift evidence and tests; exports and check inventories are generated. Hosted MCP implementations are reviewed presets. Notion, Vercel and Cloudflare default to hosted MCP with explicit REST complements. The GitHub provider combines hosted tools and a REST complement through a GitHub App, supporting organization and repository scopes together with repository-narrowed installation tokens. It acts as the App; repository creation and forks are absent, and workflow-file writes are off by default. Provider drift checks run locally, on provider pull requests and weekly, reporting findings without filing issues. Google Workspace providers retain domain-wide delegation, including draft-only Gmail and Slides comments.
|
|
24
|
+
|
|
25
|
+
The operator UI is rebuilt on React, Radix and Tailwind with hashed immutable assets on both runtimes. It shows caller-scoped configuration, catalogs, access and activity; capability changes remain in deployment code. Activity records the real package version independently of display `serverInfo`, validated client name and version, call classification and result size, without payloads. Shipped executors carry lifecycle brands; custom sandboxes explicitly accept cleanup responsibility through `customExecutor(executor, { lifecycle: "self-managed" })`.
|
|
26
|
+
|
|
27
|
+
The built-in artifacts feature, package subpath, pages, scheduled refresh and agent tools are removed. A leftover `artifacts:` or `artifactOrigin` option throws `ConfigError`. Existing artifact data is ignored and untouched. Deployments without the built-in feature need no artifact migration; dedicated downstream publishing services remain ordinary connectors.
|
|
28
|
+
|
|
29
|
+
Agent evals now use the 0.29 tools and guest API with correctness grading for destination, evidence and final answers. Sonnet 5.5 and GPT-6-Luna baselines and their measured failures are recorded in `eval/baselines/notes-0.29.md`. Runner limitations are explicit N/A outcomes. These diagnostic trials do not establish a model ranking or native CLI URL-elicitation support.
|
|
30
|
+
|
|
31
|
+
Security fixes harden Clerk audience binding and downstream OAuth completion binding. Credential redaction and payload-free operator records cover shared request boundaries. GitHub grants bind to repository IDs, catalog sharing respects host auth partitions, and resource URI validation is tightened. OAuth token requests refuse redirects, and refresh coordination preserves one-time dispatch across cancellation and grant resets.
|
|
32
|
+
|
|
33
|
+
Contributors use oxfmt through `npm run format`; `npm run format:check` joins the verification gates. Generated files, fixtures, baseline data and released changelog history retain their existing serialization. Deployments need no formatting migration.
|
|
34
|
+
|
|
35
|
+
Before upgrading an existing deployment, back up its state and database, preserve the credential-encryption key and identity namespaces, and prepare the replacement configuration and imports. Delete `discovery.persistCatalog` and `discovery.staleCatalogSeconds`. Replace the old registry freshness policy with `discovery.catalogMinTtlSeconds` and `discovery.catalogMaxTtlSeconds`, defaulting to 0 and 86400; `catalogTtlSeconds` remains the 300-second fallback when a downstream omits TTL hints. Explicit zero hints disable reuse. Delete `execute.approval`, `Connector.approval`, `execute.resumableWrites` and `execute.pausedRunTtlSeconds`. Set root `trust` and each `pools.<name>.trust` deliberately; named pools do not inherit root trust. Remove artifacts configuration, the `@zackbart/connecta/artifacts` import, artifact bindings and refresh jobs. Replace legacy `credentials` with `vault`, move root branding into `operatorUi({ branding })`, and replace boolean or object `accessTokens` configuration with `accessTokens(storage)`. Fix rejected options before starting the new version.
|
|
36
|
+
|
|
37
|
+
Move persistent storage before reopening traffic. On Node 22.13 or later, stop the old server and run `connecta migrate-state <state.json> <connecta.sqlite>` once, then use `sqliteStorage` and `sqliteActivityStore` from `@zackbart/connecta/sqlite`. Replace `CONNECTA_STATE_FILE` and `CONNECTA_ACTIVITY_FILE` with `CONNECTA_DATABASE`; the old JSONL activity log is not imported. Workers use `d1Storage` and `d1ActivityStore` from `@zackbart/connecta/d1`, replacing copied KV/D1 adapters. Existing `connecta_kv` and `tool_call_activity` tables remain readable; the example uses `CONNECTA_DB` for both. `fileStorage` is no longer exported from `/node`, and custom stores must implement `list` and `compareAndSet`. Deployments using `memoryStorage()` can ignore the persistent-store conversion.
|
|
38
|
+
|
|
39
|
+
For Workers KV, use the Worker example's `scripts/copy-kv-to-d1.mjs` or `copyKvToD1` from `/d1` under maintenance. Back up values with expiration metadata and D1, stop background writers, and drain requests, callbacks and refreshes. Wait at least 60 seconds or the longest KV cache TTL after the last write, and require stable listing/hash passes. Copy before switching to D1, resolve invalid entries and conflicts, and verify exact value hashes and absolute expiries. Preserve the existing vault key. Keep maintenance through startup and credential checks, then mark the source live with `--mark-live` or `markKvToD1Live` before reopening traffic. Later copies refuse stale KV state. Keep backups until the upgrade is verified. OAuth layout migration runs automatically on each owner's first grant read; issuer-stamped grants carry over, pending links and pre-issuer-binding grants need Connect again. Rolling back after that migration requires consent again.
|
|
40
|
+
|
|
41
|
+
Replace `@zackbart/connecta/auth/bearer`, `bearerToken` and `assertedPrincipal` with `accessTokens(storage)`. Provision a separate stored token per machine or represented human, update grants to its metadata ID and `access_token` actor, then rotate clients to `Authorization: Bearer cta_…` and remove the static secret and `X-Connecta-Principal`. Keep exact principal namespaces and IDs when preserving personal state; existing unrevoked `cta_` tokens survive the storage copy. Worker machines also need Access service credentials at the edge. Protect `/connect/*` and `/oauth/callback/*` with the intended Worker Access application. Clerk deployments enable `aud_claim_enabled`, reconnect using the advertised endpoint URL as `resource`, and verify refresh. Custom auth adapters replace `final` and `finalRefusals` with synchronous credential recognition; consolidate Access wrappers that depended on refusal fallthrough.
|
|
42
|
+
|
|
43
|
+
Replace upstream `DynamicWorkerExecutor` construction with `workerExecutor({ loader: env.LOADER })` from `@zackbart/connecta/worker`, keeping the optional `@cloudflare/codemode` peer. Wrap custom executors with `customExecutor(..., { lifecycle: "self-managed" })` and implement termination and cleanup. Existing `quickJsExecutor()` configurations need no change. Public provider import names survive the folder move. Existing Notion integration workflows select `surface: "api"` and rename REST tools to `integration_*`; Vercel and Cloudflare users migrate removed operations to live hosted schemas and configure retained REST complements under separate connector IDs. Follow `documentation/provider-migration-0.29.md`. Update client workflows to the flat discovery page and `{ data, format }` guest results, page direct results through `connecta.result`, and remove calls to `get_result` or `resume_execution`.
|
|
44
|
+
|
|
45
|
+
Start the upgraded deployment under maintenance and run `connecta doctor` with its required machine and edge credentials. Doctor checks health, SDK protocol negotiation, the exact current six-tool set and an actual executor program, and reports the negotiated revision and package version. With the UI enabled, `doctor --config` prints the caller-scoped secret-free snapshot instead of running diagnostics. Check existing OAuth and vault credentials, intended identity and pool grants, token revocation and activity, reissue pending connection links, then reopen traffic. UI, provider and contributor changes require no additional migration for deployments that do not use them. These notes apply whenever a deployment chooses to upgrade; the release includes no owner deployment cutover.
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- Add seven Google Slides comment tools: `list_comments`, `create_comment`, `create_comment_reply`, `update_comment_thread`, `update_comment_post`, `delete_comment`, and `delete_comment_reply` (#696). Reads include authors, replies and anchors, with revision/content-bound cursors and raw thread paging. Comment mutations are writes; trusted pools may run them in programs, and the host controls approval. The raw batch hatch accepts comment requests and preserves reply ids ahead of text under its result budget. Oversized direct write results can be paged with program `connecta.result` on trusted endpoints. Read-only endpoints return an inline truncation notice and preview without a write stash. Results distinguish batch acceptance from comment save confirmation; a comment write without `ALL_SAVED` reports `commentUpdateState` and instructs callers to re-read instead of repeating it.
|
|
50
|
+
- **`Connector.classification`: connectors report facts, the registry
|
|
51
|
+
classifies** ([#705](https://github.com/zackbart/connecta/issues/705)). A
|
|
52
|
+
connector may carry a reviewed `ToolClassification` as the readonly
|
|
53
|
+
`classification` field. `remoteMcp({ classify })` and maintained providers set
|
|
54
|
+
it to a deep-frozen copy, and their `listTools` now returns the downstream's
|
|
55
|
+
listing unclassified. The registry validates the field when it first reads a
|
|
56
|
+
connector, rejects it beside `staticTools`, and keeps request-local classified entries built from intake-redacted listing facts.
|
|
57
|
+
The SQL-backed SDK cache stores complete intake-redacted facts without registry
|
|
58
|
+
classification verdicts, and each request classifies them into fresh objects. It also observes catalog drift for such connectors itself, and
|
|
59
|
+
ignores their `catalogDrift()`. A decorator that filters, copies, or annotates a
|
|
60
|
+
listing, or mutates tools it listed or was handed, cannot turn a reviewed write
|
|
61
|
+
into a read. Wrappers keep the review only by forwarding the field:
|
|
62
|
+
`{ ...connector }`, `Object.assign`, and `Object.create` do; a forwarding class
|
|
63
|
+
that omits it serves an unreviewed connector, whose downstream annotations fail
|
|
64
|
+
closed when absent. Exact deployment-level overrides
|
|
65
|
+
([#706](https://github.com/zackbart/connecta/issues/706)) apply ahead of review
|
|
66
|
+
and downstream annotations, regardless of wrapping.
|
|
67
|
+
|
|
68
|
+
Request-scoped catalogs own their definitions and serve deep copies to
|
|
69
|
+
discovery and invocation. Each connector dispatch also receives a fresh deep
|
|
70
|
+
copy of its definition. Mutating a dispatched definition or a discovery
|
|
71
|
+
result cannot change later discovery, schema validation, or write accounting
|
|
72
|
+
within the same program.
|
|
73
|
+
- Add a GitHub provider with mixed org and repository scopes through a GitHub App, owner-routed and repository-narrowed installation tokens, exact hosted-tool allowlisting, scope discovery, confined search, release writes, and workflow-file writes disabled unless explicitly configured. Add request-local Bearer authentication and an opt-in classification allowlist that hides unlisted tools.
|
|
74
|
+
- `copyKvToD1(kv, db, { source, cursor, overwriteFamilies, maxKeys, verify })`
|
|
75
|
+
on `@zackbart/connecta/d1` copies a 0.28 Workers KV deployment's state into D1
|
|
76
|
+
during a maintenance window, preserving OAuth grants, vault credentials,
|
|
77
|
+
`cta_` tokens, and absolute expiries. It skips oversized UTF-8 strings/rows,
|
|
78
|
+
bounds buffered bytes, and reports invalid/conflict/verification counts by
|
|
79
|
+
family only. Resume tokens require the same source id and an atomic claim;
|
|
80
|
+
error labels use class identity. Overwrite requires an explicit family list.
|
|
81
|
+
`markKvToD1Live` seals the source in D1 before traffic reopens, refusing later
|
|
82
|
+
copies unless explicitly overridden. The Worker runbook backs up and drains
|
|
83
|
+
writers, waits for stable KV, copies and verifies before deployment, checks
|
|
84
|
+
existing credentials under maintenance, then reopens traffic (#709).
|
|
85
|
+
- **Google Docs connection.** `@zackbart/connecta/providers/docs` exports
|
|
86
|
+
`docs(id, options)` with the same Workspace options as `gmail()`, plus
|
|
87
|
+
`DOCS_SCOPES` (exactly `documents`) and `DOCS_API_BASE_URL`
|
|
88
|
+
([#681](https://github.com/zackbart/connecta/issues/681)). Six hand-written
|
|
89
|
+
tools: `get_document` (every tab, or one, rendered as markdown-ish text —
|
|
90
|
+
headings, lists, links, tables as pipe rows, footnotes, and every inline chip
|
|
91
|
+
by what it shows, a dropdown's selected value included — capped by
|
|
92
|
+
`maxChars` with an explicit marker, with `title`, `revisionId`, on
|
|
93
|
+
`withIndexes` the UTF-16 start and end index of every paragraph, table, and
|
|
94
|
+
table of contents, paged by cursor in 64 KiB pages so a default read always
|
|
95
|
+
fits the 256 KiB `execute_code` bridge, and a per-tab `notRendered` naming headers, footers,
|
|
96
|
+
floating images, unmarked suggestions, or unknown elements the text leaves
|
|
97
|
+
out; `raw: true` returns Google's resource instead, up to 4 MB),
|
|
98
|
+
`create_document` (title and an optional body; a body that fails after the
|
|
99
|
+
document exists names its id instead of inviting a duplicate), `append_text`
|
|
100
|
+
and `insert_text` (additive), `replace_all_text` (string or RE2, by tab,
|
|
101
|
+
reporting the count), and `batch_update_document`, a raw
|
|
102
|
+
`documents.batchUpdate` passthrough annotated as destructive and classified as a write, takes 1–100
|
|
103
|
+
requests of known generally available kinds (contents unvalidated), returns
|
|
104
|
+
Google's replies within the shared result budget (projected to ids and
|
|
105
|
+
counts, then cut to a counted prefix, still saying the batch applied), and
|
|
106
|
+
requires `requiredRevisionId`. Every other edit takes it optionally, and a
|
|
107
|
+
write naming one is revision-guarded, so a stale revision fails `conflict`
|
|
108
|
+
on Google's own reason code. Failures are classified by how far the request
|
|
109
|
+
got, from the shared client's outcome facts: a 4xx refusal passes through
|
|
110
|
+
as mapped and applied nothing; an edit sent with no answer back, answered
|
|
111
|
+
with a 5xx (whatever reason it carries), or redirected says its outcome is
|
|
112
|
+
unknown, non-retryably, because no Docs write is safe to send twice; only a
|
|
113
|
+
2xx whose reply broke off, overflowed, or would not parse says the edit was
|
|
114
|
+
applied; and a create that may have left a document behind never invites a
|
|
115
|
+
second one. Ids Google sends back are copied only if a tool could take them
|
|
116
|
+
as input again, and otherwise dropped whole and named in `dropped`; titles
|
|
117
|
+
are clamped; and every result passes one final size check, which a read
|
|
118
|
+
answers with its way out and a write with a small acknowledgment that it
|
|
119
|
+
applied. There is no search or
|
|
120
|
+
list tool: finding a document is Drive's job, and `documents` cannot list. A
|
|
121
|
+
404 stays `connector_call_failed`, because Google answers it for an id that
|
|
122
|
+
is unknown and for one not shared with the caller alike. Setup, including
|
|
123
|
+
the Admin console scope, is documented on `docs()`; drift is checked against
|
|
124
|
+
the Docs Discovery document.
|
|
125
|
+
|
|
126
|
+
Trusted pools permit program writes; read-only pools use `call_destructive_tool`.
|
|
127
|
+
The MCP host controls approval.
|
|
128
|
+
- **Draft-only Gmail connection.** `@zackbart/connecta/providers/gmail`
|
|
129
|
+
exports `gmail(id, { purpose, serviceAccount, subject, title?,
|
|
130
|
+
instructions?, callAdmission?, maxResultBytes?, baseUrl? })`, plus
|
|
131
|
+
`GMAIL_SCOPES` and `GMAIL_API_BASE_URL`. Eight hand-written tools:
|
|
132
|
+
`search_threads` (Gmail search syntax, cursor-paged, each thread summarized),
|
|
133
|
+
`get_thread`, `get_message` (the message's own body — never an attached or
|
|
134
|
+
forwarded email's — decoded from its charset, HTML converted only when no
|
|
135
|
+
text part exists, fetched from the attachments endpoint when Gmail stored it
|
|
136
|
+
apart, reported as `bodyFormat: "unavailable"` rather than empty when too
|
|
137
|
+
large to read, capped by `maxBodyChars` with an explicit marker, and
|
|
138
|
+
metadata only for attachments, which include unnamed inline images and any
|
|
139
|
+
other part that is not body text), `list_labels`, `list_drafts`,
|
|
140
|
+
`get_draft`, `create_draft`
|
|
141
|
+
(To/Cc/Bcc, RFC 2047-encoded headers, an optional HTML alternative, and
|
|
142
|
+
`replyToMessageId`, which sets the thread, `In-Reply-To`, `References`, a
|
|
143
|
+
`Re:` subject, and every recipient of the replied message's Reply-To or
|
|
144
|
+
From, parsed as an RFC 5322 address list), and `update_draft` (replaces the
|
|
145
|
+
body; keeps From, To, Cc, Bcc, Reply-To, Subject, the thread, and the reply
|
|
146
|
+
headers unless restated, and no other header; refuses, unchanged, a draft
|
|
147
|
+
with attachments, inline images, or anything else Gmail's whole-message
|
|
148
|
+
update would delete, and one nested deeper than it inspects). Every result
|
|
149
|
+
is built to stay under 192 KiB of JSON, so it reaches a program through
|
|
150
|
+
`execute_code`'s 256 KiB bridge as well as `call_tool` — the whole result,
|
|
151
|
+
wrapper and cursor included, measured as sent rather than estimated:
|
|
152
|
+
`get_thread` and `list_labels` page by cursor when a thread or label set is
|
|
153
|
+
larger, every string field is bounded, an identifier far past any Gmail id
|
|
154
|
+
is dropped rather than cut and the drop is said — `omittedIds` names the
|
|
155
|
+
field, `labelIdsOmitted`, `labelsOmitted`, and `attachmentsOmitted` count
|
|
156
|
+
what a list left out — a saved draft whose id Gmail returned unusably still
|
|
157
|
+
reads `saved: true` with a note pointing to `list_drafts`, a body past
|
|
158
|
+
the limit ends with a marker at any `maxBodyChars`, and an untouched
|
|
159
|
+
`raw: true` message past it is refused with the way forward. Every cursor is
|
|
160
|
+
bound to the tool and arguments that issued it, and a thread or label page
|
|
161
|
+
resumes by the identity of what came before: a message or label added or
|
|
162
|
+
removed before that point answers `conflict` rather than repeating or
|
|
163
|
+
skipping one. There is no send, delete, or label tool and no raw
|
|
164
|
+
hatch; `gmail.compose` technically permits sending, and the tool surface is
|
|
165
|
+
what forbids it. `create_draft` is an additive write and `update_draft` a
|
|
166
|
+
destructive one. Both classify as writes; trusted pools permit them in programs,
|
|
167
|
+
and the host controls approval. The transport is confined beneath `users/me`, so the
|
|
168
|
+
token's subject is the only mailbox a request can reach. Setup — Cloud
|
|
169
|
+
project, API, service account with no IAM roles, JSON key (and the
|
|
170
|
+
`iam.disableServiceAccountKeyCreation` override some organizations need), and
|
|
171
|
+
the Admin console's domain-wide delegation entry with exactly
|
|
172
|
+
`gmail.readonly` and `gmail.compose` — is documented on `gmail()` itself.
|
|
173
|
+
- **Google Drive connection.** `@zackbart/connecta/providers/drive` exports
|
|
174
|
+
`drive(id, options)` with the same Workspace delegation options, plus
|
|
175
|
+
`DRIVE_SCOPES` (exactly `https://www.googleapis.com/auth/drive`) and
|
|
176
|
+
`DRIVE_API_BASE_URL`. Seventeen hand-written tools, every one reaching shared
|
|
177
|
+
drives as well as My Drive. Reads: `search_files` (Drive query syntax, trash
|
|
178
|
+
left out unless asked, `user`/`drive`/`allDrives`/`domain` corpora, Drive's
|
|
179
|
+
own `incompleteSearch` surfaced), `list_folder_items`, `get_file`,
|
|
180
|
+
`get_file_content` (Docs exported as Markdown, Sheets as CSV of the first
|
|
181
|
+
sheet only, Slides as text, text files read by range and cut at `maxChars`
|
|
182
|
+
code points, never inside a character, binaries as base64 when the encoded
|
|
183
|
+
file fits the result and never past 1 MiB, and anything else — a larger
|
|
184
|
+
binary, a drawing, a form, a folder, a shortcut — as `format: "unavailable"`
|
|
185
|
+
with a note rather than an empty success; the caps hold on the bytes the
|
|
186
|
+
download returns, never on the size metadata reported, so a file that grew,
|
|
187
|
+
shrank, or emptied between the two reads is still read right — a download
|
|
188
|
+
is read only one byte past its cap and the rest of the stream cancelled
|
|
189
|
+
unread, even from a server that ignores the range, and a range answered
|
|
190
|
+
`416` with `Content-Range: bytes */0` is a verified empty file — and Drive's
|
|
191
|
+
10 MB export limit is named from Google's own reason code),
|
|
192
|
+
`list_permissions`, and `list_shared_drives`. Every read is built to stay
|
|
193
|
+
under `maxBytes` of JSON, envelope and cursor included: 192 KiB by default,
|
|
194
|
+
which a program inside `execute_code` always receives, from 64 KiB to 4 MiB
|
|
195
|
+
on request, above the default for a direct `call_tool` only. A final guard
|
|
196
|
+
around every tool refuses any result past that bound rather than returning
|
|
197
|
+
it; a write it stops has applied, and says to re-read rather than repeat. A listing that would outgrow it stops early with a
|
|
198
|
+
cursor that resumes at the first row left out, and fails `conflict` rather
|
|
199
|
+
than skip or repeat a row if that Drive page has changed since — the cursor
|
|
200
|
+
carries fingerprints of Drive's raw ids for that page, in order, and of the
|
|
201
|
+
last one returned. A cursor is never issued that could not be accepted back:
|
|
202
|
+
a Drive page token too long to carry fails, saying so.
|
|
203
|
+
Every cursor is bound to its tool and the arguments that decide its rows,
|
|
204
|
+
and refused elsewhere. Content is cut with a marker naming the bound that
|
|
205
|
+
cut it. Names are cut at 2 KiB in listings and content results and 32 KiB in
|
|
206
|
+
`get_file`, descriptions at 64 KiB, other strings Drive does not bound at
|
|
207
|
+
1 KiB, and owners and parents at ten; identifiers are never cut, and one that
|
|
208
|
+
is malformed is left out. Every such field is named in the result's
|
|
209
|
+
`truncatedFields`.
|
|
210
|
+
Additive writes:
|
|
211
|
+
`create_folder` and `restore_file`. Destructive writes: `create_file` (text
|
|
212
|
+
or base64 content as one multipart upload, or an empty file; `convertTo`
|
|
213
|
+
imports it as a Google Doc, Sheet, or Slides file) and `copy_file`, because
|
|
214
|
+
a new file takes its folder's sharing and so discloses its content to
|
|
215
|
+
everyone a shared destination reaches. These classify as writes, as do `update_file_content`,
|
|
216
|
+
`update_file` (rename, description), `move_file` (a move changes inherited
|
|
217
|
+
sharing), `trash_file`, `share_file` (user, group, domain, or anyone with
|
|
218
|
+
the link, up to writer or organizer, emailing no one unless
|
|
219
|
+
`sendNotificationEmail`), `update_permission`, and `delete_permission`.
|
|
220
|
+
A create, copy, or share that may have landed — sent with no answer,
|
|
221
|
+
answered with a redirect it never follows, answered 5xx (a rate-limit reason
|
|
222
|
+
on a 5xx included), or accepted with a reply that broke — is never
|
|
223
|
+
retryable and names the read to check before repeating it, since a repeat
|
|
224
|
+
makes a second one. Only a write Google answered 2xx is ever said to have
|
|
225
|
+
applied. Writes that set fixed values (`update_file_content`, `update_file`,
|
|
226
|
+
`trash_file`, `restore_file`, `update_permission`) are sent as idempotent, so
|
|
227
|
+
a 5xx to them stays a retryable outage.
|
|
228
|
+
There is no permanent delete,
|
|
229
|
+
no empty-trash, no ownership transfer, and no raw hatch. A 404 says the file
|
|
230
|
+
may be missing or hidden from this account, because Drive does not say
|
|
231
|
+
which. Setup — the Google Drive API and the one scope on the delegation
|
|
232
|
+
entry — is documented on `drive()` itself.
|
|
233
|
+
|
|
234
|
+
Trusted pools permit program writes; read-only pools use `call_destructive_tool`.
|
|
235
|
+
The MCP host controls approval.
|
|
236
|
+
- **Google Sheets connection** ([#682](https://github.com/zackbart/connecta/issues/682)).
|
|
237
|
+
`@zackbart/connecta/providers/sheets` exports `sheets(id, options)` with the
|
|
238
|
+
same Workspace options as `gmail()`, plus `SHEETS_SCOPES` and
|
|
239
|
+
`SHEETS_API_BASE_URL`. Nine hand-written tools: `get_spreadsheet` (title,
|
|
240
|
+
sheets with ids and grid sizes, named ranges as A1 with the sheet title
|
|
241
|
+
always quoted, never cell values; `raw: true` for Google's untouched
|
|
242
|
+
metadata) and `get_values` (one to twenty A1 ranges through
|
|
243
|
+
`values:batchGet`, value and date render options, paged by whole rows under
|
|
244
|
+
`maxCells` and `maxBytes` — never more, an oversized row is refused — with
|
|
245
|
+
the default byte budget sized to what `execute_code` can receive and at most
|
|
246
|
+
4 MiB for a direct `call_tool` read, which the result stash can still page,
|
|
247
|
+
and a cursor bound to the call's spreadsheet, ranges, and render options
|
|
248
|
+
that only ever continues the caller's own ranges; cells over 5,000 characters cut with a marker, and
|
|
249
|
+
`maxCellChars` up to 50,000 to read one whole) are read-only;
|
|
250
|
+
`create_spreadsheet`, `add_sheet`, and `append_values` (pinned to
|
|
251
|
+
`INSERT_ROWS`, so nothing below the table is overwritten) are additive
|
|
252
|
+
writes; `update_values`, `batch_update_values`, `clear_values`, and
|
|
253
|
+
`batch_update_spreadsheet` — Google's own `batchUpdate` requests, passed
|
|
254
|
+
through untouched and classified as a write, its replies cut to their kind,
|
|
255
|
+
ids, and counts when too large to deliver — are destructive. Writes take a
|
|
256
|
+
required `RAW` or `USER_ENTERED` and at most 50,000 cells per call. Value
|
|
257
|
+
updates and clears of fixed ranges are idempotent, so a 5xx stays
|
|
258
|
+
retryable; an append or create whose outcome is unknown says what to read
|
|
259
|
+
before repeating it — the table, or a Drive search for the title. A 404
|
|
260
|
+
never claims absence, since a spreadsheet is a Drive file that may simply not
|
|
261
|
+
be shared with the caller. Listing and finding spreadsheets is Drive's job.
|
|
262
|
+
The one scope, `https://www.googleapis.com/auth/spreadsheets`, and its setup
|
|
263
|
+
are documented on `sheets()`.
|
|
264
|
+
|
|
265
|
+
Trusted pools permit program writes; read-only pools use `call_destructive_tool`.
|
|
266
|
+
The MCP host controls approval.
|
|
267
|
+
- **Google Forms connection.** `@zackbart/connecta/providers/forms` exports
|
|
268
|
+
`forms(id, options)`, taking the same Workspace options as `gmail()`, plus
|
|
269
|
+
`FORMS_SCOPES` and `FORMS_API_BASE_URL`. Six hand-written tools: `get_form`
|
|
270
|
+
(title, description, quiz and publish state, `revisionId`, and every item
|
|
271
|
+
with the question ids answers are keyed by — one per grid row — their types,
|
|
272
|
+
options, and required flags; long descriptions and option lists cut with
|
|
273
|
+
explicit markers; `raw: true` for Google's whole Form), `list_responses`
|
|
274
|
+
(cursor-paged, an optional exclusive `submittedAfter` instant, each answer
|
|
275
|
+
labeled with its question's title in form order, with any quiz grade and
|
|
276
|
+
grader feedback, answers and feedback over 2,000 characters cut) and
|
|
277
|
+
`get_response` (one whole, with file ids, quiz grades, and feedback text and
|
|
278
|
+
links; `questionIds` narrows it), `create_form` (title, Drive file name,
|
|
279
|
+
optionally unpublished — all Google accepts at creation), `update_form_info`
|
|
280
|
+
(replaces the title or description), and `batch_update_form` (Google's own
|
|
281
|
+
batchUpdate requests, each exactly one of its six kinds, all or none, under a
|
|
282
|
+
required `requiredRevisionId` so an edit never lands on a form someone
|
|
283
|
+
changed since it was read; a stale revision fails `conflict`). Every result
|
|
284
|
+
stays inside 192 KiB, under `execute_code`'s 256 KiB host-result bridge,
|
|
285
|
+
measured whole, cursor included: a response page ends early and continues
|
|
286
|
+
after the last response it returned, only while Google's page still holds
|
|
287
|
+
the same responses in the same order (otherwise `conflict`), every
|
|
288
|
+
projected text is bounded in bytes as well as characters and cut without
|
|
289
|
+
splitting a surrogate pair, a large form pages its items under a cursor
|
|
290
|
+
bound to its revision — continuing inside a grid too large for one page, so
|
|
291
|
+
every question id is reachable — a response too large to read whole names the answers
|
|
292
|
+
it left out in `omittedQuestionIds`, `raw: true` refuses a form too large
|
|
293
|
+
for one result, and a batch reply too large to return whole drops question
|
|
294
|
+
ids, then everything but counts, marked `truncated` and still reported as
|
|
295
|
+
applied. `create_form` is additive; the other two writes are
|
|
296
|
+
destructive. A write Google may have applied — no answer, a 5xx or redirect after it
|
|
297
|
+
arrived, or a reply that broke — is reported as not retryable (no Forms
|
|
298
|
+
write is sent as idempotent), and a create says to look in Drive before
|
|
299
|
+
creating again. There is no list, delete, share, publish, or watch tool: Drive
|
|
300
|
+
lists forms, and this connection requests no Drive scope. A form is a Drive
|
|
301
|
+
file, so a 404 is reported as unknown-or-not-visible, never as absence.
|
|
302
|
+
Setup, with exactly `forms.body` and `forms.responses.readonly` for the
|
|
303
|
+
domain-wide delegation entry, is documented on `forms()` itself.
|
|
304
|
+
- **Google Workspace domain-wide delegation, shared by every Workspace
|
|
305
|
+
provider.** `serviceAccount` is `{ clientEmail, privateKey, clientId? }` or
|
|
306
|
+
the downloaded JSON key's text, from deployment secrets rather than the vault,
|
|
307
|
+
because one key serves every Workspace connector; anything but a complete,
|
|
308
|
+
exactly-encoded PKCS#8 RSA key of at least 2048 bits throws at construction,
|
|
309
|
+
and no key ever appears in
|
|
310
|
+
a message. `subject` is a function from the authenticated
|
|
311
|
+
`AuthenticatedIdentity` to a Workspace address — sync or async, for a
|
|
312
|
+
directory lookup, with the call's abort signal — or one fixed address for
|
|
313
|
+
shared or scheduled use. The function is never called for an open
|
|
314
|
+
deployment's anonymous requests or a call no request admitted; those, and an
|
|
315
|
+
`undefined` answer, fail `auth_required` before any request leaves.
|
|
316
|
+
Arguments, headers, and programs cannot choose the subject. The RS256
|
|
317
|
+
JWT-bearer assertion is signed with Web Crypto, so it runs unchanged on Node
|
|
318
|
+
and Workers with no new dependency. Access tokens are cached in memory only —
|
|
319
|
+
never in storage — per service account, key, subject, and scope set,
|
|
320
|
+
replaced a minute before expiry, and bounded at 512. Concurrent callers share
|
|
321
|
+
one mint through plain outcomes and a deadline, never another request's
|
|
322
|
+
signal, so a cancelled owner sends a waiting caller in another Worker request
|
|
323
|
+
to mint for itself. A 401 forgets only the token it rejected and replays the
|
|
324
|
+
request once, so a streamed request body is refused up front. A write that
|
|
325
|
+
names the revision it was made against passes `{ revisionGuarded: true }`,
|
|
326
|
+
and a stale revision — FAILED_PRECONDITION or ABORTED on HTTP 400 or 409 —
|
|
327
|
+
then arrives as `conflict` with fixed words to re-read and retry, unless
|
|
328
|
+
Google names a more specific reason — a disabled API, a quota, a missing
|
|
329
|
+
scope, or a precise permission refusal — which always decides first; otherwise
|
|
330
|
+
a refused precondition is reported neutrally in Google's own words. A
|
|
331
|
+
product can read Google's reason codes for any mapped failure — sanitized
|
|
332
|
+
tokens only, never its prose — and `exportSizeLimitExceeded`,
|
|
333
|
+
`domainPolicy`, and `insufficientFilePermissions`/`forbidden` refusals
|
|
334
|
+
name themselves precisely. A download or export can pass `{ maxBytes }` to
|
|
335
|
+
read only a prefix: the body is streamed, the rest is cancelled unread, and
|
|
336
|
+
the result says whether it was `truncated`, with its HTTP status and
|
|
337
|
+
`Content-Range`, so an empty file's 416 arrives as an empty result. A write
|
|
338
|
+
is never told to retry when it may have landed: one Google accepted whose
|
|
339
|
+
reply broke off or overflowed, one answered with a redirect, one sent with
|
|
340
|
+
no answer at all, and one Google answered with any 5xx — whatever reason it
|
|
341
|
+
named — fail as non-retryable with words saying to re-read its target first,
|
|
342
|
+
the verdict core's `write_outcome_unknown` gives a trusted-pool program write.
|
|
343
|
+
Only a 429, or a 4xx naming a quota, is a rate limit for a write. A read
|
|
344
|
+
stays retryable, as does a write the provider marks `{ idempotent: true }`.
|
|
345
|
+
An error status is a refusal even when its body cannot be read. A product
|
|
346
|
+
can ask how far any failed request got: before sending, awaiting a
|
|
347
|
+
response, reading an accepted reply, redirected, a server error on a write,
|
|
348
|
+
or refused. Google's
|
|
349
|
+
refusals map to what fixes them: `unauthorized_client`
|
|
350
|
+
names the client ID and the exact scopes to authorize, `invalid_grant` names an
|
|
351
|
+
unknown or suspended user, a deleted key, or clock skew, and a disabled API or
|
|
352
|
+
a missing scope says so.
|
|
353
|
+
- **Google Slides connection.** `@zackbart/connecta/providers/slides` exports
|
|
354
|
+
`slides(id, options)` with the same Workspace delegation options as `gmail()`,
|
|
355
|
+
plus `SLIDES_SCOPES` and `SLIDES_API_BASE_URL`. It requests exactly
|
|
356
|
+
`https://www.googleapis.com/auth/presentations`. Fifteen hand-written
|
|
357
|
+
tools, seven of them for comments (below). Of the other eight, four are
|
|
358
|
+
reads: `get_presentation` (title, page size, `revisionId`, a
|
|
359
|
+
layout preview, and each slide's text in reading order — top to bottom,
|
|
360
|
+
then left to right, rotated and nested groups composed — with table cells,
|
|
361
|
+
alt text, linked charts' spreadsheet ids, speaker notes, and empty
|
|
362
|
+
placeholders' ids for filling a new slide, under a field mask that leaves
|
|
363
|
+
styles behind, capped per slide), `get_page` (any slide, layout, master,
|
|
364
|
+
notes page, or the notes master by objectId: every element in reading order with its group and
|
|
365
|
+
placeholder type, index, and parent — what `placeholderIdMappings` needs —
|
|
366
|
+
and text continued across pages), `list_layouts` (masters, each followed by
|
|
367
|
+
its layouts), and `get_slide_thumbnail` (the short-lived link and size,
|
|
368
|
+
never the image). `create_presentation` and `create_slide` are additive
|
|
369
|
+
writes; `replace_all_text` (literal, case-sensitive by default, several
|
|
370
|
+
replacements in one atomic batch, optionally at a required revision) and
|
|
371
|
+
`batch_update_presentation` (1 to 100 raw Slides requests, each refused
|
|
372
|
+
locally unless it is one known Request kind, always at a required
|
|
373
|
+
`revisionId`, its replies bounded — every id at every depth kept whole,
|
|
374
|
+
large fields named in `cut`, overflow counted, and the write reported as
|
|
375
|
+
applied) are annotated as destructive and classified as writes. Every write result is size-checked too: copied
|
|
376
|
+
ids and revisions are whole or flagged, never cut, and a result that still
|
|
377
|
+
cannot be delivered after Google's 2xx is refused with "applied — do not
|
|
378
|
+
repeat it; re-read". A write refused because the deck changed since its
|
|
379
|
+
revision is a `conflict`, and a create whose outcome is unknown says what to
|
|
380
|
+
look for before creating again.
|
|
381
|
+
Every read result is built under the shared Workspace result budget, so it
|
|
382
|
+
is deliverable inside a program and directly alike, and nothing it cannot
|
|
383
|
+
carry is lost: cut text carries the `get_page` cursor that continues it
|
|
384
|
+
character by character, a crowded slide names the cursor for the elements
|
|
385
|
+
not shown, the layout preview continues in `list_layouts`, and `raw: true`
|
|
386
|
+
pages Slides' own JSON, sending an element too large for one result in
|
|
387
|
+
chunks that concatenate and parse. Every cursor is bound to the deck, page,
|
|
388
|
+
mode, and the revision it was read at — or, for a viewer Slides gives no
|
|
389
|
+
revision, a SHA-256 of the exact content its paging depends on — so a deck that
|
|
390
|
+
changed between pages is a `conflict` to restart, never a skipped or
|
|
391
|
+
repeated slide. Slides cannot list decks, and the guide says that is
|
|
392
|
+
Drive's job. A 404 is reported as unknown-or-not-visible, because a deck is
|
|
393
|
+
a Drive file. Setup is documented on `slides()`.
|
|
394
|
+
|
|
395
|
+
Trusted pools permit program writes; read-only pools use `call_destructive_tool`.
|
|
396
|
+
The MCP host controls approval.
|
|
397
|
+
- **Google Discovery drift checks.** `npm run providers:check -- --provider
|
|
398
|
+
gmail` reads Gmail's credential-free Discovery document and digests the nine
|
|
399
|
+
methods the tools call like any other touched endpoint, and also reports a
|
|
400
|
+
touched method that stops accepting the provider's delegated scopes. A
|
|
401
|
+
manifest with `"format": "google-discovery"` and a `scopes` list is all a
|
|
402
|
+
further Workspace product needs.
|
|
403
|
+
- `Connecta.describeConfig()` returns a secret-free snapshot of the running
|
|
404
|
+
configuration — limits with their `default`/`config` source, auth providers,
|
|
405
|
+
identity rules, pools, modules (with the activity store's kind and
|
|
406
|
+
retention), the storage adapter's kind, branding, and each
|
|
407
|
+
connector's source, endpoint, auth mode, credential slot labels, call
|
|
408
|
+
admission, and static tools — for the operator UI and `connecta doctor`.
|
|
409
|
+
`Connector.describe()` reports a connector's own facts and is implemented by
|
|
410
|
+
`remoteMcp()`, `api()`, and every provider. Every URL
|
|
411
|
+
either reports keeps origin and path only (a root-relative favicon, its path),
|
|
412
|
+
and a URL of any scheme but http(s) is omitted.
|
|
413
|
+
`defineConfig((env) => …)` declares a deployment's configuration as a
|
|
414
|
+
function of its environment (#705).
|
|
415
|
+
- Evaluate hosted Google Workspace and Planning Center capabilities while retaining delegated/PAT API providers, and report public provider contract drift on provider pull requests and weekly without blocking the check gate.
|
|
416
|
+
- `@zackbart/connecta/d1` exports `d1Storage(db)` and
|
|
417
|
+
`d1ActivityStore(db, { retentionDays })`; `@zackbart/connecta/sqlite`
|
|
418
|
+
(Node-only, on the built-in `node:sqlite`) exports `openSqlite(path)`,
|
|
419
|
+
`sqliteStorage(db | path)`, `sqliteActivityStore(db | path, { retentionDays })`,
|
|
420
|
+
and `importStateFile(db, statePath)`. Both drivers share one SQL store; each
|
|
421
|
+
creates its tables on first use and reads a 0.28 `connecta_kv` or
|
|
422
|
+
`tool_call_activity` table as is, adding activity columns it lacks. Activity
|
|
423
|
+
writes prune a bounded batch past the retention window, 90 days by default, so
|
|
424
|
+
no cron is needed. `connecta migrate-state <state.json> <connecta.sqlite>`
|
|
425
|
+
copies a 0.28 `fileStorage` state file into SQLite once, keeping any key the
|
|
426
|
+
database already holds. Every storage key is built in `src/storage/keys.ts`,
|
|
427
|
+
which lists each key family's scope, version, codec, and TTL (#705).
|
|
428
|
+
- Relay downstream form and URL input requests on direct MCP calls using encrypted, signed, single-use state bound to the principal, pool, target, and original arguments. Gate elicitation by the host's declared capabilities, bound rounds and payloads, and keep program input requests unsupported with direct-call guidance.
|
|
429
|
+
|
|
430
|
+
Refuse private-state echoes before publishing prompts, results, or catalogs, and prevent OAuth refresh or redirects from resending write continuations.
|
|
431
|
+
- Ask MCP 2026-07-28 hosts with URL elicitation support to connect a service when a call needs authentication. Authenticate retries with a deployment-held key and bind them to the principal, endpoint, connector, tool, arguments or code, round, and expiry. Decline and cancel stop without dispatch. Writes elicit only for Connecta-owned credential resolution failures before connector invocation. Programs also stop recovery after any write-classified handler was entered; each retry checks its own round. Raw fetches and custom transports cannot establish retry eligibility. These failures require reconciliation before retrying and retain manual connection guidance. Reads may re-run after dispatch only when classified from a catalog accepted within its TTL. The host records every entered call's classification and freshness; stale-fallback entries block program recovery. Accept rechecks every entered read before restarting any call and refuses changed classifications or digests with `auth_replay_refused` and reconciliation guidance. Normal stale-fallback calls retain their existing behavior. Other hosts receive the existing recovery envelope with a connection link.
|
|
432
|
+
- Programs can read downstream MCP resources with `connecta.read("resource://<connectorId>/<encodeURIComponent(downstreamUri)>")`. Reads require a whole-connector grant, share host-call and connector admission limits, and redact credential echoes before reaching the guest or agent.
|
|
433
|
+
- Serve usage, connector guides and opted-in downstream skills through one caller-scoped registry. Advertise the Skills extension and skill-only resources, with private cache hints, five-entry skill pages and preserved downstream digests. Both the skills meta-tool and connecta.skill read the same documents. Connector guide bodies gain Agent Skills frontmatter; connector:<id> remains a one-release lookup alias. Sent credential echoes pass through the existing agent redaction boundary, including binary supporting files.
|
|
434
|
+
|
|
435
|
+
Downstream Skills listing and file reads bypass the SDK response cache while sharing request-scoped auth with ordinary resource reads. Negotiation reuse includes the Skills opt-in.
|
|
436
|
+
- Record the build-time package version independently of display serverInfo on every activity event. Persist package and validated client facts in SQL history, migrate old tables with a nullable column, and expose them read-only at /ui/api/activity and in Activity row details. Older calls without request IDs remain separate rows. Doctor uses and reports the same generated package version.
|
|
437
|
+
- Add the typed operator UI contract and read-only `/ui/api/config` endpoint with a caller-scoped config snapshot, classified live tools, catalog age, payload-free last-call facts, and effective grants, pools, trust tiers, and permissions. `connecta doctor --config` prints the same secret-free snapshot as JSON.
|
|
438
|
+
- Add operator pages for deployment health, connectors, tool catalogs, access, request activity, and configuration on the typed, viewer-scoped contract. Preserve factory option presence so omitted token and transport options are marked as defaults.
|
|
439
|
+
|
|
440
|
+
### Changed
|
|
441
|
+
|
|
442
|
+
- **Breaking:** **`defineProvider()` and `remoteMcp({ classify })`**
|
|
443
|
+
([#705](https://github.com/zackbart/connecta/issues/705)). The root entry
|
|
444
|
+
exports `defineProvider()`, the one shape for a maintained provider: name,
|
|
445
|
+
title, kind, maintained skill, optional reviewed classification, and a
|
|
446
|
+
synchronous `create`. It validates the options every provider shares, renders
|
|
447
|
+
the usage guide with deployment instructions appended, and exposes its
|
|
448
|
+
`definition` on the factory. It imports neither transport, so `api()`
|
|
449
|
+
providers built with it gain no MCP or Effect graph. `remoteMcp()` accepts
|
|
450
|
+
`classify: { tools: { name: "read" | "write" | "destructive" | { verdict,
|
|
451
|
+
reason?, schemaDigest? } } }`, validated at construction. It fails closed,
|
|
452
|
+
and catalog drift is reported against it. Linear is the first provider
|
|
453
|
+
converted. Its tool names, verdicts, titles, descriptions, credential slot,
|
|
454
|
+
guides, and drift counts match 0.28 (`test/linear-snapshot.test.ts`). The one
|
|
455
|
+
change is stricter: a reviewed Linear create such as `create_issue_label`
|
|
456
|
+
now stays a write even when the downstream annotates it `readOnlyHint: true`.
|
|
457
|
+
`linear()` reports a blank purpose as `linear("<id>") requires a non-empty
|
|
458
|
+
purpose`, and rejects a blank `title`, a non-string `instructions`, and an
|
|
459
|
+
unknown `authScope` at construction. `LINEAR_VETTED_CATALOG` is deprecated in
|
|
460
|
+
favor of `linear.definition.classify`.
|
|
461
|
+
- **Reviewed hosted writes stay writes**
|
|
462
|
+
([#705](https://github.com/zackbart/connecta/issues/705)). The Basecamp,
|
|
463
|
+
Cloudflare, Mixpanel, Notion, RevenueCat, Stripe, and Vercel providers now
|
|
464
|
+
apply the same rule as Linear: a tool their release reviewed as a write stays
|
|
465
|
+
a write when the downstream annotates it `readOnlyHint: true`. Before, such a
|
|
466
|
+
create was served as a read and ran unapproved. Their reads, destructive
|
|
467
|
+
tools, unlisted tools, and drift counts are unchanged.
|
|
468
|
+
- Contributors: CI runs Chromium only for relevant PR paths and always on `main` and publish, with a versioned browser cache and bounded installation retries; local `check` still runs the full suite (#705).
|
|
469
|
+
- Validate Clerk OAuth audiences on `/mcp` and pool endpoints for both JWT
|
|
470
|
+
and opaque tokens. Enable Clerk's `aud_claim_enabled` setting and request the
|
|
471
|
+
endpoint's canonical URL as `resource`. Omitted or empty `allowedOAuthClientIds`
|
|
472
|
+
requires bound tokens; an explicit list admits unbound tokens from dedicated
|
|
473
|
+
clients only. Clerk browser session tokens authenticate operator routes only.
|
|
474
|
+
Rejections log fixed reason codes, and the MCP `401` metadata challenge is
|
|
475
|
+
unchanged.
|
|
476
|
+
- Reject direct upstream `DynamicWorkerExecutor` construction, including
|
|
477
|
+
subclasses, before creating runtime resources. The error names the required
|
|
478
|
+
`/worker` import and configuration migration; custom executors remain
|
|
479
|
+
supported (#704).
|
|
480
|
+
- Require a non-enumerable, versioned lifecycle brand on executors before
|
|
481
|
+
creating runtime resources. `/worker` and `/quickjs` carry the brand across
|
|
482
|
+
package copies and bundles; unbranded executors fail with all migration
|
|
483
|
+
options. Custom sandboxes opt in with
|
|
484
|
+
`customExecutor(myExecutor, { lifecycle: "self-managed" })` (#704).
|
|
485
|
+
- Replace the contributor ethos with principles, decision history, and test-backed
|
|
486
|
+
invariant and MCP coverage records. Evidence comes from passing cases in the
|
|
487
|
+
full Node run, so fixtures, non-registering calls, and runtime skips cannot
|
|
488
|
+
certify coverage. Deferred MCP features remain declined for 0.29.0 (#705).
|
|
489
|
+
- Upgrade `@modelcontextprotocol/client` and `@modelcontextprotocol/server` to 2.3.1. The SDK binds stored credentials to their issuing authorization server and propagates token-save failures. Resource transport redirects remain confined to the same origin; credential-bearing OAuth token requests refuse every redirect. Connecta overrides the server SDK's 4 MiB request-body default on both protocol legs, preserving `listen()`'s `maxBodyBytes` and the Workers platform bound. The SDK owns the closed-transport legacy-handshake race, removing connecta's workaround for typescript-sdk#2864. Teardown regressions cover workerd rejection handling.
|
|
490
|
+
- **A 2026-07-28 request must carry `MCP-Protocol-Version`.** The 2026-07-28
|
|
491
|
+
transport requires the header on every POST and has the server reject a
|
|
492
|
+
request without it, so an intermediary routing on the header and the server
|
|
493
|
+
executing the body cannot disagree; server 2.0.0 served such a request
|
|
494
|
+
anyway. A client breaks here only if it puts the
|
|
495
|
+
`io.modelcontextprotocol/protocolVersion` claim in the request's `_meta`,
|
|
496
|
+
sends `Mcp-Method`, and omits `MCP-Protocol-Version` — a hand-rolled modern
|
|
497
|
+
client, or a proxy that strips the header. It must send the header with the
|
|
498
|
+
same value as the claim. Defaulting the header for it is not open to
|
|
499
|
+
connecta: the spec's allowance to assume `2025-03-26` covers only clients
|
|
500
|
+
older than 2025-06-18, which send no 2026-07-28 claim and are still served
|
|
501
|
+
on the legacy leg.
|
|
502
|
+
- **A JSON-RPC batch holds at most 100 messages.** The cap is the server
|
|
503
|
+
SDK's, not the protocol's: batching left the protocol in 2025-06-18, which
|
|
504
|
+
sets no size for the 2025-03-26 clients that may still send one. Server 2.1.0
|
|
505
|
+
answers a longer batch `400` with `-32600` and dispatches none of it; the cap
|
|
506
|
+
is a constant with no option, and connecta keeps it rather than reimplement
|
|
507
|
+
the SDK's body handling. A client that batches more must split the batch.
|
|
508
|
+
- **Breaking:** `api()`, `remoteMcp()`, every maintained provider, `operatorUi()`,
|
|
509
|
+
`accessTokens()`, `activityHistory()`, `d1ActivityStore()`, and
|
|
510
|
+
`sqliteActivityStore()` refuse an unknown
|
|
511
|
+
option at construction, naming its path — `api("crm").maxResultByte`,
|
|
512
|
+
`operatorUi().branding.theme.accentColor` — instead of accepting and ignoring
|
|
513
|
+
it. A discriminated option with a missing or unrecognized discriminant —
|
|
514
|
+
`remoteMcp()`'s `auth.type`, a provider's `auth.type` or `surface` — throws
|
|
515
|
+
with the valid values instead of constructing with no authentication.
|
|
516
|
+
Configuration and factory options are read once, by property descriptor, into
|
|
517
|
+
the plain copy core resolves: a getter or setter on a configuration object or
|
|
518
|
+
array refuses to construct and is never run, and no Proxy trap is invoked
|
|
519
|
+
beyond inspection. An array or class instance where an options object belongs
|
|
520
|
+
(`remoteMcp([...])`, `callAdmission: []`) throws with its path, and every
|
|
521
|
+
string map (static `headers`, `authorizationParams`, `tokenRequestHeaders`)
|
|
522
|
+
must hold string values. An `api()` tool is checked in place and passed
|
|
523
|
+
through as given, so a class-instance tool keeps its prototype handler and
|
|
524
|
+
private fields; a tool without a handler function refuses to construct. Pool names and `classification` map keys such as `__proto__` and
|
|
525
|
+
`constructor` are ordinary entries (#705).
|
|
526
|
+
|
|
527
|
+
Provider definitions declare a closed `options` shape with `optionsOf<T>()`.
|
|
528
|
+
`defineProvider()` and the remaining providers use one descriptor-validation
|
|
529
|
+
path and stamp the maintained name onto `describe().source.provider`. The root
|
|
530
|
+
exports the option-shape combinators and `PROVIDER_COMMON` for provider authors.
|
|
531
|
+
`remoteMcp()` accepts `classify`; its review is still validated by the shared
|
|
532
|
+
classification validator and applied only by the registry. Custom connectors
|
|
533
|
+
remain opaque, preserving `Connector.classification` (#705).
|
|
534
|
+
- Depend on `effect` through `^4.0.0` instead of exactly `4.0.0`, so a
|
|
535
|
+
deployment that also uses Effect resolves one copy. The root still imports
|
|
536
|
+
only the stable `effect` module (#705).
|
|
537
|
+
- **Breaking:** Validate `ConnectaConfig` against one schema that also produces its TypeScript
|
|
538
|
+
type and every default, under one policy: a wrong value refuses to construct,
|
|
539
|
+
naming its path. `execute.*` limits, `discovery.*`, `calls.maxResultBytes`, and
|
|
540
|
+
a connector's own `maxResultBytes` used to warn and fall back to a default;
|
|
541
|
+
they now throw. `connectors`, `auth`, `storage`, `logger`, `publicUrl`,
|
|
542
|
+
`deploymentInfo`, and `serverInfo` are checked by shape too (`storage` must
|
|
543
|
+
implement all five `KVStorage` methods, the same refusal #722 added, now
|
|
544
|
+
reported by the schema);
|
|
545
|
+
`serverInfo.websiteUrl` and a `remoteMcp()` `url` must be http(s). An explicitly
|
|
546
|
+
`undefined` optional value counts as omitted at any depth, and a misspelled
|
|
547
|
+
pool option is reported as `ConnectaConfig.pools.<name>.<key>` with the other
|
|
548
|
+
unknown options (#705).
|
|
549
|
+
- **Breaking:** Both deployment shapes keep their configuration in `src/connecta.config.ts`
|
|
550
|
+
and start it from an entry under 30 lines; optional modules are type-checked
|
|
551
|
+
code switched on by the environment instead of commented blocks. The Node
|
|
552
|
+
template turns Clerk on with both `CLERK_PUBLISHABLE_KEY` and
|
|
553
|
+
`CLERK_SECRET_KEY` (one alone refuses to start), the vault with
|
|
554
|
+
`CONNECTA_CREDENTIAL_KEY`, and activity with `CONNECTA_ACTIVITY=on`, all in its one `CONNECTA_DATABASE` SQLite file;
|
|
555
|
+
it always serves `cta_` access tokens and depends on `@clerk/backend`. The
|
|
556
|
+
Worker example's vault follows the `CREDENTIAL_ENCRYPTION_KEY` secret, and
|
|
557
|
+
activity follows the `CONNECTA_ACTIVITY`
|
|
558
|
+
var (`"on"`), in `CONNECTA_DB`. Activity in both shapes keeps 90 days
|
|
559
|
+
(`retentionDays`), pruned on write (#705).
|
|
560
|
+
- Contributors add changelog fragments in `.changes/`; release authors assemble
|
|
561
|
+
them with a hand-written narrative using `npm run changelog:assemble`.
|
|
562
|
+
Vitest runs every suite on Node and excludes `*.node.test.ts` from Workers,
|
|
563
|
+
with each Node-only reason recorded in the file instead of shared suite lists (#705).
|
|
564
|
+
- Convert the eight hosted MCP implementations to reviewed `defineProvider()` presets. Preserve authentication, endpoint choices, tool classifications, schema digests, results, and API defaults. Add usage prerequisites and pool-trust guidance. Hosted factories now reject blank titles and malformed common options at construction. Remove the internal `withVettedCatalog` helper; public `*_VETTED_CATALOG` exports remain deprecated aliases of each provider definition's classification.
|
|
565
|
+
- Contributors get `npm run check:fast`, a partial inner loop that runs the
|
|
566
|
+
static checks concurrently with Vitest suites related to the branch's changes
|
|
567
|
+
and the always-on package guards; `npm run check` remains the gate. Pull
|
|
568
|
+
requests audit dependencies when root or nested npm manifests, lockfiles,
|
|
569
|
+
shrinkwraps, or configuration change; a nightly workflow and publishing still run `npm audit`. The Chromium
|
|
570
|
+
install retry waits for a leftover apt process instead of failing on its lock,
|
|
571
|
+
and repository skills for providers, spec features, releases, and CI triage
|
|
572
|
+
live in `.claude/skills/` (#705).
|
|
573
|
+
- Give all maintained providers their own folders and generate provider exports, build registrations, package smoke fixtures, conventions, and the README inventory. Preserve provider metadata, guides, registry classifications, public import names, and reviewed bundle caps. Standardize construction errors and report public-contract parser failures per provider, including an explicit manual review record for Breeze.
|
|
574
|
+
- **Breaking:** Make hosted MCP the default for Notion, Vercel and Cloudflare, and remove duplicate named REST operations. `surface: "api"` now selects a documented REST complement. Configure complements under distinct connector ids with their own credentials; hosted OAuth never falls back to an integration token or replays a write. Notion retains every internal-integration capability under explicit `integration_*` names because hosted user OAuth is not equivalent, including headless content writes, full REST pagination and exact JSON blocks. Vercel retains value-safe environment tools and verified deletion/domain gaps; deployment promotion moves to hosted `request_promote`. Cloudflare retains read-only-program reads, byte/header-compatible uploads and legacy Global API Key and R2 jurisdiction mutations; `execute` remains a write. Existing Notion bot workflows must select `surface: "api"` and migrate names to `integration_*`; Cloudflare writes move to hosted `execute` while REST reads can keep their explicit API connector. See `documentation/provider-migration-0.29.md`.
|
|
575
|
+
- **Breaking:** The result stash's byte and entry bounds are booked in one ledger record in
|
|
576
|
+
storage by compare-and-set, so they bound the deployment rather than each
|
|
577
|
+
isolate or process. Every chunk expires by its charge's deadline, concurrent
|
|
578
|
+
stashes retry a lost swap instead of refusing, and a stash write that fails
|
|
579
|
+
releases its charge once it has deleted what it may have written. `engines.node` is `>=22.13.0`, the first
|
|
580
|
+
Node 22 with `node:sqlite` unflagged (#705).
|
|
581
|
+
- **Breaking:** Classify tools once per request-scoped catalog entry, with exact deployment overrides ahead of provider review and downstream annotations. Add read-only and trusted pool tiers, defaulting to read-only, and annotate execute_code as a write on trusted endpoints. Remove connector and execute approval exemptions while preserving write budgets and unknown-outcome accounting. Deployments must explicitly enable trusted pools for program writes.
|
|
582
|
+
- **Breaking:** Discovery returns one flat `{ catalogErrors, tools, total, offset, limit, hasMore }` page from both `search_tools` and `connecta.search`, replacing top-level connector groups. Exact tool names and canonical addresses rank before connector identities, then partial matches. Exact connector ID or title queries browse that connector's tools even when individual descriptions mention its identity. Unknown connector scopes and recognized absent services return an explicit endpoint-scoped absence instead of lookalikes. Catalog credential and permission failures appear before tools with recovery instructions and fixed messages derived only from error codes and connector IDs. Program search and describe default to JSON Schema values; compact schemas and TypeScript signatures are labeled `schemaFormat: "text"`.
|
|
583
|
+
- **Breaking:** Make call errors actionable with agent-only schema keys, enum values, bounds, received types, validated examples and known conditional requirements. List only the current endpoint's configured connectors on address failures. Distinguish host authentication (`host_auth_required`), downstream OAuth (`downstream_oauth_required`) and provider permission (`provider_permission_denied`) recovery. A dispatched write timeout returns `write_outcome_unknown`, never invites an automatic retry, and echoes bounded arguments only to its caller.
|
|
584
|
+
|
|
585
|
+
Decide retryability from HTTP status, registered OAuth codes, typed SDK errors and runtime network facts, never prose (#700). Untyped errors whose text mentions timeout, 429, 502, 503, 504, rate limits, temporary failures or cross-request cancellation are now generic non-retryable failures; typed and structured transport failures retain their verdicts. A 400 registration refusal saying "temporarily unavailable" is non-retryable; 429/502/503/504 refusals remain retryable regardless of body or URL wording. An SDK `RequestTimeout` is a timeout regardless of message wording. An OAuth token endpoint HTTP 403 is now `provider_permission_denied`, keeps the stored grant, and does not start consent. Drop downstream `ProtocolError.data` on errors rethrown by connecta. Operator records remain payload-free (INV-6).
|
|
586
|
+
|
|
587
|
+
Preserve complete error envelopes in MCP structured content and agent-readable text in every direct-call result mode. Maintained API providers distinguish permission refusals from invalid credentials, including Workspace delegation and insufficient scopes, with provider-specific recovery guidance.
|
|
588
|
+
- **Breaking:** Complete the JavaScript guest API with `connecta.result(id, options)` and `connecta.skill(name)`, support object-form calls, and remove the `get_result` meta-tool. Guest calls now return `{ data, format: "json" | "text" }`; direct results also declare their format. Every program reports host-call counts, and guest failures use `program_error` with source locations and fixed repair hints. Uncaught host errors retain their type through executor-owned Error identities and host outcome channels. Programs and direct calls share configurable deadlines and report operation, stage, elapsed time, and effective deadline. Unawaited emission delivers MCP media content; emitted text shares the program result cap. All six meta-tools advertise output schemas. The shared discovery and doctor tool-name set excludes `get_result`, retaining negotiated doctor checks. Spec coverage records tool calls with both address headers and output schemas.
|
|
589
|
+
|
|
590
|
+
Result paging binds stashes to the admitted identity, endpoint, origin, connector, and tool, and rechecks current grants, pool membership, and trust before returning data. Legacy unbound stashes are unavailable after upgrade. Truncated direct writes on read-only pools return inline notices and bounded previews without storing results or advertising unavailable paging. Refresh refusals retain write accounting, including uncertain outcomes.
|
|
591
|
+
|
|
592
|
+
Worker guest modules cannot resolve runner modules through dynamic imports or builtin module loaders. A private runner module captures its references before guest module evaluation, and the adapter refuses splices containing more than one expression before loading. Worker typed failure details stay in a per-run host map; the runner retains only random failure IDs by Error identity, and unknown IDs attach no host details. The Worker codec reads only own binary-envelope properties, and completion captures safe array iteration even without a guest prelude. QuickJS checks interrupt and deadline facts before safely describing rejections without guest getters, serialization hooks, or Proxy traps, including interruptions during private diagnostic initialization. Images require a supported MIME type and canonical base64 before collection. Catalog search advertises the shared flat schema; eval programs and graders use typed data and result paging.
|
|
593
|
+
|
|
594
|
+
Worker startup refuses upstream binary codec changes that would bypass own-property checks. Required-field validation messages retain ordinary JavaScript guidance. Bundle baselines record Acorn for the Worker import router and single-expression validation; the parser remains outside the root import graph.
|
|
595
|
+
- Teach the guest global, positional calls, discovery envelopes, JSON and text results, partial failures, and terminal host-call budgets in a shorter usage guide. Route single known reads and writes directly. Check every JavaScript example on QuickJS and Workers, and keep the tool's API notation aligned with the shipped guest declaration.
|
|
596
|
+
- **Breaking:** Use SDK Client.listTools aggregation and a SQL-backed ResponseCacheStore for complete, intake-redacted remote MCP catalogs. Honor bounded downstream ttlMs and public/private scope within host-computed auth partitions; request tokens and personal auth remain private even under public hints. Partition private entries by admitted principal and pool even with shared auth, preserve original fetch age, and refuse invalid header declarations before SDK logging. Replace the registry's memory/stale/persist cache with request-local reads. Remove discovery.persistCatalog and discovery.staleCatalogSeconds; add catalogMinTtlSeconds and catalogMaxTtlSeconds (defaults 0 and 86400). catalogTtlSeconds remains the legacy fallback. Retired registry catalogs are ignored. Transient first-page tool listing failures may use complete SQL-backed facts in the same auth partition for five minutes after expiry. Expiry provenance survives classification; stale fallback and zero-TTL classifications cannot authorize post-entry auth recovery.
|
|
597
|
+
- Default downstream OAuth to per-connector client metadata documents on public HTTPS deployments, retain DCR fallback, support issuer-bound static remote MCP clients, and report the selected registration path. Basecamp no longer requires an external metadata document.
|
|
598
|
+
|
|
599
|
+
Validate callback state and issuer before interpreting authorization errors, then atomically consume the verified consent and discard its PKCE verifier. Error and code callbacks share one claim; Continue starts a new consent after an error.
|
|
600
|
+
|
|
601
|
+
On Disconnect, remove local authorization first and attempt issuer-bound RFC 7009 revocation once when advertised. Failed revocation reports `oauth_revocation_failed` without provider text or credentials.
|
|
602
|
+
|
|
603
|
+
Register raw confidential client IDs and secrets before token and revocation dispatch, including form-encoded Basic credentials, so downstream echoes remain redacted after refresh.
|
|
604
|
+
- Cache downstream protocol verdicts for five minutes within the connector, credential generation, admitted identity and pool. Modern verdicts skip discovery on later requests. Legacy verdicts skip the probe while retaining a fresh initialize handshake. Automatic negotiation retries a probe HTTP 5xx with a fresh legacy transport.
|
|
605
|
+
- **Breaking:** Support Clerk and Cloudflare Access for inbound human authentication, with Access limited to trusted direct Worker context. Machines use stored `cta_` tokens. Remove `/auth/bearer`, `bearerToken`, and request-selected `assertedPrincipal` identities. Install `accessTokens(storage)`, provision a token per machine or human owner, update actor-id grants, rotate clients, and delete old secrets. Access service credentials satisfy the edge only and need a `cta_` token inside connecta. Replace custom auth `final`/`finalRefusals` with synchronous `recognizesCredential`. Select 401 challenges from the provider that actually serves protected-resource metadata, include Clerk scopes, and remove the Clerk authorization-server metadata proxy. See the exact migration steps in `documentation/auth.md`.
|
|
606
|
+
- **Breaking:** **Downstream OAuth state is one grant record per owner and one flow record per
|
|
607
|
+
consent.** Each OAuth connector's owner has one `oauth:grant` record (epoch,
|
|
608
|
+
the latest consent's state digest, and one authorization server's client,
|
|
609
|
+
tokens, and discovery, sealed as one body with the vault), and each consent
|
|
610
|
+
has an `oauth:flow:<sha256(state)>` record expiring with its link. Every grant
|
|
611
|
+
write is a compare-and-set against the record it read, and Restart and
|
|
612
|
+
Disconnect replace the epoch in one, so the epoch namespaces, cleanup lineage
|
|
613
|
+
(`oauth:generation`, `oauth:cleanup:`, `oauth:cleanup-at:`), grace sweeps, and
|
|
614
|
+
flow-entry grant retirement are gone. A grant holds one server: saving
|
|
615
|
+
discovery, a client, or tokens for another replaces it. The refresh
|
|
616
|
+
coordinator stores an accepted rotation before releasing anyone and hands
|
|
617
|
+
later readers that result; the 503 "previous credentials commit" answer is
|
|
618
|
+
gone. The first read of each owner's grant migrates the 0.28 layout once and
|
|
619
|
+
deletes it: issuer-stamped client, tokens, and discovery carry over (sealed
|
|
620
|
+
values reopen under their old keys); a disconnected connector stays
|
|
621
|
+
disconnected; grants from v0.8.1 and earlier and pending consents do not, so
|
|
622
|
+
reissue pending Connect links after upgrading. Rolling back needs consent
|
|
623
|
+
again. A programmatic `finishAuth` names its consent by the callback's
|
|
624
|
+
`state`; one that names no pending consent is refused before anything is sent
|
|
625
|
+
(#707).
|
|
626
|
+
- **Breaking:** Bind modern MCP client capabilities and identity to request-local meta-tool context; record only allowlisted ASCII client name/version facts in activity, including SQLite and D1 storage. Client names are bounded to 64 characters and versions to 32; invalid values are absent at recording, storage write/read, and operator UI boundaries. Advertise the served extension map and private discovery cache hints, declare direct-call address headers, and make doctor negotiate through the SDK and report its revision using the exported META_TOOL_NAMES set. Connecta-owned 403, pool 404, and deadline 504 refusals now return JSON-RPC error bodies with unknown request IDs omitted. Admission codes change from -31001/-31002 to -33001/-33002; deadline, pool, and access refusal codes are -33003/-33004/-33005.
|
|
627
|
+
- Test operator pages with typed visual fixtures and real-server sign-in, OAuth connect, and credential save/test flows. Generate and compare screenshot baselines on Linux Chromium, with a CI workflow for snapshot updates.
|
|
628
|
+
- Rebuild the operator shell on React, Radix and Tailwind v4 with an Inter token layer, light/dark sidebar, accessible dialogs, tabs, tables and a ⌘K command palette. Serve hashed immutable assets on Node and Workers with same-origin script CSP and generate the asset manifest before build and test. Clerk pages admit its validated loader origin and the exact Cloudflare CAPTCHA host, with Clerk image and worker permissions. Apply common HTML security headers to status and error pages, including browser 404s (#708).
|
|
629
|
+
- Use signed-in Claude Code and Codex CLI logins for agent evals, default to Sonnet 5.5 and GPT-6-Luna, and activate the merged resource-read task.
|
|
630
|
+
Grade record facts together across prose, lists and tables, reject conflicting record facts, and require structured absent-service answers backed by genuine discovery.
|
|
631
|
+
- Adopt oxfmt for repository formatting. Contributors should run `npm run format` before committing; `format:check` runs in the fast and full CI checks.
|
|
632
|
+
|
|
633
|
+
### Fixed
|
|
634
|
+
|
|
635
|
+
- **Breaking:** Sanitize connector failures at shared request boundaries (#695). Transport, parser and unexpected handler failures expose checked failure facts and fixed descriptions instead of downstream-derived causes or nested errors. Agent-facing downstream refusals retain their classification and pass through request credential redaction. Callers that inspected error causes must use typed details instead.
|
|
636
|
+
- **Breaking:** Make logs, status and activity payload-free (INV-6, #695, #716). Failure records contain checked typed facts instead of messages, stacks or raw downstream error text. `ActivityEventInput.errorCode` is a closed union, and call-failure logs replace `message` with those facts. Status text and tool names follow the same disclosure checks. Downstream body reads use UTF-8 byte decoding, and catalog drift timestamps are validated before disclosure.
|
|
637
|
+
- Return fixed OAuth registration and discovery failure descriptions with checked origin, status, error code and step facts (#695). Google delegated-token failures use the same rule. Downstream response text does not enter these diagnostics; classification and retryability remain intact.
|
|
638
|
+
- **The test gate fails for behavior, not for load.** On a busy machine —
|
|
639
|
+
several `npm run check` runs at once, or a crowded CI runner — suites failed
|
|
640
|
+
with default workers that passed alone, which teaches a rerun-until-green
|
|
641
|
+
habit that hides real regressions
|
|
642
|
+
([#692](https://github.com/zackbart/connecta/issues/692)). Request
|
|
643
|
+
lifetimes, probe deadlines, refresh claims, and the QuickJS runtime's own
|
|
644
|
+
wall deadline are now tested on a fake clock that moves only when a test
|
|
645
|
+
advances it, each pinned a tick either side of its boundary. A QuickJS pool
|
|
646
|
+
run that outlives its deadline may end at the child's own report or at the
|
|
647
|
+
parent's termination after its fixed grace, and pool cases accept either.
|
|
648
|
+
QuickJS suites not about the guest CPU budget run under a generous one,
|
|
649
|
+
because QuickJS charges that budget by wall clock and a loaded host billed
|
|
650
|
+
its own contention to the guest; the heap-cap case allocates a bounded
|
|
651
|
+
amount, so only an enforced cap fails it, and the event-loop checks derive
|
|
652
|
+
their bound from the guest budget. Real subprocesses, local workerd I/O,
|
|
653
|
+
whole-tree walks, and worst-case payload sweeps keep hang guards sized for a
|
|
654
|
+
loaded host instead of vitest's 5s default. Nothing that ships changes.
|
|
655
|
+
- **Breaking:** Bind downstream OAuth completion to the initiating user for personal and
|
|
656
|
+
shared connectors. Return connecta `/connect` links to agents and operator
|
|
657
|
+
actions; keep status reads passive.
|
|
658
|
+
- Drop downstream tools whose names contain C0, DEL, or C1 control characters at fresh and cached catalog intake. Report only the dropped count in connector drift and keep space and non-ASCII names callable with withheld operator records.
|
|
659
|
+
|
|
660
|
+
Reject control characters in configured static tool names at construction, before startup logs, with a path error that omits the tool name.
|
|
661
|
+
- Keep Clerk SDK upstream error text and workerd native body-reader diagnostics out of operator output. Read all Clerk SDK responses as bytes, route SDK diagnostics through checked failure records, and preserve browser handshake and JWT authentication. Verify response-wrapper fidelity on Node and Workers.
|
|
662
|
+
|
|
663
|
+
The bundled gate client has Connecta-owned declarations for user lookup. The optional Clerk peer accepts compatible 3.x releases independently of the generator's exact internal SDK dependency.
|
|
664
|
+
- Record Clerk authentication and admission denials with checked fixed reason codes. Exclude provider user IDs, email addresses, and email domains from denial logs, including email lookup failures. Add denial canaries for Clerk and Cloudflare Access across logs, activity, and status.
|
|
665
|
+
- Make explicit Authorization headers decisive across inbound auth providers and protected routes. Normalize the case-insensitive Bearer scheme with one shared parser, reject malformed and unsupported headers with a 401 challenge, and exclude ambient cookies, Clerk browser handshake credentials, and Access context. Preserve explicit-header 401 challenges on OAuth starts and callbacks. Keep cookie-only human authentication and stored machine-token ownership.
|
|
666
|
+
- Record undeclared, denied, and throwing pool grants with checked fixed reason codes. Keep identity-provider text out of operator logs and status, while preserving typed activity actors. Cover Clerk OAuth and Access UUID/email pool refusals on Node and Workers. Preserve the operator shell sign-in and CSP contracts after rebasing.
|
|
667
|
+
- Keep downstream public catalog hints within host-computed credential and pool partitions. Request tokens and personal auth always use private cache entries, and OAuth credential changes fence cache reuse even within one epoch. Validate remote resource reads against complete, partitioned advertised resources and URI templates before dispatch; refuse unmatched URIs and encoded traversal with a fixed typed failure. Add a completed SDK catalog refresh hook with previous and new digests for drift-event integration.
|
|
668
|
+
- Keep downstream catalog notifications active for the connection after a successful resource read or listing. Give temporary remote connector instances separate cache lifetimes within the same request. Bind SDK cache I/O and cleanup to each serialized listing's cancellation and generation, let later listings publish a fresh redacted catalog after invalidation, and stop cancelled storage waits from blocking the next listing.
|
|
669
|
+
- Preserve Cloudflare R2 bucket storage-class updates through the REST complement with a validated cf-r2-storage-class header, without requiring jurisdiction for ordinary buckets.
|
|
670
|
+
- **One completed OAuth MCP call no longer aborts its siblings.** A shared
|
|
671
|
+
downstream transport and OAuth provider retained the first call's deadline
|
|
672
|
+
signal, which ends even on success. Subsequent calls and pending parallel
|
|
673
|
+
calls could therefore fail with "The operation was aborted" well before
|
|
674
|
+
their deadlines. The connection now owns that signal; each call keeps its
|
|
675
|
+
own SDK cancellation. A cancelled handshake waiter leaves other waiters
|
|
676
|
+
alone, and the last cancelled waiter still abandons the connection and its
|
|
677
|
+
refresh (#704).
|
|
678
|
+
- Preserve downstream tool definitions in the complete SQL-backed SDK catalog cache. Output-schema validation, `Mcp-Param-*` header mirroring, and refusal of tools requiring task-based execution use those definitions on warm reads as well as after a same-request listing. Registry manifests v2/v3 are retired and refreshed rather than reused (#704).
|
|
679
|
+
- **Tool catalog changes are no longer advertised.** Modern `server/discover`
|
|
680
|
+
and legacy `initialize` report `tools.listChanged: false`, because a fresh
|
|
681
|
+
server per request cannot publish those notifications. Modern
|
|
682
|
+
`subscriptions/listen` requests receive HTTP 404 with JSON-RPC `-32601`
|
|
683
|
+
(Method not found), without acquiring a request-admission permit or opening
|
|
684
|
+
an SSE stream. Authentication and SDK protocol/header validation still run.
|
|
685
|
+
Legacy listens remain unsupported (#704).
|
|
686
|
+
- End `execute_code` at the first host call beyond its budget with one typed
|
|
687
|
+
`budget_exceeded` error, even when the program catches and ignores failures.
|
|
688
|
+
The host suspends the refusing bridge call, closes further host access, and
|
|
689
|
+
releases the sandbox lease. The failure reports payload-free attempted,
|
|
690
|
+
admitted, succeeded, and failed host-call counts (#704).
|
|
691
|
+
- Require consent again for historical OAuth credentials without consistent issuer stamps. The one-shot legacy migration carries only issuer-bound grants and does not adopt unstamped credentials under newly discovered metadata.
|
|
692
|
+
- Keep each OAuth grant bound to one issuer by construction. Flow entry binds the current epoch; reads, writes and consent completion remain fenced to that epoch, so superseded flows cannot change a newer grant or return its consent URL. On the first grant read, the one-shot layout-2 migration preserves consistently issuer-stamped credentials, rejects unstamped or inconsistent credentials, and discards pending consents.
|
|
693
|
+
- Sanitize OAuth token-endpoint failures before they reach the SDK or its console diagnostics. Failures retain the HTTP status, registered OAuth error code and `Retry-After`, with fixed descriptions instead of downstream response text. Invalid code-exchange responses receive the same treatment, and migrated grants retain their issuer stamps.
|
|
694
|
+
- Retry accepted OAuth refresh rotations through bounded grant-commit attempts without sending another refresh request. Exhausted commits require re-consent; waiter deadlines remain retryable `unavailable` failures. Failed credential writes return fixed text without storage error causes.
|
|
695
|
+
- **A token endpoint cannot stamp its own issuer.** A refresh answer carrying
|
|
696
|
+
`issuer` had that field kept when connecta persisted a rotation for a
|
|
697
|
+
cancelled caller with no issuer to stamp. Only the client binds a grant, so
|
|
698
|
+
the field is dropped, as the SDK drops it.
|
|
699
|
+
- Make the optional-module probe deadline test deterministic by waiting for its timer before advancing fake time and awaiting response settlement at the deadline. Preserve the deadline boundary and verify connector scope cleanup.
|
|
700
|
+
- Provider description stamping preserves connector prototypes and inherited
|
|
701
|
+
classification. It defines `describe()` in place when possible, keeping class
|
|
702
|
+
method receivers, and otherwise inherits through a wrapper with only its own
|
|
703
|
+
`describe()` property. Discovery and calls retain the reviewed classification.
|
|
704
|
+
- Changelog assembly requires committed inputs and prints the git restore command
|
|
705
|
+
for recovery after a failure or interruption. The suite guard checks Vitest's
|
|
706
|
+
actual Node and Workers collection, including excluded directories and symlink
|
|
707
|
+
targets (#705).
|
|
708
|
+
- Fence late downstream catalog publications when the SDK receives tools/list_changed, including first cache refreshes. Stop subsequent cache storage operations after cancellation or scope teardown, preserving complete catalogs and ordinary public/private cache hits.
|
|
709
|
+
- Keep core-protocol JSON-RPC refusals, server discovery, registered meta-tool results, and both MCP transports behind the request-scoped credential-echo redaction boundary. Modern direct-call security tests send the required mirrored address header and keep their credential-echo assertions.
|
|
710
|
+
- Complete dispatched downstream OAuth refreshes after caller cancellation and retain their grant commits through Workers waitUntil. Permanently refuse refresh fingerprints after ambiguous network, response-body, or deadline outcomes so later requests and isolates require re-consent without resending the token.
|
|
711
|
+
- Fence downstream OAuth refresh dispatch with a durable CAS state and a 20-second HTTP deadline. Expired unsent claims can be taken over; ambiguous dispatched tokens require re-consent and are never resent. Waiter timeouts, including blocked storage reads and local joiners, return retryable unavailable while keeping the grant. SQLite and D1 create and check TTLs with database time so isolate clock skew cannot expire a live holder.
|
|
712
|
+
- Block outstanding and ambiguous downstream OAuth refresh fingerprints across Restart and re-consent. A resolved fingerprint can be used once in a later epoch only when the code exchange observed its resolution before completing. Preserve pending holder liveness across resets and CAS-guard every resolution transition.
|
|
713
|
+
- Record downstream OAuth refresh-token fingerprints before dispatch with outstanding, ambiguous, and resolved state across grant epochs. Retry valid rotation commits without another HTTP request and require re-consent after every dispatched failure or exhausted commit. Waiter deadlines remain retryable without permitting token replay within the epoch or while the outcome remains outstanding or ambiguous.
|
|
714
|
+
- Bind downstream OAuth consents to their issuer, client, token endpoint, and discovery; advance the epoch when the issuer changes. Coordinate refresh redemption across isolates with shared CAS leases. Retry layout-2 credential cleanup after interrupted migration or reset, and require callback state for both OAuth adapters.
|
|
715
|
+
- **A downstream OAuth callback can no longer redeem its code after a restart,
|
|
716
|
+
and a duplicate can no longer destroy the grant another completed.** The
|
|
717
|
+
exchange claims its consent by compare-and-set after every read it depends
|
|
718
|
+
on, then re-reads the epoch and sends the code in the reaction to that read:
|
|
719
|
+
of duplicate callbacks exactly one sends, the rest get the flat 400 for an
|
|
720
|
+
already-used link, and a restart published before the send fails the callback
|
|
721
|
+
with nothing sent. A refused code invalidates only the client or tokens the
|
|
722
|
+
exchange began with, and consumption is recorded on that consent alone, so a
|
|
723
|
+
delayed duplicate never deletes or invalidates a newer consent Continue
|
|
724
|
+
published (#697). A forced restart still reuses an issuer-bound registration
|
|
725
|
+
and recovers from a refused one by registering again (#611).
|
|
726
|
+
- Preserve discrete catalog-change activity after SDK cache refreshes. Compare hash-only baselines per host-computed cache partition, retain request, owner, pool and deferred-write attribution, and disclose private catalog changes only to their admitted owner. Initial, identical, rejected and abandoned catalogs emit no change event.
|
|
727
|
+
- Match advertised resource URI templates with deterministic forward scans. Refuse ambiguous expression boundaries with resource_template_ambiguous and cap each read at 262144 template-plus-URI characters with resource_match_budget_exceeded. Operator diagnostics retain each typed refusal once without URI or template text. Keep scheme and authority literal, reject Unicode controls and format characters at every decoding layer, and allow safe multi-segment reserved and exploded paths.
|
|
728
|
+
- Show empty credential slots as credential_required. Permit Activity reads for authenticated non-interactive principals explicitly granted activityAccess, with connector, tool, personal-owner and pool disclosure checks. Record call-time classification, result byte counts and discrete catalog changes in nullable activity columns, retaining request grouping and legacy rows.
|
|
729
|
+
- Correct agent eval destination aliases and final-answer evidence grading, record runner limitations as typed N/A skips, and support offline regrading with complete saved grading inputs for future trials. Reject contradictory requested record facts and require a structured refusal outcome, and grade simulated URL-auth behavior for both runners. Commit sanitized final live Sonnet 5.5 and GPT-6-Luna baselines with per-task scores, concrete failure triage and the legacy join prompt ambiguity.
|
|
730
|
+
- Mark failed downstream input continuations as not retryable once their single-use nonce is consumed. Read failures without existing recovery prerequisites direct a fresh input round through the original call. Write reconciliation and auth recovery guidance stay unchanged; spent continuations must not be resent. Typed failure codes and first-call retryability are preserved.
|
|
731
|
+
- Classify a reviewed read as a write when its live schema digest changes or cannot be checked (#705). `call_tool` refuses it toward `call_destructive_tool`; `execute_code` refuses it before dispatch in read-only pools, while trusted programs may dispatch it. Legacy discovery filters such as `approval-required` select write classification, not host approval policy. Digests cover the whole schema, and oversized unchecked schemas fail closed. Request-local entries classify intake-redacted facts using the running release's review and deployment overrides. Complete SQL-backed SDK caches retain facts, not classification verdicts; retired registry catalogs are refreshed instead of preserving old read claims.
|
|
732
|
+
- Storage keys and list prefixes reject U+0000 (NUL) with `TypeError` on D1,
|
|
733
|
+
SQLite, and memory storage before any storage access. Key builders reject
|
|
734
|
+
unencoded NUL components, and state-file migration validates every key before
|
|
735
|
+
importing. This prevents Node 22's `node:sqlite` TEXT results from truncating
|
|
736
|
+
keys at NUL while preserving the existing `connecta_kv` TEXT table (#705).
|
|
737
|
+
- Use plain positional SQL parameters with values bound in statement order on
|
|
738
|
+
both storage drivers. Node's SQLite driver prepares the shared SQL unchanged,
|
|
739
|
+
without rewriting quoted text, on every supported Node version (#705).
|
|
740
|
+
|
|
741
|
+
### Removed
|
|
742
|
+
|
|
743
|
+
- **Breaking:** Workers KV, `fileStorage` from `@zackbart/connecta/node`, and the Worker
|
|
744
|
+
example's copied `cloudflare-kv.ts`, `d1-storage.ts`, `d1-activity.ts`, and
|
|
745
|
+
`d1-activity-row.ts` are removed, as is the Node template's
|
|
746
|
+
`src/file-activity.ts`. `KVStorage.list` and `KVStorage.compareAndSet` are
|
|
747
|
+
required, and `createConnecta` refuses storage missing either at construction.
|
|
748
|
+
Every fallback for stores without compare-and-set is gone: downstream OAuth
|
|
749
|
+
grant discard, stale-write cleanup, and generation fencing; OAuth handoffs and
|
|
750
|
+
single-use connect links; and access-token rename and revocation.
|
|
751
|
+
The Worker example binds one D1 database, `CONNECTA_DB`; the Node template
|
|
752
|
+
keeps everything, activity included, in `CONNECTA_DATABASE` (default
|
|
753
|
+
`./.connecta.sqlite`, `/data/connecta.sqlite` in the container), replacing
|
|
754
|
+
`CONNECTA_STATE_FILE` and `CONNECTA_ACTIVITY_FILE` (#705).
|
|
755
|
+
- **Breaking:** Remove catalog drift counts from `/health`, `connecta doctor`, and activity. Remove the old optional drift-count channel and its event type. Keep discrete catalog-change events in the ordinary activity timeline. Authenticated connector status and the maintainer-run provider check retain drift observations.
|
|
756
|
+
- **Breaking:** Remove built-in artifacts: the `connecta/artifacts` subpath, `artifacts:` config
|
|
757
|
+
slot and `artifactOrigin`, operator pages and navigation, scheduled refresh,
|
|
758
|
+
and agent tools and guide. Publishing belongs in dedicated services.
|
|
759
|
+
|
|
760
|
+
Delete the `connecta/artifacts` import and `artifacts:` configuration, plus
|
|
761
|
+
`artifactOrigin` if set; a configuration that still contains either key fails
|
|
762
|
+
at construction with a `ConfigError`. Remove `CONNECTA_ARTIFACTS` and refresh timers or cron
|
|
763
|
+
handlers. Dedicated artifact bindings or storage can optionally be dropped.
|
|
764
|
+
Existing artifact data is left untouched and ignored; nothing migrates, reads,
|
|
765
|
+
or deletes it as part of this removal (#709).
|
|
766
|
+
|
|
767
|
+
### Security
|
|
768
|
+
|
|
769
|
+
- Bind GitHub repository grants to repository IDs and narrow installation tokens with `repository_ids`. Renames retain their grants; transfers and replacement repositories at reused names cannot inherit cached access. Verify discovery and search repository IDs before returning data.
|
|
770
|
+
- Collect sent credentials across every connector context in one upstream request
|
|
771
|
+
and redact meta-tool, guest bridge, and serialized MCP outputs at one shared
|
|
772
|
+
boundary. Redact failed catalog listings before shared refresh publication and
|
|
773
|
+
keep catalogs and result stashes sanitized before caching.
|
|
774
|
+
- Refuse credential echoes in normalized downstream OAuth metadata and saved authorization URLs, and restrict operator failure origins. Use an eight-character floor for all echo matching and warn once when configured secrets are below it.
|
|
775
|
+
- Resource URI template matches now re-check the final authority, host interpretation, and path. Matching refuses Unicode separators, URI syntax lookalikes, and dot segments while preserving encoded-percent literals, ordinary percent-encoded UTF-8 path segments, and bounded matching.
|
|
776
|
+
- Redact credentials used by a call from agent-facing downstream diagnostics,
|
|
777
|
+
tool results, and nested errors across all connectors and guest program calls.
|
|
778
|
+
The per-call secret set stays in memory and covers credential slots, auth
|
|
779
|
+
headers, bearer tokens, URL encodings, and base64 forms. Register final outgoing
|
|
780
|
+
requests, including auxiliary OAuth headers and query credentials. Redact mixed
|
|
781
|
+
JSON escapes and joined text blocks after unwrapping and before paging, emits,
|
|
782
|
+
or program returns. Cache one matcher per secret set. Match
|
|
783
|
+
credentials of at least eight characters to avoid corrupting ordinary text with
|
|
784
|
+
short Basic usernames; Connecta's own messages never quote credential values.
|
|
785
|
+
- Register credentials sent during remote MCP discovery and retries, and redact every catalog metadata string before retaining definitions or caching catalogs. Refuse the complete catalog when redaction would change a tool name, preserving dispatch names and catalog completeness.
|
|
786
|
+
- Refuse redirects for credential-bearing OAuth token requests in remote MCP and static API connectors. A redirected refresh requires re-consent without another send. Keep spent fingerprints across grant epochs. A new consent can use a byte-identical refresh token only after the earlier dispatch resolved and the consent observed that resolution.
|
|
787
|
+
|
|
788
|
+
## 0.28.1 — 2026-10-05
|
|
789
|
+
|
|
790
|
+
This patch adds six maintained connections for church operations and the
|
|
791
|
+
downstream OAuth they needed. Planning Center, Overflow, Tithe.ly, Breeze ChMS,
|
|
792
|
+
and Church Community Builder (Pushpay ChMS) are hand-written `api()` surfaces
|
|
793
|
+
over their whole APIs, with every money-moving write behind approval; Basecamp
|
|
794
|
+
proxies 37signals' hosted MCP server. Church Community Builder accepts only
|
|
795
|
+
OAuth, so `api()` connectors can now declare a static OAuth 2.0
|
|
796
|
+
authorization-code grant that reuses the `remoteMcp()` machinery. Each
|
|
797
|
+
connection is a new opt-in provider subpath and the OAuth grant is opt-in per
|
|
798
|
+
connector: existing imports, connector declarations, credentials, and
|
|
799
|
+
deployment behavior do not change. Two fixes a deployment may notice: a forced
|
|
800
|
+
OAuth restart no longer reuses a client registration the provider has deleted
|
|
801
|
+
([#611](https://github.com/zackbart/connecta/issues/611)), and a failed OAuth
|
|
802
|
+
code exchange is now logged as fixed text rather than the provider's message.
|
|
803
|
+
Internally, the guarded transport can repeat a query key for an array value,
|
|
804
|
+
and `api()`'s builder moved into its own module so OAuth stays out of bundles
|
|
805
|
+
that never use it.
|
|
806
|
+
|
|
807
|
+
### Added
|
|
808
|
+
|
|
809
|
+
- **Downstream OAuth on `api()` connectors.** A hand-written connector can
|
|
810
|
+
declare `oauth: { authorizationEndpoint, tokenEndpoint, clientId,
|
|
811
|
+
clientSecret?, tokenEndpointAuthMethod?, scope?, pkce?, authorizationParams?,
|
|
812
|
+
tokenRequestHeaders?, apiOrigins }` for a REST API that accepts only the authorization code grant,
|
|
813
|
+
such as Church Community Builder. Nothing is discovered or registered: the
|
|
814
|
+
endpoints and the provider-issued client are configuration, and the client
|
|
815
|
+
secret comes from the deployment's environment and is never stored. Handlers
|
|
816
|
+
call `ctx.oauth.fetch(url, init)`, which sends the calling owner's token as a
|
|
817
|
+
bearer to a declared origin only, answers a 401 with one coordinated refresh
|
|
818
|
+
and one replay, and otherwise fails `auth_required`. The grant is the
|
|
819
|
+
`remoteMcp()` grant underneath — the same sealed epochs, shared and personal
|
|
820
|
+
ownership, rotating-refresh coordination, dead-versus-outage verdicts,
|
|
821
|
+
`/oauth/callback/<id>`, `authorize_connector`, and operator Connect,
|
|
822
|
+
Restart, and Disconnect. `oauth` and `credential` are exclusive on one
|
|
823
|
+
connector. See [downstream OAuth on `api()`](./documentation/auth.md#downstream-oauth-on-api).
|
|
824
|
+
- **Church Community Builder (Pushpay ChMS) connection.**
|
|
825
|
+
`@zackbart/connecta/providers/ccb` exports `ccb(id, { purpose, environment,
|
|
826
|
+
mode, clientId, clientSecret, access?, scopes?, subdomain? })`, a
|
|
827
|
+
hand-written v2 REST connection over `api()`'s OAuth grant. `environment`
|
|
828
|
+
(`production` or `sandbox`) and `mode` (`system`, one church-wide grant an
|
|
829
|
+
administrator approves; `identity`, each person signing in as themselves
|
|
830
|
+
with a personal grant) are required, because CCB credentials do not cross
|
|
831
|
+
environments and the two modes reach different data. Twenty-four named reads
|
|
832
|
+
cover people and families, groups and participants, events and attendance,
|
|
833
|
+
forms and responses, processes and queues, scheduling, notes, and giving
|
|
834
|
+
metrics and pledges, with projections that drop allergies, giving
|
|
835
|
+
identifiers, and prayer requests unless `raw: true`. `ccb_api_get` and
|
|
836
|
+
`ccb_api_search` reach every GET and advanced search read-only, and
|
|
837
|
+
`ccb_api_mutate` ships only when a write scope is requested. A default
|
|
838
|
+
60-per-minute admission window per endpoint transcribes CCB's documented
|
|
839
|
+
limit, and `npm run providers:check -- --provider ccb` compares the 41
|
|
840
|
+
touched endpoints with Pushpay's published OpenAPI document. Clients are
|
|
841
|
+
issued by hand through Pushpay's API access request form.
|
|
842
|
+
- **Maintained Planning Center connection.**
|
|
843
|
+
`@zackbart/connecta/providers/planning-center` exports `planningCenter(id,
|
|
844
|
+
options)`, a hand-written `api()` surface over Planning Center Online's REST
|
|
845
|
+
API with no SDK and no new dependency. Named tools cover the jobs church
|
|
846
|
+
staff run — People search, profiles, contact records, lists, workflows and
|
|
847
|
+
card actions, notes, custom fields, and form submissions; Services plans,
|
|
848
|
+
teams, schedules, songs, and scheduling; Groups memberships and events;
|
|
849
|
+
Check-Ins attendance; Calendar occurrences; Registrations signups; and Giving
|
|
850
|
+
donations, funds, batches, pledges, and recurring gifts as reads only — with
|
|
851
|
+
JSON:API flattened into lean records and `raw: true` beside them.
|
|
852
|
+
`pco_api_get` (read-only) and `pco_api_mutate` (always approval-gated) reach
|
|
853
|
+
every other endpoint, including every write that moves money. The credential
|
|
854
|
+
is a personal access token as two labeled fields, and the Test action names
|
|
855
|
+
the person and organization it acts as. Every request sends a reviewed
|
|
856
|
+
`X-PCO-API-Version` per product and a configurable `User-Agent`, and the
|
|
857
|
+
default call budget transcribes Planning Center's 100 requests per 20
|
|
858
|
+
seconds. `npm run providers:check -- --provider planning-center` compares
|
|
859
|
+
the touched endpoints with Planning Center's per-product OpenAPI documents
|
|
860
|
+
and reports a newly published version of any pinned product. Planning
|
|
861
|
+
Center's own hosted MCP server was considered and is not wrapped: its writes
|
|
862
|
+
stop at People.
|
|
863
|
+
- **Maintained Overflow connection.** `@zackbart/connecta/providers/overflow`
|
|
864
|
+
ships 16 projected reads over contributions, deposits and their line items
|
|
865
|
+
and totals, refunds, chargebacks, donors, recurring gifts, payment methods,
|
|
866
|
+
campaigns, locations, Tap events, and webhook delivery logs, plus a
|
|
867
|
+
read-only GET hatch and a destructive JSON-mutation hatch that carries every
|
|
868
|
+
Overflow write — charges, payment authorization, refunds, recurring-gift
|
|
869
|
+
changes, and donor edits — to the host's approval prompt. `environment`
|
|
870
|
+
(`"production"` or `"staging"`) is required with no default, the credential
|
|
871
|
+
is one operator slot with `clientId` and `apiKey` fields, list rows keep
|
|
872
|
+
donor contact details and webhook payloads out unless `raw: true`, and the
|
|
873
|
+
default admission budget transcribes Overflow's 120 requests per minute per
|
|
874
|
+
client. `npm run providers:check -- --provider overflow` compares all 38
|
|
875
|
+
operations with the OpenAPI document Overflow publishes on staging.
|
|
876
|
+
- A maintained Tithe.ly giving connection, `tithely(id, { purpose, environment })`
|
|
877
|
+
from `@zackbart/connecta/providers/tithely`: a hand-written `api()` surface
|
|
878
|
+
over Tithe.ly's v1 REST API. `environment` (`"live"` for tithe.ly, `"test"`
|
|
879
|
+
for tithelydev.com) is required with no default, because Tithe.ly's key pairs
|
|
880
|
+
are environment-specific and carry nothing that says which. The credential
|
|
881
|
+
slot takes the public and private keys as two fields, refuses an obviously
|
|
882
|
+
swapped pair without a request, and its Test action reports the organization
|
|
883
|
+
the keys reach; API access is by request through support@tithe.ly. Named
|
|
884
|
+
read-only tools cover organizations and their funds, donor accounts, charges
|
|
885
|
+
by organization or donor and created-date range, recurring gifts, and
|
|
886
|
+
payment-method metadata, with amounts as integer cents, dates as ISO 8601,
|
|
887
|
+
`hasMore` paging, and organization projections that omit payout bank and
|
|
888
|
+
legal-contact details. Every write moves money or donor payment state and is
|
|
889
|
+
reached only through the always-destructive, form-encoded
|
|
890
|
+
`tithely_api_mutate`; `tithely_api_get` reaches every v1 read. No call budget
|
|
891
|
+
is declared, since Tithe.ly publishes no rate limit.
|
|
892
|
+
`npm run providers:check -- --provider tithely` digests the OpenAPI snippet
|
|
893
|
+
each reference page embeds, for every documented v1 operation.
|
|
894
|
+
- A maintained Breeze ChMS connection (Tithely Church Management since
|
|
895
|
+
2025-11-11) at `@zackbart/connecta/providers/breeze`:
|
|
896
|
+
`breeze(id, { subdomain, purpose })` binds one church's
|
|
897
|
+
`<subdomain>.breezechms.com` — validated as a single hostname label at
|
|
898
|
+
construction, so no argument can steer the API key elsewhere — with an
|
|
899
|
+
operator `Api-Key` slot tested against the account summary. Named reads cover
|
|
900
|
+
people, profile fields, tags, events, calendars, attendance, forms,
|
|
901
|
+
volunteers, the account log, and giving (`list_contributions`, `list_funds`,
|
|
902
|
+
which call endpoints Breeze stopped documenting in 2023 and still serves);
|
|
903
|
+
named writes add and update people, assign and unassign tags, and record a
|
|
904
|
+
check-in. Because Breeze sends writes as GET too, the escape hatches split by
|
|
905
|
+
endpoint rather than method: `breeze_api_get` admits only a reviewed
|
|
906
|
+
allowlist of read endpoints and their documented parameters, and the
|
|
907
|
+
destructive `breeze_api_mutate` reaches everything else. No admission budget
|
|
908
|
+
is declared, since Breeze documents no rate limit; drift is review-only,
|
|
909
|
+
since it publishes no machine-readable specification.
|
|
910
|
+
- **`basecamp()` — a maintained Basecamp hosted-MCP connection** at
|
|
911
|
+
`@zackbart/connecta/providers/basecamp`, proxying
|
|
912
|
+
`https://mcp.basecamp.com/mcp` — a server 37signals runs but has not
|
|
913
|
+
documented or announced. OAuth only, and `clientMetadataUrl` is required:
|
|
914
|
+
Basecamp restricts dynamic client registration for HTTPS callbacks, so a
|
|
915
|
+
deployment hosts a Client ID Metadata Document listing
|
|
916
|
+
`<publicUrl>/oauth/callback/<id>`, and construction throws without one. A
|
|
917
|
+
grant reaches exactly one Basecamp account, chosen at consent, so the guide
|
|
918
|
+
leads with that, names `get_me` to confirm it, and a second account is a
|
|
919
|
+
second connector. All 227 tools in the live catalog are classified (105
|
|
920
|
+
read-only, 34 additive, 88 destructive); the four verdicts that part from the
|
|
921
|
+
server's own annotations, and `create_stream_ticket`'s agreement with its
|
|
922
|
+
read-only claim, are argued beside their rows. No access mode ships: the MCP
|
|
923
|
+
client requests the scope Basecamp's challenge names, so a `read` option
|
|
924
|
+
would be a label rather than a limit. Personal access tokens are not offered,
|
|
925
|
+
because nothing shows this endpoint accepts one. No default `callAdmission`:
|
|
926
|
+
Basecamp's limits are dynamic, multi-tier, and metered at its own server's
|
|
927
|
+
IP. With no public setup page or inventory, `npm run providers:check` reads
|
|
928
|
+
the server's OAuth discovery instead — the resource names the endpoint and
|
|
929
|
+
the authorization server still accepts metadata-document clients — and
|
|
930
|
+
catalog drift is runtime-only.
|
|
931
|
+
|
|
932
|
+
### Fixed
|
|
933
|
+
|
|
934
|
+
- **A forced OAuth restart can no longer wedge on a purged client
|
|
935
|
+
registration** ([#611](https://github.com/zackbart/connecta/issues/611)).
|
|
936
|
+
Restart already carried an issuer-bound dynamic registration into the new
|
|
937
|
+
epoch; it carried it unconditionally, and a provider that had forgotten the
|
|
938
|
+
client refuses it at consent without ever redirecting back, so every later
|
|
939
|
+
Restart reused the same dead client and only Disconnect escaped. A carried
|
|
940
|
+
client is now carried again only once a grant in its epoch proves the
|
|
941
|
+
provider still knows it, so two restarts in a row with no consent between
|
|
942
|
+
them register at most once more. A URL-based client (`clientMetadataUrl`)
|
|
943
|
+
and a client whose secret has expired are never carried. Continue no longer
|
|
944
|
+
hands back a pending consent URL whose client a callback's `invalid_client`
|
|
945
|
+
has dropped; it starts a flow that registers instead. Static `api()` OAuth
|
|
946
|
+
clients are configuration and unaffected.
|
|
947
|
+
- **A refresh rotation saved after its request was cancelled keeps its
|
|
948
|
+
issuer.** When the token endpoint answered after the owning request left,
|
|
949
|
+
connecta persisted the rotated tokens itself but unbound, and the next
|
|
950
|
+
issuer-aware read adopted them for whichever authorization server the
|
|
951
|
+
connector named by then — so a connector repointed at another server would
|
|
952
|
+
send the rotated refresh token there. The recovery save now stamps the issuer
|
|
953
|
+
the refresh answered to, as the SDK's own save does, and a repointed connector
|
|
954
|
+
fences the grant instead.
|
|
955
|
+
- **A failed OAuth code exchange no longer writes the provider's message to the
|
|
956
|
+
operator log.** The SDK quotes a token endpoint's `error_description` or raw
|
|
957
|
+
body, and a provider that echoes a `client_secret_post` request put the client
|
|
958
|
+
secret, code, and verifier in the log. The callback now logs fixed text and,
|
|
959
|
+
when the error carries one, a standard OAuth error code from an allowlist.
|
|
960
|
+
|
|
5
961
|
## 0.28.0 — 2026-10-04
|
|
6
962
|
|
|
7
963
|
This minor release removes program pauses ([#672](https://github.com/zackbart/connecta/issues/672)).
|
|
@@ -1480,7 +2436,7 @@ residue from #179, and the v1 persisted-catalog reader, unreachable since
|
|
|
1480
2436
|
- **Package and docs.** Six finished design records moved to
|
|
1481
2437
|
[`records/`](https://github.com/zackbart/connecta/tree/main/records), outside
|
|
1482
2438
|
the published files, and packed-link validation folded into the documentation
|
|
1483
|
-
checker: −1,342 shipped lines, −91 KB (#464).
|
|
2439
|
+
checker: −1,342 shipped lines, −91 KB (#464). the ethos is the constitution
|
|
1484
2440
|
again — refusals, invariants, and what this is, at 1,188 words instead of
|
|
1485
2441
|
3,174, with every decision intact and a word cap replacing the line cap
|
|
1486
2442
|
(#471).
|
|
@@ -3026,7 +3982,7 @@ the new text is the entire release (#282, PR #284).
|
|
|
3026
3982
|
says to fetch first and check the shape in code, and on a surprise — empty
|
|
3027
3983
|
array, missing key — to return a trimmed first record instead of rendering.
|
|
3028
3984
|
The wrong view becomes the sample you needed.
|
|
3029
|
-
- **Result sampling on the catalog surface is refused.** The
|
|
3985
|
+
- **Result sampling on the catalog surface is refused.** The former ethos
|
|
3030
3986
|
decisions table records `sample` / `dryRun` as refused: sampling is execution
|
|
3031
3987
|
and cannot ride a catalog read, most tools carry required arguments no
|
|
3032
3988
|
sampler can invent, and a program that checks the shape before rendering
|
|
@@ -3383,7 +4339,7 @@ nine tools with the same descriptions and the same instructions as before, byte
|
|
|
3383
4339
|
for byte. `surface: "classic"` alongside an executor restores the ten-tool shape.
|
|
3384
4340
|
|
|
3385
4341
|
The default was flipped by owner decision for a single-operator deployment rather
|
|
3386
|
-
than by the repeated per-model eval that [
|
|
4342
|
+
than by the repeated per-model eval that [former ethos](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md) had gated it on;
|
|
3387
4343
|
that row now records the decision and its reasoning, and
|
|
3388
4344
|
[`eval/code-first-gate`](./eval/code-first-gate/README.md) remains as measurement
|
|
3389
4345
|
with all three of its arms now real deployment shapes rather than harness
|
|
@@ -3648,13 +4604,13 @@ optional property checks, and an automated Workers test-partition guard. A
|
|
|
3648
4604
|
deployment can take this release without touching its config: entrypoints,
|
|
3649
4605
|
options, storage formats, and the tool surface are unchanged. The docs were
|
|
3650
4606
|
restructured — the old numbered manual is retired in favor of a terse
|
|
3651
|
-
[
|
|
4607
|
+
[former ethos](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md) and stub guides in `documentation/` — and the
|
|
3652
4608
|
temporary `@hono/node-server` override is gone now that the SDK (1.30.0)
|
|
3653
4609
|
depends on a patched version upstream.
|
|
3654
4610
|
|
|
3655
4611
|
### Added
|
|
3656
4612
|
|
|
3657
|
-
-
|
|
4613
|
+
- **The former ethos document** — what connecta is, what it refuses to be, a decisions table,
|
|
3658
4614
|
and the invariants, each enforced or reviewer-owned (#175, PR #180). CI caps
|
|
3659
4615
|
its length; terseness is the point.
|
|
3660
4616
|
- **Route-contract and operator-UI browser tests** pinning server behavior
|