@zackbart/connecta 0.41.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/bin/connecta.mjs +26 -17
- package/bin/meta-tool-names.d.mts +3 -1
- package/bin/meta-tool-names.mjs +4 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +2 -1
- package/dist/activity.js +5 -5
- package/dist/advertised-schema.js +15 -0
- package/dist/agent-routes.js +31 -5
- package/dist/auth/downstream-oauth/refresh-coordinator.js +4 -1
- package/dist/auth/downstream-oauth.js +3 -0
- package/dist/auth/static-oauth.js +26 -18
- package/dist/auth-elicitation.js +118 -61
- package/dist/authorization-handoff.js +36 -3
- package/dist/catalog-cache.js +292 -24
- package/dist/catalog-freshness.js +12 -0
- package/dist/catalog-service.js +373 -70
- package/dist/catalog.js +147 -58
- package/dist/config-defaults.d.ts +2 -1
- package/dist/config-defaults.js +4 -1
- package/dist/config.d.ts +43 -17
- package/dist/config.js +26 -10
- package/dist/connectors/remote-mcp/catalog.js +52 -28
- package/dist/connectors/remote-mcp/connection.js +62 -5
- package/dist/connectors/remote-mcp.js +10 -1
- package/dist/describe-config.d.ts +3 -2
- package/dist/discovery-health.js +26 -11
- package/dist/errors.d.ts +52 -3
- package/dist/errors.js +86 -3
- package/dist/execute.js +476 -200
- package/dist/executor-admission.js +2 -0
- package/dist/executor-contract.js +6 -0
- package/dist/executor-result.js +2 -2
- package/dist/executors/quickjs-child.js +13 -4
- package/dist/executors/quickjs-runtime.d.ts +2 -0
- package/dist/executors/quickjs-runtime.js +36 -19
- package/dist/executors/quickjs.js +23 -6
- package/dist/guest-functions.js +5 -4
- package/dist/guest-runtime.js +37 -1
- package/dist/guest-types.d.ts +18 -0
- package/dist/http/client.js +7 -2
- package/dist/http/errors.js +9 -4
- package/dist/http/fields.js +17 -5
- package/dist/http/hatch.js +9 -3
- package/dist/http/paging.js +1 -1
- package/dist/http/provider.js +2 -6
- package/dist/http/vendor.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -0
- package/dist/invocation.js +98 -34
- package/dist/meta-output.js +24 -18
- package/dist/meta-tool-names.d.ts +1 -1
- package/dist/meta-tool-names.js +1 -1
- package/dist/meta-tools.js +259 -60
- package/dist/native-recovery.js +4 -1
- package/dist/native-tools.js +166 -51
- package/dist/node.js +3 -1
- package/dist/oauth-handoff.js +53 -43
- package/dist/operation-activity.js +2 -1
- package/dist/operation-vocabulary.js +6 -0
- package/dist/operator-record.d.ts +1 -1
- package/dist/operator-record.js +1 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/program-result.js +2 -0
- package/dist/program-source.js +1 -1
- package/dist/program-writes.js +112 -19
- package/dist/provider.d.ts +0 -13
- package/dist/provider.js +0 -3
- package/dist/providers/_shared/google/kit.js +2 -2
- package/dist/providers/breeze/api.js +2 -2
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/ccb/api.js +2 -2
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +2 -2
- package/dist/providers/cloudflare/index.js +2 -0
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/forms/index.js +1 -0
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/github/api.js +98 -6
- package/dist/providers/github/index.js +7 -2
- package/dist/providers/github/lib/auth.d.ts +3 -1
- package/dist/providers/github/lib/auth.js +43 -12
- package/dist/providers/github/lib/discovery.js +11 -1
- package/dist/providers/github/lib/operations.js +8 -0
- package/dist/providers/github/safety.js +39 -0
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +12 -5
- package/dist/providers/gmail/index.js +1 -0
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/index.js +2 -0
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/index.js +11 -5
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/skill.generated.js +1 -1
- package/dist/providers/quickbooks/index.js +2 -5
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/index.js +1 -0
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/slides/index.js +2 -2
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/index.js +1 -0
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +2 -2
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/registry.d.ts +36 -4
- package/dist/registry.js +127 -27
- package/dist/result-preview.js +210 -0
- package/dist/result-seal.js +168 -0
- package/dist/route-names.js +13 -4
- package/dist/routes/connect.js +21 -13
- package/dist/routes/mcp.js +55 -26
- package/dist/routes/shared.d.ts +6 -0
- package/dist/runtime/call.js +15 -10
- package/dist/runtime/request-scope.js +36 -1
- package/dist/runtime/run.js +4 -1
- package/dist/sent-secrets.d.ts +20 -0
- package/dist/sent-secrets.js +49 -1
- package/dist/skills.js +100 -33
- package/dist/storage/keys.js +9 -6
- package/dist/storage/sql-native.js +1 -1
- package/dist/tool-safety.d.ts +33 -0
- package/dist/tool-safety.js +51 -15
- package/dist/types.d.ts +31 -0
- package/dist/usage-guide.js +14 -13
- package/dist/validate.js +29 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +28 -8
- package/documentation/architecture.md +104 -42
- package/documentation/auth.md +46 -19
- package/documentation/code-mode.md +378 -116
- package/documentation/deploying.md +10 -9
- package/documentation/hosted-mcp-migration.md +9 -3
- package/documentation/integrating.md +27 -5
- package/documentation/meta-tools.md +655 -153
- package/documentation/operating.md +5 -3
- package/documentation/providers.md +18 -6
- package/examples/worker/README.md +3 -3
- package/guides/how-it-works.md +2 -1
- package/guides/safety-and-trust.md +1 -3
- package/package.json +1 -1
- package/templates/node/AGENTS.md +3 -3
- package/templates/node/README.md +1 -1
- package/templates/node/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.42.0 — 2026-11-05
|
|
6
|
+
|
|
7
|
+
This release makes Connecta easier for agents to use: shorter served text, failures that name the next step, a read-only program route, and better discovery. Six breaking changes can alter how existing clients or deployments behave, so check them before upgrading.
|
|
8
|
+
|
|
9
|
+
**Breaking:** A deployment `classification` override to `"read"` no longer applies to a tool whose source declares a write. That tool stays a write on every surface, so its calls prompt on hosts that prompt, a direct `call_tool` is refused, and a read-only pool loses it. Remove the override, or change it to the new `"read-repeatable"` if you have checked that a repeat is harmless. Native pool writes are now named `<connector>__WRITE__<tool>`, or a hashed form of it, so update anything that hardcodes a native write's name, such as host approval rules, allowlists and prompts. `execute.watchdogMs` now defaults to 55 s (was 120 s) and caps direct calls, and `workerExecutor()` defaults to a 50 s timeout and a 40 s queue deadline; raise them only below your clients' own request limit. GitHub answers a resource HTTP 403 as `provider_permission_denied` instead of `auth_required`, without a credential handoff. A `skills` fetch returns its text once, in `content`, not in `structuredContent.text`. `@zackbart/connecta-eve` now requires exactly Eve `1.0.0-rc.4` as its peer; its README lists what that brings to your app, including a default `edit_file` tool to gate wherever `write_file` is gated.
|
|
10
|
+
|
|
11
|
+
**Reads and programs:** Writable `/mcp` and `/mcp/<pool>` endpoints now list a seventh meta tool, `execute_read_code`, annotated read-only and refusing every write before dispatch, so hosts that gate on annotations can run read-only programs without a write approval. Set `execute: { readRoute: false }` to turn it off, and read the read programs section in `documentation/meta-tools.md` first: such a program runs unprompted on hosts that auto-approve reads, and a read tool that takes a URL or query can carry data out in its arguments. `connecta.result(handleOrId, { all: true })` reads a retained result whole up to 1.5 MiB. A direct read over its inline cap can now answer with a sample in the result's own shape rather than its first bytes, so a client that parses a truncated direct result should not assume the body is a prefix of the result.
|
|
12
|
+
|
|
13
|
+
**Failures and recovery:** Failure messages now give the exact next step, and anything branching on `nextAction.tool === "authorize_connector"` must also accept the new handoff shapes. A program that may have written warns against rerunning it, program writes report how they failed in `unknownWrites`, and a tool's `isError` text now reaches agents up to 4,096 UTF-8 bytes. Program errors and timeouts arrive as typed failures before MCP clients give up on the request. A QuickJS crash that could cost a run its answer, or the next request, is fixed.
|
|
14
|
+
|
|
15
|
+
**Discovery:** Served instructions and tool descriptions are shorter, and connector guides now hold only provider-specific facts. Search ranks partial matches by the rarity of the query words they cover, so a hatch's common verb no longer hides a named tool. A connector that does not answer in time keeps its tools in discovery from its last complete catalog, marked `stale: true`. The `execute_code` inventory can show each connector's configured description as a short purpose.
|
|
16
|
+
|
|
17
|
+
Deployments without these overrides, native write names, longer client timeouts or the Eve helper need no configuration changes.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- The `execute_code` connector inventory now adds each connector's configured description as a one-line purpose, capped at 60 UTF-8 bytes. Purposes are the first detail dropped under the inventory's 1,024-byte cap, and they never appear in the server instructions.
|
|
22
|
+
- Add an eval-only Eve runner using local ChatGPT subscription models, the Connecta Eve helper, deferred tool traces, and the existing task graders on Node and Workers, across every eval surface, with a configurable connection name and description.
|
|
23
|
+
- Teach Eve agents how to reach Connecta tools: `connectaInstructions` now places an Eve guide before the server's text, with a `connection` option for connections not named `connecta`. `connectaConnection`'s `description` is optional and warns when it carries no domain information, both factories accept `headers`, and the helper warns on release skew. Discovery and `initialize` advertise caller-scoped endpoint facts in `capabilities.extensions["dev.connecta/endpoint"]`.
|
|
24
|
+
- Writable `/mcp` and `/mcp/<pool>` endpoints now list a seventh meta tool, `execute_read_code`, annotated read-only, so hosts that gate on annotations need no write approval for read-only programs. It has the same guest API, sandbox and connector inventory as `execute_code`, and refuses every call whose effective or source class is write before dispatch, with the next step and zero writes, whether or not the program catches the refusal. A tool set to `"read-repeatable"` runs there, since the deployment accepted that hosts may repeat it. Discovery inside it lists writes with `callable: false`. Handles from direct reads and read programs page from it. Read-only endpoints, `/agent/<pool>` and native pools are unchanged. `execute: { readRoute: false }` turns it off. Activity records source `execute_read_code`. A read program runs unprompted on hosts that auto-approve reads, and a read tool that takes a URL or query can carry data out in its arguments: see the read programs section in `documentation/meta-tools.md`.
|
|
25
|
+
- GitHub's reviewed read allowlist now covers check runs, check suites, check-run annotations and commit statuses. These reads require the App to hold Checks read or Commit statuses read; an App without them fails only those reads, with a typed permission failure, until the permission is granted.
|
|
26
|
+
- Add twelve hard-discovery eval tasks over a shared 110-tool catalog, with no-model controls on every arm and target.
|
|
27
|
+
- Add eval arms for host-native search and five meta-tools, deterministic catalog padding, and first-request and discovery efficiency telemetry, per-trial distractor reporting, and an explicit list of tasks the no-code arm cannot be compared on.
|
|
28
|
+
- Add `connecta.result(handleOrId, { all: true })`, which reads a retained result whole as `{ data, format }` for one host call up to a read ceiling of 1.5 MiB (scaled with `cpuTimeMs` on QuickJS, and declared by the new optional `Executor.maxReadBytes`), and throws a catchable `result_too_large` above it. A read's finished transfer now gives its stash back, up to three per run within a one-second budget, leaving the run's unsealed ledger first. Handles within the ceiling now say to read whole. Migration: an oversized program return's `nextAction` now reads whole and marks where to reduce, instead of returning a 3,829-byte page, so code that parses that action's output as a page should read `connecta.result` with `{ offset }` instead.
|
|
29
|
+
- `classification` accepts a new value, `"read-repeatable"`, the migration path for a deployment that overrode a vendor-declared write to `"read"` on purpose. The deployment has checked the tool and accepts that hosts may repeat it, so it serves as a read on every route: read annotations, read-only endpoints, `call_tool`, programs and its plain native name. A host's resend after a connection failure can reach the vendor twice. Connecta still sends it at most once and never replays it after an authorization failure. Plain `"read"` is unchanged: it does not apply over a declared write, so a vendor that later declares one fails safe.
|
|
30
|
+
- Add eval-only native and native-without-code pool arms for Worker comparisons, with native call evidence and surface controls.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Failure messages now give the exact next step in one read. An auth error that carries a handoff says so in `nextAction` (`{ url, purpose }`, or `{ action: "contact_operator", purpose }` when there is no link) instead of sending the agent to `authorize_connector` for the same thing, and its `instructions` address the person opening the link; `authorizationUrl`, `operatorUrl`, `credential` and `instructions` keep their meaning, and anything branching on `nextAction.tool === "authorize_connector"` must also accept the new shapes. A program that finishes with nothing to read gets a `note`, which after a write says not to rerun it; an unawaited read in a program that finished gets the await hint there too. A truncated direct read offers a `connecta.result` loop over the retained result in one program when that fits the host-call budget, and repeats the read otherwise; a paged handle offers that loop on the same terms, counting the calls its program has left, and otherwise bounds the pages per program. Budget, write-outcome, read-only, unknown-tool, empty-search, program-error and direct-call-shape messages name what to do next; `invalid_args` with complete findings no longer sends the agent to search first, and its example uses a named placeholder rather than an empty string. The `usage` paging example no longer sets `maxBytes`, which could exhaust the host-call budget.
|
|
35
|
+
- **Breaking:** Shrink every maintained connector guide to provider-specific facts. Generic paging, `fields`, withheld-value and write-once rules now live in the `cursor`, `page` and `fields` schemas and the `write_outcome_unknown` error; a hatch page that cannot continue adds an incomplete-result warning; and a read-only connection is no longer served its guide's write rules. A `skills` fetch now returns its text once, in `content`, as documented, so a client reading `structuredContent.text` from that fetch must read `content`; the tool advertises no `outputSchema`.
|
|
36
|
+
- **Breaking:** `@zackbart/connecta-eve` now requires exactly Eve `1.0.0-rc.4` as its peer. Update the app to `eve@1.0.0-rc.4`, reinstall, rebuild and test before deploying; helper authoring is unchanged. Eve's own changes reach the app: a deployment from before rc.3 cannot take over sessions rc.3 or later wrote, so plan rollbacks around that; every agent gains a default `edit_file` tool, so gate it wherever `write_file` is gated; and builds now reject `nitro/storage`, `nitro/kv`, `nitro/database` and asset imports that leave the authored package. The helper's README lists them with the release notes.
|
|
37
|
+
- **Breaking:** Answer before MCP clients give up, so an agent gets a typed `timeout` or `write_outcome_unknown` with `hostCalls` and `writes` rather than a transport `The operation timed out`. Claude Code and Codex both give one request 60 s by default. `execute.watchdogMs` now defaults to 55 s (was 120 s), counts from the request's arrival so queueing spends it, and caps direct `call_tool`/`call_destructive_tool` deadlines, including an explicit `timeoutMs` or `calls.defaultTimeoutMs`. `workerExecutor()` now defaults to a 50 s `timeout` (was 60 s) and a 40 s queue deadline (was 60 s); `quickJsExecutor()` keeps 30 s. A run cut short by the ceiling rather than wedged fails as a `timeout` at stage `response ceiling`, retryable unless a write in it landed. Any program error is now non-retryable and drops `retryAfterMs` once a write in the run has landed, because running the program again would repeat it. A run or direct call with no time left is refused unsent, and a call whose time runs out during catalog resolution, admission or auth is refused unsent by a check immediately before dispatch. On Node, the ceiling also counts the request's upload. Storage after a run stops waiting 2 s past the ceiling at most, so a stalled store costs an oversized return its paging, not its answer.
|
|
38
|
+
|
|
39
|
+
Migration: if your clients allow longer requests (`MCP_TOOL_TIMEOUT`, a server's `timeout`, `tool_timeout_sec`) and your programs or direct calls need the time, set `execute.watchdogMs` a margin below that limit, then raise `workerExecutor({ timeout })` or `quickJsExecutor({ timeoutMs })` below it. Construction warns when an executor's deadline is not below `execute.watchdogMs`.
|
|
40
|
+
- End program reads just before the executor's run deadline, so a slow read becomes a catchable, retryable `timeout` and `Promise.allSettled` keeps every result that finished. Executors may publish `deadlineMs`; `quickJsExecutor()` and `workerExecutor()` do, and `execute_code` states it as `<n>s/run`. Writes stay exempt so a drained write's outcome is still learned, and a write that reaches its gate after reads have stopped is refused unsent.
|
|
41
|
+
- Remove the contributor policy limiting Opus agents.
|
|
42
|
+
- A direct read over its inline cap (24,000 bytes unless a connector overrides it) on a surface with `execute_code` can now answer with a sample instead of its first bytes: for rows, the row count, keys and whole first rows as valid JSON in the result's own shape; for text, the line count, head and tail. Its notice says which rows or bytes it holds and that the rest are not shown, and carries `resultId`, the original `totalBytes`, a `nextAction` program over the retained result, and the fallback when that program is refused. A program's value and the paged handle are unchanged. Results at or under the cap return whole, as before. Migration: a client that parses a truncated direct result should not assume the body after the notice is a prefix of the result: `nextOffset` is 0 and the body is a separate view, showing less than first bytes did. Writes, including a source write a deployment overrode to read, value mode, several or non-text blocks, native pools with `executeCode: false`, results with numbers `JSON.parse` would change, and any shape the rule does not cover keep their first bytes. A source write overridden to read now also says its write already ran and is no longer told to repeat the read inside a program. A `fields` miss warning is shorter: `fields/0 matched nothing after filtering, at segment 1; declared there: id, name.`, with at most 160 characters of names, and a later miss at the same place says `same as fields/0`.
|
|
43
|
+
- A program's retained results are now unreadable from other requests until they pass the run's complete secret set. At its end a run waits for its host calls and credential reads to finish within the response's answer-by time, then publishes each entry that passes. A run pays no extra storage, and reads stored bytes back only for entries the set grew past. Stored bytes are never rewritten, so an offset issued during the run stays valid. An entry that matched, or could not be checked or written, stays unreadable: paging it from a later request answers `result_not_retained` with `retryable: false`, a read told to rerun and a write told not to repeat, and the run's response carries a `note` saying so. Paging inside the run is unchanged, and direct-call stashes keep passing their own request's set when stored.
|
|
44
|
+
- `tools/list` now advertises open output schemas for `execute_code`, `search_tools` and `authorize_connector`: required keys and top-level types, with every other field open. Served results are unchanged, and the server still validates each successful one against the full contract before sending it. A client that read the closed schema or `$defs.failure` from `tools/list` finds the failure and handoff shape in `documentation/meta-tools.md`.
|
|
45
|
+
- **Breaking:** A deployment `classification` override to `"read"` no longer applies to a tool whose source explicitly declares a write (`readOnlyHint: false`, `destructiveHint: true`, or contradictory hints). Such a tool stays a write on every surface and is never served read-annotated, because a host may resend a read-annotated call after a connection failure, which duplicated physical writes. Where the source is silent, `"read"` still serves as a read, and setting it asserts the call is safe to repeat.
|
|
46
|
+
|
|
47
|
+
**Migration:** if you overrode a vendor-declared write to `"read"`, an operator warning now names that override once per catalog change. Its calls prompt on hosts that prompt, and a direct `call_tool` is refused with the next step, naming `call_destructive_tool`. Native pools list it as destructive. Read-only endpoints and connectors omit and refuse it, so a read-only pool loses it. Remove the override, or, if you have checked the tool and a repeat is harmless, change it to `"read-repeatable"`. `"write"` and `"hidden"` overrides are unchanged.
|
|
48
|
+
- Shorten the MCP instructions and tool descriptions served to agents. Meta instructions now open with the caller's admitted connector ids; `skills` and `connecta.skill` return a local guide's body without frontmatter; the connector inventory marks required guides with `*` and no longer names optional ones.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Raise the root bundle cap so main passes after two changes that each fit it alone.
|
|
53
|
+
- Stop the required CI gate failing when the jobs API trails completed matrix legs.
|
|
54
|
+
- Keep the required CI gate waiting while the jobs API still reports finished legs as queued.
|
|
55
|
+
- A declined or cancelled URL elicitation now returns the verified connection handoff, so an agent on a client that refuses prompts automatically can give the user a working `/connect` link. The refusal text no longer implies that a person saw the prompt.
|
|
56
|
+
- Stop eval graders failing correct refusals, handoffs and QuickBooks and CCB routes, and passing a handoff link altered inside a Markdown destination; make the QuickBooks journeys passable by following their prompts; harden Worker restarts against port collisions.
|
|
57
|
+
- Stop build hooks rewriting unchanged generated sources, so they cannot reload a smoke under Wrangler, and report a Worker eval reload as an infrastructure error instead of a lost connection or host authorization failure. Freeze one verified Worker graph per eval batch, refusing a batch or comparison whose served sources differ from its label or its other arm.
|
|
58
|
+
- A `force: true` connection flow now resets once across all of its links. URL elicitation `accept` does not mean a browser opened the link, so every link stays live: the first verified visit performs the reset and later visits continue it, without invalidating the first consent. A visit during a running start returns 409 without spending its link and says to start a new connection request if it persists, and a failed start lets the flow's next visit begin again. A declined or cancelled prompt's link now works alongside the refusal's link instead of being retired. An unforced flow's unspent links are no longer retired at completion, and a refusal whose link could not be issued now tells the agent how to get a new one. A link from an earlier version that names a predecessor link is refused and replaced by a new link.
|
|
59
|
+
- **Breaking:** GitHub answers a resource HTTP 403 as `provider_permission_denied` instead of `auth_required`, without a credential handoff, naming the repository and any permission or scope GitHub reports. The message states possible causes without claiming the credential works or a grant is missing. Rate-limit 403s stay `rate_limited`, a 401 stays `auth_required`, and SAML enforcement is stated without its link. A refused token mint reports the typed gap only when GitHub's installation record lacks the requested permission; otherwise a 422 is `connector_call_failed` with vendor text withheld, and a mint 403 keeps its causes open. Repositories or owners outside the App's installation report `provider_permission_denied`. A 403 on the App's own installation list stays `auth_required`. Programs that branched on `auth_required` for these failures must branch on `provider_permission_denied`, and these failures no longer carry a credential handoff.
|
|
60
|
+
- **Breaking:** A native pool's tool name now binds its classification, so a host resending a call it cached as read-only cannot write. Writes are named `<connector>__WRITE__<tool>`, or a hashed form of it; every read keeps the name it had, and a name stays stable until the tool's classification changes. A read name whose tool is now a write is refused before dispatch with `destructive_tool_requires_approval`, and `nextAction.tool` names the write. To migrate, take native names from `tools/list`, and change anything hardcoding a native write's name, such as host approval rules, allowlists, prompts and recipes, to the `__WRITE__` form. A host's cached list needs a refresh before it can call a write. `nativeToolName` now requires a classification. Addresses (`connector.tool`), programs and meta pools are unchanged.
|
|
61
|
+
- Program writes no longer hide how they failed or leave altered. `write_outcome_unknown` now leads with the tool's words when a tool answered `isError`, and carries `unknownWrites` with each write's address, code and redacted message, plus `toolAnswered: true`; the outcome stays unknown and nothing is sent again. A tool's `isError` text now reaches every agent surface up to 4,096 UTF-8 bytes instead of 512, cut only between tokens and marked, so an approval link or request id arrives usable. A program's report is redacted against the run's final credentials before any cut and bounded per message, in total, and by `execute.maxResultChars`. A program write whose arguments hold a credential the request already sent is refused before dispatch with `invalid_args`, rather than sent with `[redacted]` in place of the match, which could first appear on a program's second call. Program arguments no longer match a credential's public prefix or an identifier sent beside it, so reads and writes naming them leave as written. Maintained providers now declare a header public from the vendor's contract, and only Cloudflare's Global API Key `X-Auth-Email` and Overflow's `x-client-id` are declared: the whole value stays withheld from output, but its fragments no longer match, so such a connection's domain survives in results. Every other header and query parameter stays fully protected whatever its name. Connecta's clips of tool errors, program failures and logs now end between tokens. Operator logs name a tool's own failure `DownstreamToolError` rather than `Error`.
|
|
62
|
+
- Keep a QuickJS child alive when a program's memory grows during a job pass. A bug in `quickjs-emscripten-core` 0.32.0 aborted the child while freeing a finished run's runtime, which cost the run its answer, or the next request when a host call was still open. If freeing a runtime ever fails again, the run keeps its answer, a warning is logged and the child is replaced before it can take another run.
|
|
63
|
+
- Stop a hatch's operation vocabulary hiding partial matches from named tools, and rank on what search shows. Search descriptions are now the first complete sentence with `descriptionTruncated: true` when cut, `connecta.describe` returns full text by default within a 32 KiB budget per call, a description's tail can no longer make a tool a complete match, titles are indexed, and a few abbreviations such as `env`/`environment` match. Partial matches score by the squared share of the query's rarity they cover, so rare words in a hatch's description outrank a common verb in another tool's name, and GitHub's hatch operations now count in main search as other hatches' already do.
|
|
64
|
+
- Keep a slow connector's tools in discovery. When a connector does not answer an unscoped search or native `tools/list` in time, its last complete catalog, up to 24 hours past expiry, is listed and reported with `stale: true` and `ageMs` in `catalogErrors`. Calls still resolve the live catalog. An auth or permission refusal lists nothing and retires the catalog of the credential that attempt sent, in every isolate, until a listing begun after the refusal succeeds. A custom connector refused after its credential changed retires all of that connector's catalogs, since the credential it sent cannot be known. A downstream's retry delay is kept in `retryAfterMs`, and `connecta doctor` fails with "Native discovery degraded" when a native pool lists a connector stale. Cached and authored catalogs no longer wait for a probe slot, a timed-out probe refreshes its catalog in the runtime's tail, and a publish deletes the catalog revision it supersedes.
|
|
65
|
+
|
|
5
66
|
## 0.41.0 — 2026-11-03
|
|
6
67
|
|
|
7
68
|
This release fixes a production outage on Workers, tightens auth and network defaults, and makes warm requests cheaper. Three changes can stop an existing deployment at startup or at validation, so check them before upgrading.
|
package/bin/connecta.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { copyFile, cp, lstat, mkdtemp, readFile, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { CONNECTA_VERSION } from "./version.mjs";
|
|
5
|
-
import { META_TOOL_NAMES } from "./meta-tool-names.mjs";
|
|
5
|
+
import { META_TOOL_NAMES, READ_PROGRAM_TOOL } from "./meta-tool-names.mjs";
|
|
6
6
|
import { basename, dirname, join, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
@@ -245,6 +245,8 @@ async function doctor() {
|
|
|
245
245
|
let negotiatedVersion;
|
|
246
246
|
let native = false;
|
|
247
247
|
let executeCode = true;
|
|
248
|
+
let toolCount = META_TOOL_NAMES.length;
|
|
249
|
+
let readRoute = false;
|
|
248
250
|
try {
|
|
249
251
|
await client.connect(transport);
|
|
250
252
|
negotiatedVersion = client.getNegotiatedProtocolVersion();
|
|
@@ -257,15 +259,28 @@ async function doctor() {
|
|
|
257
259
|
throw new Error(
|
|
258
260
|
`Native catalogs unavailable: ${surface.catalogErrors.join(", ")}. Inspect connector status and authentication.`,
|
|
259
261
|
);
|
|
262
|
+
// Tools are listed, from an older catalog, so nothing above notices that the connector did not answer.
|
|
263
|
+
if (native && surface.staleCatalogs?.length)
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Native discovery degraded: ${surface.staleCatalogs.join(", ")} did not answer and listed from an older catalog. Inspect connector status.`,
|
|
266
|
+
);
|
|
267
|
+
// A writable human endpoint also lists the read program route, unless the deployment turned it off.
|
|
268
|
+
// An agent endpoint never does, so seven tools there is a wrong surface.
|
|
269
|
+
readRoute = !native && !agent && actual.includes(READ_PROGRAM_TOOL);
|
|
260
270
|
const expected = native
|
|
261
271
|
? ["authorize_connector", "skills", executeCode ? "execute_code" : "read_result"].sort()
|
|
262
|
-
: [...META_TOOL_NAMES].sort();
|
|
272
|
+
: [...META_TOOL_NAMES, ...(readRoute ? [READ_PROGRAM_TOOL] : [])].sort();
|
|
273
|
+
toolCount = expected.length;
|
|
263
274
|
if (
|
|
264
275
|
native
|
|
265
276
|
? expected.some((name) => !actual.includes(name)) ||
|
|
266
|
-
[
|
|
267
|
-
|
|
268
|
-
|
|
277
|
+
[
|
|
278
|
+
"search_tools",
|
|
279
|
+
"call_tool",
|
|
280
|
+
"call_destructive_tool",
|
|
281
|
+
READ_PROGRAM_TOOL,
|
|
282
|
+
executeCode ? "read_result" : "execute_code",
|
|
283
|
+
].some((name) => actual.includes(name))
|
|
269
284
|
: JSON.stringify(actual) !== JSON.stringify(expected)
|
|
270
285
|
) {
|
|
271
286
|
throw new Error(
|
|
@@ -274,20 +289,14 @@ async function doctor() {
|
|
|
274
289
|
);
|
|
275
290
|
}
|
|
276
291
|
|
|
277
|
-
|
|
278
|
-
const executed = await client.callTool(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
arguments: { code: "async () => 42" },
|
|
282
|
-
},
|
|
283
|
-
undefined,
|
|
284
|
-
{ timeout: DOCTOR_TIMEOUT_MS },
|
|
285
|
-
);
|
|
292
|
+
for (const name of executeCode ? ["execute_code", ...(readRoute ? [READ_PROGRAM_TOOL] : [])] : []) {
|
|
293
|
+
const executed = await client.callTool({ name, arguments: { code: "async () => 42" } }, undefined, {
|
|
294
|
+
timeout: DOCTOR_TIMEOUT_MS,
|
|
295
|
+
});
|
|
286
296
|
const executionResult = executed.structuredContent ?? JSON.parse(executed.content?.[0]?.text ?? "null");
|
|
287
297
|
if (executed.isError || executionResult?.result !== 42) {
|
|
288
298
|
throw new Error(
|
|
289
|
-
`${executorName ? `${executorName} execution` :
|
|
290
|
-
`failed: ${JSON.stringify(executed)}`,
|
|
299
|
+
`${executorName ? `${executorName} execution` : name} check ` + `failed: ${JSON.stringify(executed)}`,
|
|
291
300
|
);
|
|
292
301
|
}
|
|
293
302
|
}
|
|
@@ -298,7 +307,7 @@ async function doctor() {
|
|
|
298
307
|
console.log(
|
|
299
308
|
`Connecta doctor passed: ${health.connectors} connector(s), ` +
|
|
300
309
|
`${executeCode ? (executorName ? `${executorName} executed` : "code executed") : "code disabled"}, ` +
|
|
301
|
-
`${native ? "native tool surface" : `prescribed ${
|
|
310
|
+
`${native ? "native tool surface" : `prescribed ${toolCount}-tool surface`}, MCP ${negotiatedVersion}, package ${CONNECTA_VERSION}.`,
|
|
302
311
|
);
|
|
303
312
|
}
|
|
304
313
|
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
/** The
|
|
1
|
+
/** The upward meta-tool set every meta endpoint lists, shared by discovery checks and doctor. */
|
|
2
2
|
export declare const META_TOOL_NAMES: readonly string[];
|
|
3
|
+
/** A writable `/mcp` or `/mcp/<pool>` endpoint also lists this, unless `execute.readRoute` is false. */
|
|
4
|
+
export declare const READ_PROGRAM_TOOL: string;
|
package/bin/meta-tool-names.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** The
|
|
1
|
+
/** The upward meta-tool set every meta endpoint lists, shared by discovery checks and doctor. */
|
|
2
2
|
export const META_TOOL_NAMES = Object.freeze([
|
|
3
3
|
"authorize_connector",
|
|
4
4
|
"call_destructive_tool",
|
|
@@ -7,3 +7,6 @@ export const META_TOOL_NAMES = Object.freeze([
|
|
|
7
7
|
"search_tools",
|
|
8
8
|
"skills",
|
|
9
9
|
]);
|
|
10
|
+
|
|
11
|
+
/** A writable `/mcp` or `/mcp/<pool>` endpoint also lists this, unless `execute.readRoute` is false. */
|
|
12
|
+
export const READ_PROGRAM_TOOL = "execute_read_code";
|
package/bin/version.mjs
CHANGED
package/dist/activity.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Logger } from "./types.js";
|
|
|
5
5
|
export declare function activityClientFact(value: unknown, field: "name" | "version"): string | undefined;
|
|
6
6
|
/** Stored package versions use a bounded release-version grammar, including prereleases. */
|
|
7
7
|
export declare function activityPackageVersion(value: unknown): string | undefined;
|
|
8
|
-
export type ActivityCallSource = "catalog_refresh" | "native" | "call_tool" | "call_destructive_tool" | "batch_call" | "execute_code" | "resume_execution";
|
|
8
|
+
export type ActivityCallSource = "catalog_refresh" | "native" | "call_tool" | "call_destructive_tool" | "batch_call" | "execute_code" | "execute_read_code" | "resume_execution";
|
|
9
9
|
export type ActivityOutcome = "success" | "error" | "timeout" | "cancelled" | "paused" | "approved";
|
|
10
10
|
/** Read-only history: how much one `resume_execution` approved (#672). */
|
|
11
11
|
export type ActivityApproval = "call" | "tool";
|
|
@@ -230,6 +230,7 @@ export declare function activityHistory(options: ActivityHistoryOptions): Activi
|
|
|
230
230
|
/** Operation records reuse the detached sink and accept no payload fields. */
|
|
231
231
|
export declare function recordOperationActivity(context: ActivityRequestContext, input: {
|
|
232
232
|
operation: ActivityOperation;
|
|
233
|
+
source?: "execute_read_code" | undefined;
|
|
233
234
|
durationMs: number;
|
|
234
235
|
resultBytes?: number | undefined;
|
|
235
236
|
outcome: ActivityOutcome;
|
package/dist/activity.js
CHANGED
|
@@ -2,7 +2,7 @@ import { checkedActivityOperation } from "./operation-activity.js";
|
|
|
2
2
|
import { CONNECTA_VERSION } from "./version.js";
|
|
3
3
|
import { assertKnownOptions, keys, optionsOf } from "./config-schema.js";
|
|
4
4
|
import { routeActivity } from "./routes/activity.js";
|
|
5
|
-
import {
|
|
5
|
+
import { clampedRecordName, classificationCode } from "./errors.js";
|
|
6
6
|
import { failureRecord, logFailure } from "./operator-record.js";
|
|
7
7
|
import { keepErrorName } from "./redaction-identity.js";
|
|
8
8
|
/**
|
|
@@ -123,9 +123,9 @@ export function recordToolActivity(context, input) {
|
|
|
123
123
|
pool: context.pool,
|
|
124
124
|
actorBasis: input.privateCatalog || (input.personal && context.principalActor) ? "principal" : undefined,
|
|
125
125
|
}),
|
|
126
|
-
connectorId:
|
|
127
|
-
toolName:
|
|
128
|
-
address:
|
|
126
|
+
connectorId: clampedRecordName(input.connectorId, MAX_ACTIVITY_NAME_BYTES),
|
|
127
|
+
toolName: clampedRecordName(input.toolName, MAX_ACTIVITY_NAME_BYTES),
|
|
128
|
+
address: clampedRecordName(input.address, MAX_ACTIVITY_ADDRESS_BYTES),
|
|
129
129
|
source: input.source,
|
|
130
130
|
outcome: input.outcome,
|
|
131
131
|
durationMs: Math.max(0, Math.trunc(input.durationMs)),
|
|
@@ -206,7 +206,7 @@ export function recordOperationActivity(context, input) {
|
|
|
206
206
|
connectorId,
|
|
207
207
|
toolName: operation.name,
|
|
208
208
|
address: `${connectorId}.${operation.name}`,
|
|
209
|
-
source: "execute_code",
|
|
209
|
+
source: input.source ?? "execute_code",
|
|
210
210
|
outcome: input.outcome,
|
|
211
211
|
durationMs: input.durationMs,
|
|
212
212
|
...(input.resultBytes !== undefined ? { resultBytes: input.resultBytes } : {}),
|
|
@@ -51,3 +51,18 @@ export function advertisedSchema(schema) {
|
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* An output schema that advertises less than it enforces. The SDK validates each
|
|
56
|
+
* non-error result through `~standard.validate`, so the server keeps the full contract and
|
|
57
|
+
* a client's copy states only what it can use: required keys and top-level types,
|
|
58
|
+
* with every other field open.
|
|
59
|
+
*/
|
|
60
|
+
export function openOutputSchema(validator, advertised) {
|
|
61
|
+
const json = deepFreeze(advertised);
|
|
62
|
+
return {
|
|
63
|
+
"~standard": {
|
|
64
|
+
...validator["~standard"],
|
|
65
|
+
jsonSchema: { input: () => json, output: () => json },
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
package/dist/agent-routes.js
CHANGED
|
@@ -49,11 +49,25 @@ const nativeAuthorize = {
|
|
|
49
49
|
authorizeCall: () => "Give this error's nextAction to the user or operator.",
|
|
50
50
|
authorizeAgain: "retry the original native call",
|
|
51
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* An auth error's message, once that error carries what authorize_connector
|
|
54
|
+
* would return: the fixed route text stops sending the agent to fetch it again.
|
|
55
|
+
* Only Connecta's own phrases change; provider wording around them stays.
|
|
56
|
+
*/
|
|
57
|
+
export function handoffInHand(message, connector) {
|
|
58
|
+
return message
|
|
59
|
+
.replaceAll(metaAuthorize.authorizeAndOpen(connector), nativeAuthorize.authorizeAndOpen())
|
|
60
|
+
.replaceAll(metaAuthorize.authorizeAndFollow(connector), nativeAuthorize.authorizeAndFollow())
|
|
61
|
+
.replaceAll(metaAuthorize.authorizeCall(connector), nativeAuthorize.authorizeCall())
|
|
62
|
+
.replaceAll(metaAuthorize.authorizeCall(), nativeAuthorize.authorizeCall())
|
|
63
|
+
.replaceAll(metaAuthorize.authorizeFor("recovery options"), nativeAuthorize.authorizeFor())
|
|
64
|
+
.replaceAll(metaAuthorize.authorizeFor("this connector"), nativeAuthorize.authorizeFor());
|
|
65
|
+
}
|
|
52
66
|
const nativeDirect = {
|
|
53
67
|
directOnly: "when calling this tool directly",
|
|
54
68
|
forDirect: "when calling this tool directly",
|
|
55
69
|
onlyDirectRead: "only when calling this tool directly",
|
|
56
|
-
writeNeedsApproval: "Invoke it through its own
|
|
70
|
+
writeNeedsApproval: "This name is bound to a read, so nothing was sent. Invoke it through its own write tool so the MCP host can request approval.",
|
|
57
71
|
};
|
|
58
72
|
const PHRASES = {
|
|
59
73
|
meta: Object.freeze({
|
|
@@ -65,6 +79,15 @@ const PHRASES = {
|
|
|
65
79
|
writeNeedsApproval: "Invoke it through call_destructive_tool so the MCP host can request approval.",
|
|
66
80
|
...metaAuthorize,
|
|
67
81
|
}),
|
|
82
|
+
"read-route": Object.freeze({
|
|
83
|
+
program: "execute_read_code",
|
|
84
|
+
directOnly: "for a direct call_tool only",
|
|
85
|
+
forDirect: "for a direct call_tool",
|
|
86
|
+
onlyDirectRead: "only for a direct call_tool read",
|
|
87
|
+
callDirectlyAndPage: "call directly (call_tool, connecta.result to page)",
|
|
88
|
+
writeNeedsApproval: "Invoke it through call_destructive_tool so the MCP host can request approval.",
|
|
89
|
+
...metaAuthorize,
|
|
90
|
+
}),
|
|
68
91
|
// The eval code arm keeps the text its comparator protocol recorded.
|
|
69
92
|
code: Object.freeze({
|
|
70
93
|
program: "execute_code",
|
|
@@ -88,7 +111,10 @@ const PHRASES = {
|
|
|
88
111
|
...nativeAuthorize,
|
|
89
112
|
}),
|
|
90
113
|
};
|
|
91
|
-
const SURFACES = new Map(["meta", "code", "native", "native-code"].map((surface) => [
|
|
114
|
+
const SURFACES = new Map(["meta", "read-route", "code", "native", "native-code"].map((surface) => [
|
|
115
|
+
routeNames(surface),
|
|
116
|
+
surface,
|
|
117
|
+
]));
|
|
92
118
|
/** The surface a names table belongs to; meta for a table Connecta did not build. */
|
|
93
119
|
function routeSurface(routes) {
|
|
94
120
|
return (routes && SURFACES.get(routes)) ?? "meta";
|
|
@@ -185,10 +211,10 @@ const programRouteConfigs = new WeakMap();
|
|
|
185
211
|
* No package entry exports it and no configuration key selects it; the eval
|
|
186
212
|
* code arm marks the configuration object it passes to createConnecta.
|
|
187
213
|
*/
|
|
188
|
-
export function renderRoutesAsPrograms(config) {
|
|
189
|
-
programRouteConfigs.set(config,
|
|
214
|
+
export function renderRoutesAsPrograms(config, searchOnly = false) {
|
|
215
|
+
programRouteConfigs.set(config, searchOnly ? { ...META_ROUTES, search: routeNames("code").search } : routeNames("code"));
|
|
190
216
|
}
|
|
191
217
|
/** The names every reply of a deployment built from `config` uses outside native pools. */
|
|
192
218
|
export function deploymentRoutes(config) {
|
|
193
|
-
return programRouteConfigs.
|
|
219
|
+
return programRouteConfigs.get(config) ?? META_ROUTES;
|
|
194
220
|
}
|
|
@@ -188,8 +188,11 @@ export class OAuthRefreshCoordinator {
|
|
|
188
188
|
// Production adapters resolve the required host policy before a code or
|
|
189
189
|
// refresh claim can consume its single-use credential. Custom SDK leaves
|
|
190
190
|
// may supply their own policy; this hook performs no I/O and owns no data.
|
|
191
|
+
// Each also registers the request's credentials before this point, as `api()`
|
|
192
|
+
// does in this hook: a refresh outlives its caller once claimed, and a sealing
|
|
193
|
+
// run must not publish ahead of that. `remoteMcp()` does so around its SDK flows.
|
|
191
194
|
if (isOAuthCredentialRequest(init))
|
|
192
|
-
prepareCredentialRequest?.();
|
|
195
|
+
prepareCredentialRequest?.(input, init);
|
|
193
196
|
if (isRefreshTokenRequest(init)) {
|
|
194
197
|
const signal = requestSignal && init?.signal
|
|
195
198
|
? AbortSignal.any([requestSignal, init.signal])
|
|
@@ -105,6 +105,8 @@ export class KvOAuthProvider {
|
|
|
105
105
|
* only: no credential beyond the access token it already handed out.
|
|
106
106
|
*/
|
|
107
107
|
observed;
|
|
108
|
+
/** The same grant's tokens, which a catalog's identity digests. Memory only, like the grant they came from. */
|
|
109
|
+
observedTokens;
|
|
108
110
|
constructor(connectorId, storage, redirectUri, refreshCoordinator, allowAuthorization = true,
|
|
109
111
|
/** Present when the deployment's vault can seal; the grant is then ciphertext. */
|
|
110
112
|
sealer,
|
|
@@ -153,6 +155,7 @@ export class KvOAuthProvider {
|
|
|
153
155
|
}
|
|
154
156
|
observe(grant) {
|
|
155
157
|
const { tokens, expiresAt } = grant.body;
|
|
158
|
+
this.observedTokens = tokens;
|
|
156
159
|
this.observed = tokens
|
|
157
160
|
? {
|
|
158
161
|
epoch: grant.epoch,
|
|
@@ -397,25 +397,31 @@ export function staticOAuth(id, config) {
|
|
|
397
397
|
return new StaticOAuthProvider(id, ctx, coordinatorFor(ctx), settings, requestOwned);
|
|
398
398
|
};
|
|
399
399
|
/**
|
|
400
|
-
* The
|
|
401
|
-
*
|
|
402
|
-
* from configuration the token endpoint is
|
|
403
|
-
* the check keeps the headers on it even if
|
|
400
|
+
* The token request as sent, with the configured token-request headers laid
|
|
401
|
+
* over the SDK's own, and every credential it carries registered. With
|
|
402
|
+
* discovery and registration answered from configuration the token endpoint is
|
|
403
|
+
* the only URL the SDK fetches here; the check keeps the headers on it even if
|
|
404
|
+
* that ever changes.
|
|
404
405
|
*/
|
|
406
|
+
const registeredTokenRequest = (secrets, input, init = {}) => {
|
|
407
|
+
const url = new URL(input);
|
|
408
|
+
const request = { ...init, redirect: "manual" };
|
|
409
|
+
if (settings.tokenRequestHeaders.length && `${url.origin}${url.pathname}` === settings.identity) {
|
|
410
|
+
const headers = new Headers(init.headers);
|
|
411
|
+
for (const [name, value] of settings.tokenRequestHeaders)
|
|
412
|
+
headers.set(name, value);
|
|
413
|
+
for (const [name, value] of settings.tokenRequestHeaders)
|
|
414
|
+
if (!PUBLIC_TOKEN_REQUEST_HEADERS.get(name.toLowerCase())?.has(value))
|
|
415
|
+
secrets.header(value);
|
|
416
|
+
request.headers = headers;
|
|
417
|
+
}
|
|
418
|
+
trackRemoteClientRequest(secrets, input, request);
|
|
419
|
+
return request;
|
|
420
|
+
};
|
|
421
|
+
/** The owned credential transport. */
|
|
405
422
|
const tokenEndpointFetch = authMethod((secrets, input, init = {}) => {
|
|
406
423
|
return authFlow(function* () {
|
|
407
|
-
const
|
|
408
|
-
const request = { ...init, redirect: "manual" };
|
|
409
|
-
if (settings.tokenRequestHeaders.length && `${url.origin}${url.pathname}` === settings.identity) {
|
|
410
|
-
const headers = new Headers(init.headers);
|
|
411
|
-
for (const [name, value] of settings.tokenRequestHeaders)
|
|
412
|
-
headers.set(name, value);
|
|
413
|
-
for (const [name, value] of settings.tokenRequestHeaders)
|
|
414
|
-
if (!PUBLIC_TOKEN_REQUEST_HEADERS.get(name.toLowerCase())?.has(value))
|
|
415
|
-
secrets.header(value);
|
|
416
|
-
request.headers = headers;
|
|
417
|
-
}
|
|
418
|
-
trackRemoteClientRequest(secrets, input, request);
|
|
424
|
+
const request = registeredTokenRequest(secrets, input, init);
|
|
419
425
|
// The coordinator or code-exchange wrapper authorizes this send. Refresh
|
|
420
426
|
// claim submission may outlive the caller; this transport uses no
|
|
421
427
|
// request-owned capability and changes no closed resource-call ledger.
|
|
@@ -425,7 +431,6 @@ export function staticOAuth(id, config) {
|
|
|
425
431
|
provider: id,
|
|
426
432
|
method: request.method ?? "GET",
|
|
427
433
|
};
|
|
428
|
-
secrets.request(input, request);
|
|
429
434
|
const response = (yield authCall(sendCredentialPost, [
|
|
430
435
|
input,
|
|
431
436
|
request,
|
|
@@ -446,6 +451,7 @@ export function staticOAuth(id, config) {
|
|
|
446
451
|
early) => {
|
|
447
452
|
return authFlow(function* () {
|
|
448
453
|
const secrets = sentSecretsFor(ctx);
|
|
454
|
+
const owner = viewSignal(ctx, requestOwned || exchange !== undefined);
|
|
449
455
|
const coordinated = coordinatorFor(ctx).coordinatedFetch(provider, authMethod((input, init) => {
|
|
450
456
|
return authFlow(function* () {
|
|
451
457
|
if (exchange) {
|
|
@@ -478,8 +484,10 @@ export function staticOAuth(id, config) {
|
|
|
478
484
|
}
|
|
479
485
|
return (yield authCall(tokenEndpointFetch, [secrets, input, init]));
|
|
480
486
|
});
|
|
481
|
-
}),
|
|
487
|
+
}), owner, ctx.defer, undefined, (input, init) => {
|
|
482
488
|
guardedTransport(true);
|
|
489
|
+
if (!owner.aborted)
|
|
490
|
+
registeredTokenRequest(secrets, input, init);
|
|
483
491
|
});
|
|
484
492
|
return (yield authCall(auth, [
|
|
485
493
|
provider,
|