@zackbart/connecta 0.34.1 → 0.35.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 +363 -0
- package/README.md +62 -106
- package/SECURITY.md +3 -2
- package/bin/connecta.mjs +41 -16
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +2 -1
- package/dist/access-tokens.js +66 -30
- package/dist/activity.d.ts +1 -1
- package/dist/activity.js +2 -0
- package/dist/agent-config.d.ts +23 -0
- package/dist/agent-config.js +59 -0
- package/dist/agent-description.js +16 -0
- package/dist/agent-identity.js +4 -4
- package/dist/agent-observation.js +39 -0
- package/dist/auth/clerk-sdk/client.d.ts +2 -0
- package/dist/auth/clerk-sdk/generated.js +554 -563
- package/dist/auth/clerk-transport.js +70 -3
- package/dist/auth/clerk.d.ts +13 -0
- package/dist/auth/clerk.js +653 -246
- package/dist/auth/cloudflare-access.js +46 -42
- package/dist/auth/downstream-client-metadata.js +2 -2
- package/dist/auth/downstream-oauth.js +2051 -1160
- package/dist/auth/downstream-revocation.js +88 -88
- package/dist/auth/native-call.js +20 -0
- package/dist/auth/native.js +184 -0
- package/dist/auth/oauth-start-reset.js +11 -5
- package/dist/auth/oauth-token-response.js +121 -102
- package/dist/auth/oauth-v2-migration.js +97 -78
- package/dist/auth/renewable-grant.js +10 -0
- package/dist/auth/static-oauth.js +663 -271
- package/dist/auth/vercel-agent.js +266 -0
- package/dist/auth-admission.js +204 -0
- package/dist/auth-elicitation.js +350 -241
- package/dist/authorization-handoff.js +164 -90
- package/dist/call-admission-context.js +3 -10
- package/dist/call-admission.js +2 -0
- package/dist/call-classification.js +9 -0
- package/dist/call-recovery.js +32 -13
- package/dist/canonical-json.js +8 -0
- package/dist/catalog-cache.js +369 -213
- package/dist/catalog-service.js +659 -530
- package/dist/catalog.js +5 -1
- package/dist/concurrency.js +0 -25
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +3 -1
- package/dist/config-schema.js +13 -3
- package/dist/config.d.ts +40 -6
- package/dist/config.js +40 -26
- package/dist/connector-caller.d.ts +4 -0
- package/dist/connector-caller.js +17 -4
- package/dist/connectors/api-connector.d.ts +12 -7
- package/dist/connectors/api-connector.js +142 -56
- package/dist/connectors/guarded-fetch.d.ts +51 -17
- package/dist/connectors/guarded-fetch.js +215 -121
- package/dist/connectors/negotiation-cache.js +20 -10
- package/dist/connectors/remote-mcp.d.ts +10 -30
- package/dist/connectors/remote-mcp.js +955 -563
- package/dist/d1.js +10 -243
- package/dist/derived-context.js +5 -0
- package/dist/describe-config.d.ts +3 -1
- package/dist/describe-config.js +2 -0
- package/dist/downstream-input.js +347 -260
- package/dist/errors.d.ts +87 -6
- package/dist/errors.js +114 -4
- package/dist/execute.js +87 -50
- package/dist/executor-admission.js +3 -0
- package/dist/executors/quickjs.js +163 -112
- package/dist/guarded-transport.js +50 -0
- package/dist/guest-runtime.js +1 -1
- package/dist/http/client.js +439 -0
- package/dist/http/errors.js +558 -0
- package/dist/http/fields.js +49 -0
- package/dist/http/hatch-paging.js +318 -0
- package/dist/http/hatch.js +1279 -0
- package/dist/http/json-schema.js +267 -0
- package/dist/http/node-transport.js +241 -0
- package/dist/http/oauth.js +162 -0
- package/dist/http/paging.js +228 -0
- package/dist/http/provider.js +221 -0
- package/dist/http/safety.js +82 -0
- package/dist/http/services.js +79 -0
- package/dist/http/tools.js +383 -0
- package/dist/http/vendor-index.js +138 -0
- package/dist/http/vendor.js +448 -0
- package/dist/http/write-request.js +66 -0
- package/dist/http-policy.d.ts +11 -0
- package/dist/http-policy.js +45 -0
- package/dist/inbound-credential.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -0
- package/dist/invocation-auth.js +30 -19
- package/dist/invocation.js +218 -85
- package/dist/mcp-result.js +0 -4
- package/dist/membership.d.ts +4 -0
- package/dist/meta-output.js +1 -1
- package/dist/meta-tools.js +531 -406
- package/dist/native-recovery.js +52 -0
- package/dist/native-tools.js +382 -0
- package/dist/node.d.ts +4 -3
- package/dist/node.js +45 -8
- package/dist/oauth-handoff.js +177 -104
- package/dist/oauth-partition.js +6 -20
- package/dist/oauth-sealing.js +6 -7
- package/dist/operation-vocabulary.js +101 -0
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +17 -4
- package/dist/operator-ui/agents.d.ts +5 -4
- package/dist/operator-ui/agents.js +17 -7
- package/dist/operator-ui/config-summary.js +5 -4
- package/dist/operator-ui/connection-map.js +7 -5
- package/dist/operator-ui/contract.d.ts +7 -3
- package/dist/operator-ui/endpoints.js +4 -13
- package/dist/operator-ui/fix-prompts.d.ts +1 -1
- package/dist/operator-ui/fix-prompts.js +0 -8
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/model.d.ts +2 -3
- package/dist/operator-ui/overview.js +5 -2
- package/dist/operator-ui/setup-commands.js +0 -4
- package/dist/operator-ui/view.d.ts +2 -25
- package/dist/operator-ui/view.js +2 -76
- package/dist/program-writes.js +47 -26
- package/dist/provider-call-facts.d.ts +115 -0
- package/dist/provider-call-facts.js +182 -0
- package/dist/provider-call.d.ts +112 -0
- package/dist/provider-call.js +271 -0
- package/dist/provider.d.ts +65 -31
- package/dist/provider.js +116 -39
- package/dist/providers/_shared/google/hatch.js +91 -0
- package/dist/providers/_shared/google/kit.js +537 -0
- package/dist/providers/_shared/google/result-size.js +15 -2
- package/dist/providers/_shared/google/service-account.d.ts +2 -1
- package/dist/providers/_shared/google/service-account.js +8 -4
- package/dist/providers/_shared/google/workspace.d.ts +6 -210
- package/dist/providers/_shared/google/workspace.js +6 -425
- package/dist/providers/_shared/rest/compact-index.js +86 -0
- package/dist/providers/_shared/rest/dispatch.js +3 -23
- package/dist/providers/_shared/rest/kit.js +546 -0
- package/dist/providers/_shared/rest/operation-index.js +230 -14
- package/dist/providers/_shared/rest/response-media.js +11 -0
- package/dist/providers/_shared/rest/tools.js +101 -78
- package/dist/providers/_shared/rest/value-safety.js +353 -195
- package/dist/providers/_shared/sensitive-fields.js +27 -0
- package/dist/providers/breeze/api.js +584 -0
- package/dist/providers/breeze/index.d.ts +1 -3
- package/dist/providers/breeze/index.js +67 -1420
- package/dist/providers/breeze/openapi.generated.js +60 -0
- package/dist/providers/breeze/skill.generated.js +2 -6
- package/dist/providers/breeze/tools.js +696 -0
- package/dist/providers/ccb/api.js +496 -0
- package/dist/providers/ccb/index.d.ts +15 -37
- package/dist/providers/ccb/index.js +77 -1702
- package/dist/providers/ccb/lib/constants.d.ts +34 -0
- package/dist/providers/ccb/lib/constants.js +74 -0
- package/dist/providers/ccb/lib/guide.js +9 -0
- package/dist/providers/ccb/lib/verdicts.js +387 -0
- package/dist/providers/ccb/openapi.generated.js +397 -0
- package/dist/providers/ccb/safety.js +295 -0
- package/dist/providers/ccb/skill.generated.js +2 -6
- package/dist/providers/ccb/tools.js +853 -0
- package/dist/providers/cloudflare/api.js +1066 -0
- package/dist/providers/cloudflare/index.d.ts +5 -22
- package/dist/providers/cloudflare/index.js +79 -197
- package/dist/providers/cloudflare/lib/config.d.ts +16 -0
- package/dist/providers/cloudflare/lib/config.js +2 -0
- package/dist/providers/cloudflare/lib/discovery.js +29 -0
- package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
- package/dist/providers/cloudflare/lib/hatch.js +103 -0
- package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
- package/dist/providers/cloudflare/lib/response-media.js +75 -0
- package/dist/providers/cloudflare/openapi.generated.js +4 -3
- package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -10
- package/dist/providers/cloudflare/tools.js +360 -0
- package/dist/providers/docs/api.js +51 -0
- package/dist/providers/docs/index.d.ts +1 -12
- package/dist/providers/docs/index.js +53 -1165
- package/dist/providers/docs/lib/constants.d.ts +18 -0
- package/dist/providers/docs/lib/constants.js +18 -0
- package/dist/providers/docs/lib/discovery.js +26 -0
- package/dist/providers/docs/lib/requests.js +48 -0
- package/dist/providers/docs/lib/schemas.js +168 -0
- package/dist/providers/docs/openapi.generated.js +16 -0
- package/dist/providers/docs/safety.js +54 -0
- package/dist/providers/docs/skill.generated.js +2 -6
- package/dist/providers/docs/tools.js +802 -0
- package/dist/providers/drive/api.js +177 -0
- package/dist/providers/drive/index.d.ts +1 -12
- package/dist/providers/drive/index.js +38 -1448
- package/dist/providers/drive/lib/constants.d.ts +6 -0
- package/dist/providers/drive/lib/constants.js +6 -0
- package/dist/providers/drive/lib/contracts.js +532 -0
- package/dist/providers/drive/lib/hatch.js +197 -0
- package/dist/providers/drive/openapi.generated.js +77 -0
- package/dist/providers/drive/safety.js +90 -0
- package/dist/providers/drive/skill.generated.js +2 -6
- package/dist/providers/drive/tools.js +1290 -0
- package/dist/providers/forms/api.js +71 -0
- package/dist/providers/forms/index.d.ts +1 -12
- package/dist/providers/forms/index.js +49 -1145
- package/dist/providers/forms/lib/constants.d.ts +14 -0
- package/dist/providers/forms/lib/constants.js +19 -0
- package/dist/providers/forms/lib/hatch.js +72 -0
- package/dist/providers/forms/openapi.generated.js +23 -0
- package/dist/providers/forms/safety.js +34 -0
- package/dist/providers/forms/skill.generated.js +2 -8
- package/dist/providers/forms/tools.js +1024 -0
- package/dist/providers/github/api.js +150 -0
- package/dist/providers/github/index.d.ts +4 -5
- package/dist/providers/github/index.js +30 -87
- package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
- package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
- package/dist/providers/github/lib/discovery.js +132 -0
- package/dist/providers/github/lib/operations.js +58 -0
- package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
- package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
- package/dist/providers/github/openapi.generated.js +1216 -0
- package/dist/providers/github/safety.js +216 -0
- package/dist/providers/github/skill.generated.js +2 -5
- package/dist/providers/github/tools.js +506 -0
- package/dist/providers/gmail/api.js +114 -0
- package/dist/providers/gmail/index.d.ts +1 -12
- package/dist/providers/gmail/index.js +73 -1621
- package/dist/providers/gmail/lib/constants.d.ts +13 -0
- package/dist/providers/gmail/lib/constants.js +16 -0
- package/dist/providers/gmail/lib/index.js +173 -0
- package/dist/providers/gmail/lib/schemas.js +176 -0
- package/dist/providers/gmail/lib/threading.js +120 -0
- package/dist/providers/gmail/openapi.generated.js +92 -0
- package/dist/providers/gmail/safety.js +21 -0
- package/dist/providers/gmail/skill.generated.js +2 -6
- package/dist/providers/gmail/tools.js +1435 -0
- package/dist/providers/overflow/api.js +1008 -0
- package/dist/providers/overflow/index.d.ts +1 -17
- package/dist/providers/overflow/index.js +60 -1488
- package/dist/providers/overflow/skill.generated.js +2 -5
- package/dist/providers/overflow/tools.js +873 -0
- package/dist/providers/planning-center/api.js +467 -0
- package/dist/providers/planning-center/index.d.ts +4 -35
- package/dist/providers/planning-center/index.js +69 -2445
- package/dist/providers/planning-center/lib/constants.d.ts +58 -0
- package/dist/providers/planning-center/lib/constants.js +83 -0
- package/dist/providers/planning-center/lib/discovery.js +40 -0
- package/dist/providers/planning-center/lib/guide.js +39 -0
- package/dist/providers/planning-center/lib/json-api.js +83 -0
- package/dist/providers/planning-center/lib/verdicts.js +1758 -0
- package/dist/providers/planning-center/openapi.generated.js +1764 -0
- package/dist/providers/planning-center/safety.js +188 -0
- package/dist/providers/planning-center/skill.generated.js +2 -27
- package/dist/providers/planning-center/tools.js +1010 -0
- package/dist/providers/sheets/api.js +133 -0
- package/dist/providers/sheets/index.d.ts +4 -16
- package/dist/providers/sheets/index.js +61 -1172
- package/dist/providers/sheets/lib/constants.d.ts +14 -0
- package/dist/providers/sheets/lib/constants.js +14 -0
- package/dist/providers/sheets/lib/hatch.js +163 -0
- package/dist/providers/sheets/lib/requests.js +105 -0
- package/dist/providers/sheets/lib/schemas.js +138 -0
- package/dist/providers/sheets/openapi.generated.js +30 -0
- package/dist/providers/sheets/safety.js +27 -0
- package/dist/providers/sheets/skill.generated.js +2 -7
- package/dist/providers/sheets/tools.js +734 -0
- package/dist/providers/slides/api.js +147 -0
- package/dist/providers/slides/index.d.ts +2 -10
- package/dist/providers/slides/index.js +48 -2555
- package/dist/providers/slides/lib/constants.d.ts +11 -0
- package/dist/providers/slides/lib/constants.js +11 -0
- package/dist/providers/slides/lib/requests.js +60 -0
- package/dist/providers/slides/lib/schemas.js +565 -0
- package/dist/providers/slides/openapi.generated.js +18 -0
- package/dist/providers/slides/skill.generated.js +2 -6
- package/dist/providers/slides/tools.js +1968 -0
- package/dist/providers/stripe/api.js +401 -0
- package/dist/providers/stripe/index.d.ts +5 -27
- package/dist/providers/stripe/index.js +74 -210
- package/dist/providers/stripe/lib/config.d.ts +4 -0
- package/dist/providers/stripe/lib/config.js +2 -0
- package/dist/providers/stripe/openapi.generated.js +4 -3
- package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
- package/dist/providers/stripe/skill.generated.js +2 -11
- package/dist/providers/stripe/tools.js +77 -0
- package/dist/providers/tithely/api.js +531 -0
- package/dist/providers/tithely/index.d.ts +17 -0
- package/dist/providers/tithely/index.js +86 -965
- package/dist/providers/tithely/skill.generated.js +2 -5
- package/dist/providers/tithely/tools.js +426 -0
- package/dist/providers/vercel/api.js +551 -0
- package/dist/providers/vercel/index.d.ts +9 -25
- package/dist/providers/vercel/index.js +66 -260
- package/dist/providers/vercel/lib/config.d.ts +2 -0
- package/dist/providers/vercel/lib/config.js +2 -0
- package/dist/providers/vercel/lib/index.js +117 -0
- package/dist/providers/vercel/lib/logs.js +100 -0
- package/dist/providers/vercel/lib/pin.js +270 -0
- package/dist/providers/vercel/openapi.generated.js +10 -5
- package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
- package/dist/providers/vercel/skill.generated.js +2 -7
- package/dist/providers/vercel/tools.js +642 -0
- package/dist/redaction-identity.js +47 -0
- package/dist/redaction-marker.js +125 -0
- package/dist/registry.d.ts +21 -15
- package/dist/registry.js +409 -376
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/actor-labels.js +1 -1
- package/dist/routes/connect.js +1 -1
- package/dist/routes/dispatch.js +103 -0
- package/dist/routes/mcp.js +170 -79
- package/dist/routes/oauth.js +3 -1
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +5 -4
- package/dist/routes/shared.js +244 -177
- package/dist/routes/skills.js +10 -8
- package/dist/routes/ui-config.js +27 -3
- package/dist/routes/ui-data.js +21 -20
- package/dist/routes/ui.js +1 -1
- package/dist/runtime/call-admission.js +0 -26
- package/dist/runtime/call-entry.js +30 -0
- package/dist/runtime/call.js +556 -0
- package/dist/runtime/connector-scope.js +7 -0
- package/dist/runtime/credential-probe.js +34 -0
- package/dist/runtime/executor.js +40 -0
- package/dist/runtime/native-storage.js +63 -0
- package/dist/runtime/registry.js +27 -0
- package/dist/runtime/request-scope.js +72 -0
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/shared-read.js +34 -5
- package/dist/runtime/storage.js +34 -21
- package/dist/sent-secrets.d.ts +194 -37
- package/dist/sent-secrets.js +1743 -340
- package/dist/server.js +5 -56
- package/dist/skills.js +322 -222
- package/dist/sqlite.js +15 -14
- package/dist/storage/d1-copy.js +255 -0
- package/dist/storage/d1-driver.js +15 -0
- package/dist/storage/keys.js +28 -0
- package/dist/storage/sql-driver.js +88 -0
- package/dist/storage/sql-native.js +544 -0
- package/dist/storage/sql-schema.d.ts +6 -0
- package/dist/storage/sql-schema.js +15 -0
- package/dist/storage/sql.d.ts +14 -20
- package/dist/storage/sql.js +29 -535
- package/dist/timeout.js +0 -26
- package/dist/tool-safety.js +20 -10
- package/dist/types.d.ts +7 -109
- package/dist/upward.js +3 -0
- package/dist/usage-guide.js +3 -1
- package/dist/validate.d.ts +4 -2
- package/dist/validate.js +86 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +312 -292
- package/dist/workspace-subject.js +5 -1
- package/dist/write-outcome.js +454 -34
- package/documentation/README.md +27 -7
- package/documentation/architecture.md +2547 -226
- package/documentation/auth.md +821 -98
- package/documentation/code-mode.md +49 -16
- package/documentation/deploying.md +164 -20
- package/documentation/hosted-mcp-migration.md +460 -0
- package/documentation/integrating.md +129 -144
- package/documentation/meta-tools.md +409 -32
- package/documentation/operating.md +27 -3
- package/documentation/operator-tests.md +3 -0
- package/documentation/operator-ui.md +68 -45
- package/documentation/provider-auth-migration.md +16 -63
- package/documentation/provider-migration-0.29.md +10 -5
- package/documentation/provider-migration-0.35.md +975 -0
- package/documentation/providers.md +965 -0
- package/examples/worker/AGENTS.md +23 -9
- package/examples/worker/README.md +254 -135
- package/examples/worker/alchemy.run.ts +116 -0
- package/examples/worker/gitignore +10 -0
- package/examples/worker/src/connecta.config.ts +23 -21
- package/examples/worker/wrangler.jsonc +14 -5
- package/guides/connecting-agents.md +157 -0
- package/guides/how-it-works.md +156 -0
- package/guides/safety-and-trust.md +153 -0
- package/package.json +27 -46
- package/templates/node/.env.example +3 -0
- package/templates/node/README.md +22 -2
- package/templates/node/docker-compose.yml +1 -0
- package/templates/node/package.json +3 -2
- package/templates/node/src/connecta.config.ts +8 -0
- package/dist/catalog-drift.d.ts +0 -138
- package/dist/catalog-drift.js +0 -578
- package/dist/providers/_shared/request-token.js +0 -83
- package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
- package/dist/providers/_shared/rest/tools.d.ts +0 -141
- package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
- package/dist/providers/basecamp/index.d.ts +0 -45
- package/dist/providers/basecamp/index.js +0 -997
- package/dist/providers/basecamp/skill.generated.js +0 -9
- package/dist/providers/cloudflare/rest.d.ts +0 -37
- package/dist/providers/cloudflare/rest.js +0 -1097
- package/dist/providers/github/hosted.js +0 -218
- package/dist/providers/github/rest.js +0 -321
- package/dist/providers/infisical/index.d.ts +0 -7
- package/dist/providers/infisical/index.js +0 -713
- package/dist/providers/infisical/skill.generated.js +0 -8
- package/dist/providers/intercom/index.d.ts +0 -24
- package/dist/providers/intercom/index.js +0 -121
- package/dist/providers/intercom/skill.generated.js +0 -8
- package/dist/providers/linear/index.d.ts +0 -55
- package/dist/providers/linear/index.js +0 -537
- package/dist/providers/linear/skill.generated.js +0 -8
- package/dist/providers/mixpanel/index.d.ts +0 -59
- package/dist/providers/mixpanel/index.js +0 -472
- package/dist/providers/mixpanel/operations.js +0 -20
- package/dist/providers/mixpanel/rest.js +0 -603
- package/dist/providers/mixpanel/skill.generated.js +0 -10
- package/dist/providers/mixpanel/value-safety.js +0 -16
- package/dist/providers/notion/index.d.ts +0 -67
- package/dist/providers/notion/index.js +0 -368
- package/dist/providers/notion/openapi.generated.js +0 -75
- package/dist/providers/notion/rest.d.ts +0 -24
- package/dist/providers/notion/rest.js +0 -1226
- package/dist/providers/notion/skill.generated.js +0 -11
- package/dist/providers/notion/value-safety.js +0 -56
- package/dist/providers/revenuecat/index.d.ts +0 -51
- package/dist/providers/revenuecat/index.js +0 -697
- package/dist/providers/revenuecat/skill.generated.js +0 -11
- package/dist/providers/stripe/rest.d.ts +0 -14
- package/dist/providers/stripe/rest.js +0 -402
- package/dist/providers/vercel/rest.d.ts +0 -20
- package/dist/providers/vercel/rest.js +0 -1020
- package/dist/providers/wispr-flow/index.d.ts +0 -14
- package/dist/providers/wispr-flow/index.js +0 -117
- package/dist/providers/wispr-flow/skill.generated.js +0 -8
- /package/dist/providers/github/{key.js → lib/key.js} +0 -0
package/dist/invocation-auth.js
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { authCall, authFlow, registerAuth } from "./auth/native.js";
|
|
2
|
+
import { upwardStep } from "./upward.js";
|
|
3
|
+
import { runEdge } from "./runtime/run.js";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import { requestOwner } from "./runtime/request-scope.js";
|
|
6
|
+
import { cloneTool, sourceClassification } from "./tool-safety.js";
|
|
2
7
|
// Only built-in factories register local credential checks. This hook is not
|
|
3
8
|
// part of Connector, and a replacement callTool cannot inherit its proof.
|
|
4
9
|
const checks = new WeakMap();
|
|
5
10
|
export function registerInvocationAuth(connector, check) {
|
|
6
11
|
checks.set(connector, { callTool: connector.callTool, check });
|
|
7
12
|
}
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Local credential resolution only, before entering any connector call.
|
|
15
|
+
* `callTool` is the function the invocation will invoke, read once.
|
|
16
|
+
*/
|
|
17
|
+
export function resolveInvocationAuthEffect(connector, callTool, ctx) {
|
|
18
|
+
return Effect.suspend(() => {
|
|
19
|
+
const entry = checks.get(connector);
|
|
20
|
+
return entry?.callTool === callTool ? authCall(entry.check, [ctx]) : Effect.void;
|
|
21
|
+
});
|
|
13
22
|
}
|
|
14
|
-
const requests = new WeakMap();
|
|
15
23
|
function requestFor(scope) {
|
|
16
|
-
|
|
17
|
-
if (!facts) {
|
|
18
|
-
facts = { entered: [], authFailures: new Map(), retryReads: new Map() };
|
|
19
|
-
requests.set(scope, facts);
|
|
20
|
-
}
|
|
21
|
-
return facts;
|
|
24
|
+
return requestOwner(scope).retained.invocations;
|
|
22
25
|
}
|
|
23
26
|
/** Bind the verdict, annotation facts and schemas used for this call. */
|
|
24
27
|
export async function classificationDigest(definition) {
|
|
25
28
|
try {
|
|
26
|
-
const schema =
|
|
29
|
+
const schema = { inputSchema: definition.inputSchema, outputSchema: definition.outputSchema };
|
|
27
30
|
const annotations = Object.entries(definition.annotations ?? {}).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
28
|
-
const bytes = new TextEncoder().encode(JSON.stringify({
|
|
31
|
+
const bytes = new TextEncoder().encode(JSON.stringify({
|
|
32
|
+
classification: definition.classification,
|
|
33
|
+
source: sourceClassification(definition),
|
|
34
|
+
annotations,
|
|
35
|
+
schema,
|
|
36
|
+
}));
|
|
29
37
|
return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
30
38
|
}
|
|
31
39
|
catch {
|
|
@@ -37,7 +45,7 @@ export async function classificationDigest(definition) {
|
|
|
37
45
|
export function recordCallEntry(scope, call, definition) {
|
|
38
46
|
// Snapshot synchronously. Recovery hashes this private copy only when it
|
|
39
47
|
// needs a replay binding, without changing ordinary admission or completion.
|
|
40
|
-
requestFor(scope).entered.push({ ...call, definition:
|
|
48
|
+
requestFor(scope).entered.push({ ...call, definition: cloneTool(definition) });
|
|
41
49
|
}
|
|
42
50
|
export function bindReplayReads(scope, reads) {
|
|
43
51
|
requestFor(scope).retryReads = new Map(reads.map((read) => [read.address, read.digest]));
|
|
@@ -50,13 +58,16 @@ export function recordAuthFailure(scope, connector, eligible) {
|
|
|
50
58
|
const failures = requestFor(scope).authFailures;
|
|
51
59
|
failures.set(connector, eligible && failures.get(connector) !== false);
|
|
52
60
|
}
|
|
53
|
-
export
|
|
61
|
+
export function authRecoveryFacts(scope, connector) {
|
|
62
|
+
return runEdge(authRecoveryFactsNative(scope, connector));
|
|
63
|
+
}
|
|
64
|
+
const authRecoveryFactsNative = /* @__PURE__ */ registerAuth(authRecoveryFacts, (scope, connector) => authFlow(function* () {
|
|
54
65
|
const facts = requestFor(scope);
|
|
55
66
|
const writeEntered = facts.entered.some((call) => call.classification === "write");
|
|
56
67
|
const reads = new Map();
|
|
57
68
|
let unsafeEntered = writeEntered;
|
|
58
69
|
for (const call of facts.entered) {
|
|
59
|
-
const digest =
|
|
70
|
+
const digest = yield* upwardStep(authCall(classificationDigest, [call.definition]));
|
|
60
71
|
unsafeEntered ||= !call.fresh || !digest || (reads.has(call.address) && reads.get(call.address) !== digest);
|
|
61
72
|
reads.set(call.address, digest);
|
|
62
73
|
}
|
|
@@ -66,4 +77,4 @@ export async function authRecoveryFacts(scope, connector) {
|
|
|
66
77
|
eligible: !unsafeEntered && facts.authFailures.get(connector) === true,
|
|
67
78
|
reads: [...reads].map(([address, digest]) => ({ address, digest })),
|
|
68
79
|
};
|
|
69
|
-
}
|
|
80
|
+
}));
|
package/dist/invocation.js
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { authCall, registerAuthMethod } from "./auth/native.js";
|
|
2
|
+
import { withAuthorizationHandoffEffect } from "./authorization-handoff.js";
|
|
2
3
|
import { hasControlCharacters } from "./tool-name.js";
|
|
3
|
-
import { writeRefusal } from "./tool-safety.js";
|
|
4
|
-
import { Cause, Effect, Exit } from "effect";
|
|
5
|
-
import { isCallAdmissionError } from "./call-admission.js";
|
|
6
|
-
import {
|
|
7
|
-
import { boundedEchoText, classificationCode, classifyCallError, ConnectorCallError, echoedCallArgs, framingError, } from "./errors.js";
|
|
4
|
+
import { cloneTool, isWriteLike, writeRefusal } from "./tool-safety.js";
|
|
5
|
+
import { Cause, Context, Effect, Exit } from "effect";
|
|
6
|
+
import { CallAdmissionError, isCallAdmissionError } from "./call-admission.js";
|
|
7
|
+
import { boundedEchoText, classificationCode, classifyCallError, classifyWithoutAdvice, ConnectorCallError, echoedCallArgs, framingError, keyResendAdviceOf, resendAdviceOf, withResendAdvice, } from "./errors.js";
|
|
8
8
|
import { downstreamValue } from "./mcp-result.js";
|
|
9
9
|
import { carryFailureFacts, classifiedFailure, failureRecord, logFailure, recordedToolName, } from "./operator-record.js";
|
|
10
10
|
import { splitAddress } from "./registry.js";
|
|
11
|
-
import { runEdge, withDeadlineEffect } from "./runtime/run.js";
|
|
11
|
+
import { causeError, runEdge, withDeadlineEffect } from "./runtime/run.js";
|
|
12
12
|
import { validateCatalogToolInput } from "./validate.js";
|
|
13
|
-
import { sentSecretsFor, sentSecretsForRequest
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
13
|
+
import { sentSecretsFor, sentSecretsForRequest } from "./sent-secrets.js";
|
|
14
|
+
import { recoveryRecord, recordRecoveryFacts } from "./call-recovery.js";
|
|
15
|
+
import { callFromContext, contextView, viewServices, Credentials } from "./runtime/call.js";
|
|
16
|
+
import { chargeCallBudget } from "./runtime/call-admission.js";
|
|
17
|
+
import { callEntry } from "./runtime/call-entry.js";
|
|
18
|
+
import { forgetProviderFailure, isKnownProviderFailure, isSettledWrite, keyProtectsCall, knownCallFailure, onlyAuxiliaryRequests, knownProviderFailure, openLogicalCall, } from "./provider-call.js";
|
|
19
|
+
import { classifyWriteOutcome, settleWrite, settleWriteResult } from "./write-outcome.js";
|
|
20
|
+
import { classificationDigest, recordAuthFailure, recordCallEntry, replayClassificationDigest, resolveInvocationAuthEffect, } from "./invocation-auth.js";
|
|
17
21
|
import { downstreamContinuation, recordDownstreamArgumentEcho, isDownstreamInputResult, assertDownstreamOutputSafe, } from "./downstream-input-context.js";
|
|
22
|
+
import { keepErrorName } from "./redaction-identity.js";
|
|
18
23
|
function defined(values) {
|
|
19
24
|
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
|
|
20
25
|
}
|
|
@@ -74,8 +79,8 @@ function isCallerCancellation(error, signal) {
|
|
|
74
79
|
}
|
|
75
80
|
/**
|
|
76
81
|
* A downstream MCP tool answered with `isError`. Classified exactly as the
|
|
77
|
-
* plain `Error`: untyped
|
|
78
|
-
* a
|
|
82
|
+
* plain `Error`: untyped and non-retryable. It is a read's answer; for a
|
|
83
|
+
* write it is a 2xx reply after which the handler may have applied it.
|
|
79
84
|
*/
|
|
80
85
|
class DownstreamToolError extends Error {
|
|
81
86
|
}
|
|
@@ -97,13 +102,18 @@ function assertRawMcpSuccess(kind, result) {
|
|
|
97
102
|
.join("") || "Downstream tool call failed"));
|
|
98
103
|
}
|
|
99
104
|
/**
|
|
100
|
-
* Whether a failed attempt is the downstream tool's own answer
|
|
101
|
-
* `isError
|
|
102
|
-
*
|
|
103
|
-
*
|
|
105
|
+
* Whether a failed attempt is the downstream tool's own answer. For a read:
|
|
106
|
+
* an MCP `isError`, or a boundary's known refusal/unsent failure. For a
|
|
107
|
+
* write, only refusal provenance that holds for the whole logical call
|
|
108
|
+
* (`knownCallFailure`); an `isError` arrives over a 2xx after the handler
|
|
109
|
+
* ran. A typed error alone is not proof: a body ceiling, refused redirect, or
|
|
110
|
+
* handler error may follow execution. Boundaries supply provenance, never
|
|
111
|
+
* wording.
|
|
104
112
|
*/
|
|
105
|
-
function answeredFailure(error) {
|
|
106
|
-
|
|
113
|
+
function answeredFailure(error, write, call) {
|
|
114
|
+
if (!write)
|
|
115
|
+
return error instanceof DownstreamToolError || isKnownProviderFailure(error);
|
|
116
|
+
return knownCallFailure(call, error);
|
|
107
117
|
}
|
|
108
118
|
export class InvocationFailure extends Error {
|
|
109
119
|
details;
|
|
@@ -131,6 +141,7 @@ export class InvocationFailure extends Error {
|
|
|
131
141
|
this.data = details;
|
|
132
142
|
}
|
|
133
143
|
}
|
|
144
|
+
keepErrorName(InvocationFailure, "InvocationFailure");
|
|
134
145
|
/**
|
|
135
146
|
* Shared downstream invocation engine. Address/catalog resolution is delegated
|
|
136
147
|
* to the request-local CatalogService; every remaining call semantic lives
|
|
@@ -149,6 +160,7 @@ export class InvocationService {
|
|
|
149
160
|
this.registry = registry;
|
|
150
161
|
this.catalog = catalog;
|
|
151
162
|
this.activity = activity;
|
|
163
|
+
void InvocationService.nativePrograms;
|
|
152
164
|
}
|
|
153
165
|
invoke(address, args, context) {
|
|
154
166
|
return runEdge(this.pipeline(address, args, context));
|
|
@@ -169,11 +181,20 @@ export class InvocationService {
|
|
|
169
181
|
let attempts = 0;
|
|
170
182
|
let resultBytes;
|
|
171
183
|
let dispatchedToConnector = false;
|
|
172
|
-
// The
|
|
173
|
-
|
|
174
|
-
|
|
184
|
+
// The logical call bridge; recovery is a separate invocation-owned record.
|
|
185
|
+
let logicalCall;
|
|
186
|
+
// The served class owns gating and presentation. Physical source-write
|
|
187
|
+
// provenance additionally owns failure settlement, after dispatch only.
|
|
188
|
+
const sentSourceWrite = () => logicalCall?.sent.some((request) => request.sourceWrite === true) === true;
|
|
189
|
+
const recoveryState = recoveryRecord();
|
|
175
190
|
let preInvocationAuthFailure = false;
|
|
176
191
|
let answered = false;
|
|
192
|
+
// A dispatched failure's advice to send it again, composed by enrich
|
|
193
|
+
// only once the outcome allows it (resendAdvice), and its advice to
|
|
194
|
+
// reuse an idempotency key, only where that key protects the whole
|
|
195
|
+
// logical call (keyResendAdvice, keyProtectsCall).
|
|
196
|
+
let failureAdvice;
|
|
197
|
+
let failureKeyAdvice;
|
|
177
198
|
const sentSecrets = sentSecretsForRequest(this.catalog.requestScope);
|
|
178
199
|
context.sentSecrets?.include(sentSecrets);
|
|
179
200
|
// A write gate's refusal that is no attempt; see WriteGateDecision.
|
|
@@ -207,7 +228,7 @@ export class InvocationService {
|
|
|
207
228
|
connectorId: identity.connectorId,
|
|
208
229
|
toolName,
|
|
209
230
|
address: `${identity.connectorId}.${toolName}`,
|
|
210
|
-
source: context.source,
|
|
231
|
+
source: context.activitySource ?? context.source,
|
|
211
232
|
outcome,
|
|
212
233
|
...(activityTarget?.connector.authScope === "personal" ? { personal: true } : {}),
|
|
213
234
|
durationMs: Date.now() - started,
|
|
@@ -220,20 +241,33 @@ export class InvocationService {
|
|
|
220
241
|
}),
|
|
221
242
|
});
|
|
222
243
|
};
|
|
223
|
-
const enrich = (error, target) => {
|
|
244
|
+
const enrich = (error, target, settled) => {
|
|
224
245
|
if (!target)
|
|
225
246
|
return error;
|
|
226
|
-
const recovery =
|
|
227
|
-
const
|
|
247
|
+
const recovery = recoveryState.facts;
|
|
248
|
+
const enteredWrite = dispatchedToConnector &&
|
|
249
|
+
((resolved !== undefined && resolved.definition.classification !== "read") || sentSourceWrite());
|
|
250
|
+
// A known answer alone is not dispatch evidence. Require an owned,
|
|
251
|
+
// complete ledger with a positively recorded auxiliary resource read;
|
|
252
|
+
// an empty ledger, custom entry or interruption proves nothing.
|
|
253
|
+
const mayHaveWritten = enteredWrite && (error.writeOutcome === "unknown" || !(answered && onlyAuxiliaryRequests(logicalCall)));
|
|
254
|
+
// A recorded key is a diagnostic; advice to reuse it needs proof that
|
|
255
|
+
// it protects the whole logical call, not one request inside it. Q8's
|
|
256
|
+
// settled verdict is final: no enricher adds advice to it.
|
|
257
|
+
const recoveryRetry = !settled && keyProtectsCall(logicalCall, recovery?.["idempotencyKey"])
|
|
228
258
|
? " A deliberate retry with uncertainCall.recovery.idempotencyKey and the exact original arguments cannot repeat the write while the vendor retains that key (Stripe keeps v1 keys at least 24 hours); after that, look the object up before retrying."
|
|
229
259
|
: "";
|
|
230
|
-
if (isTimeoutFailure(error) &&
|
|
260
|
+
if (isTimeoutFailure(error) && mayHaveWritten) {
|
|
261
|
+
// An empty ledger's known mark cannot settle this timeout. Keep
|
|
262
|
+
// program accounting conservative as well as its displayed code.
|
|
263
|
+
answered = false;
|
|
231
264
|
const echoed = argumentEcho;
|
|
232
265
|
return {
|
|
233
266
|
...error,
|
|
234
267
|
code: "write_outcome_unknown",
|
|
235
268
|
message: `${error.message} Its outcome is unknown. Check the target before repeating this call.`,
|
|
236
269
|
retryable: false,
|
|
270
|
+
...(sentSourceWrite() ? { writeOutcome: "unknown" } : {}),
|
|
237
271
|
connector: target.connector.id,
|
|
238
272
|
operation: `${target.connector.id}.${target.toolName}`,
|
|
239
273
|
uncertainCall: {
|
|
@@ -251,14 +285,35 @@ export class InvocationService {
|
|
|
251
285
|
: " The arguments could not be echoed safely; use the exact arguments you sent."),
|
|
252
286
|
};
|
|
253
287
|
}
|
|
288
|
+
if (mayHaveWritten &&
|
|
289
|
+
(error.code === "auth_required" || error.code === "downstream_oauth_required") &&
|
|
290
|
+
!logicalCall?.sent.some((request) => request.dispatched)) {
|
|
291
|
+
// An empty resource ledger cannot settle this auth failure. Keep
|
|
292
|
+
// actual resource-refusal provenance, but withdraw an empty ledger's
|
|
293
|
+
// known mark before composing advice and counting program writes.
|
|
294
|
+
answered = false;
|
|
295
|
+
forgetProviderFailure(error);
|
|
296
|
+
}
|
|
297
|
+
// A dispatched write without refusal provenance is unknown whatever
|
|
298
|
+
// its connector's code says, a custom connector's included: never
|
|
299
|
+
// retryable, and no advice below may tell the agent to retry it.
|
|
300
|
+
const unknownWrite = mayHaveWritten && classifyWriteOutcome({ ok: false, dispatched: true, answered, error }) === "unknown";
|
|
301
|
+
if (unknownWrite)
|
|
302
|
+
error = { ...error, retryable: false, writeOutcome: "unknown" };
|
|
303
|
+
// The failure's advice to send it again was kept apart until now, and
|
|
304
|
+
// joins its message only where the outcome allows it: never on a write
|
|
305
|
+
// whose outcome is unknown, whatever redaction did to the message.
|
|
306
|
+
// Key-reuse advice also needs its key to protect the logical call.
|
|
307
|
+
else if (!settled) {
|
|
308
|
+
if (failureKeyAdvice && keyProtectsCall(logicalCall, failureKeyAdvice.key))
|
|
309
|
+
error = { ...error, message: withResendAdvice(error.message, failureKeyAdvice.advice) };
|
|
310
|
+
if (failureAdvice !== undefined)
|
|
311
|
+
error = { ...error, message: withResendAdvice(error.message, failureAdvice) };
|
|
312
|
+
}
|
|
254
313
|
// Any other dispatched write whose outcome is unknown (a lost response
|
|
255
314
|
// body, a 5xx, an unreadable reply) keeps the facts its connector
|
|
256
315
|
// recorded before dispatch, here and through program write accounting.
|
|
257
|
-
if (recovery &&
|
|
258
|
-
!error.uncertainCall &&
|
|
259
|
-
dispatchedToConnector &&
|
|
260
|
-
resolved?.definition.classification === "write" &&
|
|
261
|
-
classifyWriteOutcome({ ok: false, dispatched: true, answered, error }) === "unknown") {
|
|
316
|
+
if (recovery && !error.uncertainCall && unknownWrite) {
|
|
262
317
|
const echoed = argumentEcho;
|
|
263
318
|
error = {
|
|
264
319
|
...error,
|
|
@@ -274,11 +329,16 @@ export class InvocationService {
|
|
|
274
329
|
if (error.code === "auth_required" && target.connector.startAuth) {
|
|
275
330
|
error = { ...error, code: "downstream_oauth_required" };
|
|
276
331
|
}
|
|
277
|
-
const enteredWrite = resolved?.definition.classification === "write" && dispatchedToConnector;
|
|
278
332
|
switch (error.code) {
|
|
279
333
|
case "input_required_unsupported":
|
|
280
334
|
if (context.processInputRequired)
|
|
281
335
|
return error;
|
|
336
|
+
// Resending could apply the write twice: reconcile first.
|
|
337
|
+
if (unknownWrite)
|
|
338
|
+
return {
|
|
339
|
+
...error,
|
|
340
|
+
retry: "Do not resend this write. Re-read its target to check whether it took effect before any fresh call.",
|
|
341
|
+
};
|
|
282
342
|
return {
|
|
283
343
|
...error,
|
|
284
344
|
nextAction: {
|
|
@@ -293,6 +353,8 @@ export class InvocationService {
|
|
|
293
353
|
},
|
|
294
354
|
};
|
|
295
355
|
case "destructive_tool_requires_approval": {
|
|
356
|
+
if (settled)
|
|
357
|
+
return error;
|
|
296
358
|
const echoed = argumentEcho;
|
|
297
359
|
return {
|
|
298
360
|
...error,
|
|
@@ -318,17 +380,22 @@ export class InvocationService {
|
|
|
318
380
|
connector: target.connector.id,
|
|
319
381
|
operation: `${target.connector.id}.${target.toolName}`,
|
|
320
382
|
recovery: recoveryMode(this.registry, target.connector, this.catalog.baseUrl),
|
|
321
|
-
...(
|
|
383
|
+
...(unknownWrite ? { reconciliationRequired: true, retryable: false } : {}),
|
|
322
384
|
nextAction: {
|
|
323
385
|
tool: "authorize_connector",
|
|
324
386
|
arguments: { connector: target.connector.id },
|
|
325
387
|
operatorHandoff: "Give the URL and instructions it returns to the operator.",
|
|
326
388
|
},
|
|
327
|
-
retry:
|
|
389
|
+
retry: unknownWrite
|
|
328
390
|
? "This write may have partially run. Reconcile its target before retrying after the operator completes recovery."
|
|
329
|
-
:
|
|
391
|
+
: enteredWrite && logicalCall?.sent.some((request) => request.dispatched)
|
|
392
|
+
? `Fix the credentials, then retry ${target.connector.id}.${target.toolName}.`
|
|
393
|
+
: `Retry ${target.connector.id}.${target.toolName} after the operator completes recovery.`,
|
|
330
394
|
};
|
|
331
395
|
case "provider_permission_denied":
|
|
396
|
+
// A write whose outcome is unknown is never told to retry.
|
|
397
|
+
if (error.writeOutcome === "unknown")
|
|
398
|
+
return error;
|
|
332
399
|
return {
|
|
333
400
|
...error,
|
|
334
401
|
connector: target.connector.id,
|
|
@@ -336,7 +403,7 @@ export class InvocationService {
|
|
|
336
403
|
retry: "Ask the provider's resource owner or administrator to grant the required permission or scope, then retry. Repeating the call or reconnecting alone will not grant access.",
|
|
337
404
|
};
|
|
338
405
|
case "invalid_args":
|
|
339
|
-
if (!error.validation)
|
|
406
|
+
if (!error.validation || error.writeOutcome === "unknown")
|
|
340
407
|
return error;
|
|
341
408
|
return {
|
|
342
409
|
...error,
|
|
@@ -354,13 +421,27 @@ export class InvocationService {
|
|
|
354
421
|
};
|
|
355
422
|
let stage = "catalog";
|
|
356
423
|
const failed = (error) => {
|
|
424
|
+
// Q8 (settleWrite): this invocation's deadline or its caller cut off
|
|
425
|
+
// a write. No connector failure arrives, so the logical call's
|
|
426
|
+
// recorded requests alone decide whether a 401 left it unknown.
|
|
427
|
+
if (dispatchedToConnector &&
|
|
428
|
+
(resolved?.definition.classification !== "read" || sentSourceWrite()) &&
|
|
429
|
+
(error.code === "cancelled" || isTimeoutFailure(error))) {
|
|
430
|
+
const departed = settleWrite(logicalCall?.sent ?? [], undefined, true);
|
|
431
|
+
if (departed !== undefined) {
|
|
432
|
+
failureAdvice = failureKeyAdvice = undefined;
|
|
433
|
+
error = carryFailureFacts(departed, classifyWithoutAdvice(sentSecrets.redact(departed)));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
357
436
|
const diagnostics = timing();
|
|
358
437
|
const target = resolved ?? activityTarget;
|
|
359
438
|
if (target && (error.code === "auth_required" || error.code === "downstream_oauth_required")) {
|
|
360
|
-
recordAuthFailure(this.catalog.requestScope, target.connector.id, (resolved
|
|
439
|
+
recordAuthFailure(this.catalog.requestScope, target.connector.id, (resolved !== undefined && !isWriteLike(resolved.definition) && resolved.classificationFresh === true) ||
|
|
361
440
|
preInvocationAuthFailure ||
|
|
362
441
|
(!resolved && context.source === "call_tool"));
|
|
363
442
|
}
|
|
443
|
+
// Read before any step below rebuilds the details: Q8's verdict is final.
|
|
444
|
+
const settled = isSettledWrite(error);
|
|
364
445
|
// Attach host deadline facts before write recovery changes the code.
|
|
365
446
|
if (isTimeoutFailure(error)) {
|
|
366
447
|
const elapsedMs = Date.now() - started;
|
|
@@ -372,7 +453,7 @@ export class InvocationService {
|
|
|
372
453
|
};
|
|
373
454
|
}
|
|
374
455
|
// Downstream diagnostics were redacted under payload rules where they entered.
|
|
375
|
-
const details = sentSecrets.redact(enrich(error, target), "envelope");
|
|
456
|
+
const details = carryFailureFacts(error, sentSecrets.redact(enrich(error, target, settled), "envelope"));
|
|
376
457
|
const outcome = () => ({
|
|
377
458
|
ok: false,
|
|
378
459
|
durationMs: Date.now() - started,
|
|
@@ -395,7 +476,7 @@ export class InvocationService {
|
|
|
395
476
|
connector: target.connector.id,
|
|
396
477
|
// Named only when the catalog listed it (src/operator-record.ts).
|
|
397
478
|
tool: resolved?.definition,
|
|
398
|
-
source: context.source,
|
|
479
|
+
source: context.activitySource ?? context.source,
|
|
399
480
|
attempts,
|
|
400
481
|
durationMs: Date.now() - started,
|
|
401
482
|
// Every refusal reaching `failed` is one connecta built: a
|
|
@@ -449,6 +530,7 @@ export class InvocationService {
|
|
|
449
530
|
argumentEcho = echoedCallArgs(args ?? {}, target.definition.inputSchema);
|
|
450
531
|
sentSecrets.arguments(args ?? {}, target.definition.inputSchema);
|
|
451
532
|
const write = target.definition.classification !== "read";
|
|
533
|
+
const writeLike = isWriteLike(target.definition);
|
|
452
534
|
const canonicalAddress = `${target.connector.id}.${target.toolName}`;
|
|
453
535
|
const input = context.source !== "execute_code"
|
|
454
536
|
? downstreamContinuation(this.catalog.requestScope, target.connector.id, canonicalAddress)
|
|
@@ -457,7 +539,8 @@ export class InvocationService {
|
|
|
457
539
|
recordDownstreamArgumentEcho(this.catalog.requestScope, argumentEcho);
|
|
458
540
|
const expectedDigest = replayClassificationDigest(this.catalog.requestScope, canonicalAddress);
|
|
459
541
|
if (expectedDigest !== undefined &&
|
|
460
|
-
(
|
|
542
|
+
(writeLike ||
|
|
543
|
+
target.classificationFresh !== true ||
|
|
461
544
|
(yield* Effect.promise(() => classificationDigest(target.definition))) !== expectedDigest)) {
|
|
462
545
|
return {
|
|
463
546
|
...framingError("auth_replay_refused", "An entered read no longer has the same fresh classification. Reconcile its target before starting a new request."),
|
|
@@ -486,7 +569,8 @@ export class InvocationService {
|
|
|
486
569
|
if (invalid)
|
|
487
570
|
return classifyCallError(invalid);
|
|
488
571
|
}
|
|
489
|
-
|
|
572
|
+
// Source writes keep program write accounting; only effective writes spend W10 budget.
|
|
573
|
+
if (writeLike && context.beforeWrite) {
|
|
490
574
|
const decision = yield* context.beforeWrite(target, args ?? {});
|
|
491
575
|
if (decision.kind === "refuse") {
|
|
492
576
|
unrecorded = decision.unrecorded === true;
|
|
@@ -496,44 +580,71 @@ export class InvocationService {
|
|
|
496
580
|
if (callSignal?.aborted)
|
|
497
581
|
return yield* Effect.fail(callSignal.reason);
|
|
498
582
|
attempts = 1;
|
|
499
|
-
const call =
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
583
|
+
const call = (permit) => Effect.gen({ self: this }, function* () {
|
|
584
|
+
const operationContext = this.registry.contextFor(target.connector.id, this.catalog.baseUrl, this.catalog.requestScope, defined({ signal: callSignal, timeoutMs: context.timeoutMs, defer: this.catalog.defer }));
|
|
585
|
+
const lease = callFromContext(operationContext, write ? "write" : "read", {
|
|
586
|
+
admitted: true,
|
|
587
|
+
// An invocation deadline aborts when its call finishes. A
|
|
588
|
+
// request-owned auxiliary must retain only request cancellation.
|
|
589
|
+
requestSourceSignal: context.requestSignal,
|
|
590
|
+
served: write ? "write" : "read",
|
|
591
|
+
charge: (signal) => runEdge(chargeCallBudget(permit, signal)),
|
|
592
|
+
chargeEffect: (signal) => chargeCallBudget(permit, signal),
|
|
593
|
+
chargeClosed: () => new CallAdmissionError("closed", "connector_call_failed", "This call has ended; its admission permit can no longer be charged."),
|
|
594
|
+
record: (facts) => recordRecoveryFacts(recoveryState, facts),
|
|
595
|
+
});
|
|
596
|
+
yield* Effect.addFinalizer(() => Effect.sync(lease.close));
|
|
597
|
+
const connectorContext = contextView(lease, operationContext);
|
|
503
598
|
sentSecrets.include(sentSecretsFor(connectorContext));
|
|
504
|
-
|
|
505
|
-
|
|
599
|
+
// The one object and function this call invokes, read once: its
|
|
600
|
+
// logical call, auth check, and dispatch all name them, so a
|
|
601
|
+
// connector that reassigns its own callTool mid-call changes none.
|
|
602
|
+
const connector = target.connector;
|
|
603
|
+
const callTool = connector.callTool;
|
|
604
|
+
logicalCall = openLogicalCall(connectorContext, connector, callTool);
|
|
605
|
+
yield* Effect.gen(function* () {
|
|
506
606
|
if (target.connector.credential) {
|
|
507
607
|
if (!connectorContext.credential) {
|
|
508
608
|
throw new ConnectorCallError("auth_required", "Operator-managed credential storage is not configured. Call " +
|
|
509
609
|
`authorize_connector({ connector: "${target.connector.id}" }).`);
|
|
510
610
|
}
|
|
511
|
-
if (!(
|
|
611
|
+
if (!(yield* Context.get(lease.services, Credentials).values)) {
|
|
512
612
|
throw new ConnectorCallError("auth_required", `Connector "${target.connector.id}" has no stored credential. Call authorize_connector.`);
|
|
513
613
|
}
|
|
514
614
|
}
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
const code = classifyCallError(error).code;
|
|
615
|
+
yield* resolveInvocationAuthEffect(connector, callTool, connectorContext);
|
|
616
|
+
}).pipe(Effect.onError((cause) => Effect.sync(() => {
|
|
617
|
+
const code = classifyCallError(causeError(cause, connectorContext.signal)).code;
|
|
519
618
|
preInvocationAuthFailure = code === "auth_required" || code === "downstream_oauth_required";
|
|
520
|
-
|
|
521
|
-
}
|
|
619
|
+
})));
|
|
522
620
|
// Cancellation can arrive during admission or context construction.
|
|
523
621
|
if (admissionSignal?.aborted)
|
|
524
622
|
throw admissionSignal.reason;
|
|
525
623
|
dispatchedToConnector = true;
|
|
526
624
|
recordCallEntry(this.catalog.requestScope, {
|
|
527
625
|
address: canonicalAddress,
|
|
528
|
-
classification:
|
|
626
|
+
classification: writeLike ? "write" : "read",
|
|
529
627
|
fresh: target.classificationFresh === true,
|
|
530
628
|
}, target.definition);
|
|
531
|
-
|
|
532
|
-
//
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
629
|
+
// Reflect.apply, never the function's own `call` or `apply`
|
|
630
|
+
// property, which anyone holding the function could replace.
|
|
631
|
+
const options = { definition: cloneTool(target.definition), ...defined({ input }) };
|
|
632
|
+
const entry = callEntry(connector, callTool);
|
|
633
|
+
if (entry)
|
|
634
|
+
return yield* entry(target.toolName, args ?? {}, options).pipe(Effect.provideContext(viewServices(connectorContext)));
|
|
635
|
+
return yield* Effect.tryPromise({
|
|
636
|
+
try: () => Reflect.apply(callTool, connector, [
|
|
637
|
+
target.toolName,
|
|
638
|
+
args ?? {},
|
|
639
|
+
connectorContext,
|
|
640
|
+
// The connector may retain or mutate its definition. Keep the
|
|
641
|
+
// invocation's classification and schema private, even during
|
|
642
|
+
// this call, and give every dispatch its own deep copy.
|
|
643
|
+
options,
|
|
644
|
+
]),
|
|
645
|
+
catch: (error) => error,
|
|
646
|
+
});
|
|
647
|
+
});
|
|
537
648
|
// The permit belongs to this scope, so success, failure, and the
|
|
538
649
|
// deadline's interruption all release it. Interruption stops the
|
|
539
650
|
// wait instead of waiting for the connector, so an uncooperative one
|
|
@@ -546,17 +657,16 @@ export class InvocationService {
|
|
|
546
657
|
stage = "downstream";
|
|
547
658
|
const reply = yield* timed((elapsed) => {
|
|
548
659
|
connectorMs += elapsed;
|
|
549
|
-
},
|
|
550
|
-
try: () => Promise.resolve(call(permit)),
|
|
551
|
-
catch: (error) => error,
|
|
552
|
-
}));
|
|
660
|
+
}, call(permit));
|
|
553
661
|
if (target.connector.kind === "mcp" && isDownstreamInputResult(reply)) {
|
|
662
|
+
// The downstream suspended for input. A handler asks for
|
|
663
|
+
// input from inside its run, so a write may already have
|
|
664
|
+
// applied: only a read's suspension is a known answer.
|
|
554
665
|
if (!context.processInputRequired)
|
|
555
|
-
throw
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
});
|
|
666
|
+
throw write
|
|
667
|
+
? new ConnectorCallError("input_required_unsupported", "The downstream returned input_required after this write was sent, and this call cannot provide input. Whether the write took effect is unknown.", { writeOutcome: "unknown" })
|
|
668
|
+
: knownProviderFailure(new ConnectorCallError("input_required_unsupported", "The downstream returned input_required. Use the equivalent direct MCP call to provide input."));
|
|
669
|
+
const value = yield* authCall(context.processInputRequired, [reply, target, sentSecrets]);
|
|
560
670
|
return { inputRequired: true, value };
|
|
561
671
|
}
|
|
562
672
|
assertDownstreamOutputSafe(this.catalog.requestScope, reply);
|
|
@@ -572,11 +682,23 @@ export class InvocationService {
|
|
|
572
682
|
return yield* Effect.fail(callSignal.reason);
|
|
573
683
|
if (!dispatchedToConnector && admissionSignal?.aborted)
|
|
574
684
|
return callerCancelledDetails();
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
685
|
+
const thrown = Cause.squash(attempt.cause);
|
|
686
|
+
if (isCallerCancellation(thrown, context.requestSignal))
|
|
687
|
+
return callerCancelledDetails();
|
|
688
|
+
const failureWrite = write || sentSourceWrite();
|
|
689
|
+
answered = answeredFailure(thrown, failureWrite, logicalCall);
|
|
690
|
+
// Q8 is decided here for every served write, whatever its
|
|
691
|
+
// connector made of the 401: over the logical call's whole ledger.
|
|
692
|
+
const attemptError = failureWrite ? settleWrite(logicalCall?.sent ?? [], thrown, answered) : thrown;
|
|
693
|
+
if (attemptError !== thrown)
|
|
694
|
+
answered = false;
|
|
695
|
+
failureAdvice = resendAdviceOf(attemptError);
|
|
696
|
+
failureKeyAdvice = keyResendAdviceOf(attemptError);
|
|
697
|
+
const details = carryFailureFacts(attemptError, classifyWithoutAdvice(sentSecrets.redact(attemptError)));
|
|
698
|
+
// An inner call's verdict that does not hold for this one is withdrawn.
|
|
699
|
+
if (failureWrite && !answered)
|
|
700
|
+
forgetProviderFailure(details);
|
|
701
|
+
return details;
|
|
580
702
|
}
|
|
581
703
|
if ("inputRequired" in attempt.value) {
|
|
582
704
|
inputRequiredValue = { value: attempt.value.value };
|
|
@@ -613,9 +735,11 @@ export class InvocationService {
|
|
|
613
735
|
: dispatch());
|
|
614
736
|
if (Exit.isFailure(dispatched)) {
|
|
615
737
|
const failure = Cause.squash(dispatched.cause);
|
|
738
|
+
failureAdvice = resendAdviceOf(failure);
|
|
739
|
+
failureKeyAdvice = keyResendAdviceOf(failure);
|
|
616
740
|
const details = context.requestSignal?.aborted
|
|
617
741
|
? callerCancelledDetails()
|
|
618
|
-
: carryFailureFacts(failure,
|
|
742
|
+
: carryFailureFacts(failure, classifyWithoutAdvice(sentSecrets.redact(failure)));
|
|
619
743
|
return failed(details);
|
|
620
744
|
}
|
|
621
745
|
if (dispatched.value)
|
|
@@ -625,14 +749,21 @@ export class InvocationService {
|
|
|
625
749
|
if (!completed) {
|
|
626
750
|
return yield* Effect.die(new Error("Invocation completed without a resolved tool"));
|
|
627
751
|
}
|
|
752
|
+
// Q8 settles a result too: a write an undeclared 401 left unknown is no
|
|
753
|
+
// success, whatever its handler made of the 401, and its result is not
|
|
754
|
+
// processed.
|
|
755
|
+
if (dispatchedToConnector && (completed.definition.classification !== "read" || sentSourceWrite())) {
|
|
756
|
+
const unknown = settleWriteResult(logicalCall?.sent ?? []);
|
|
757
|
+
if (unknown) {
|
|
758
|
+
failureAdvice = failureKeyAdvice = undefined;
|
|
759
|
+
return failed(carryFailureFacts(unknown, classifyWithoutAdvice(sentSecrets.redact(unknown))));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
628
762
|
const processResult = context.processResult;
|
|
629
763
|
const processing = inputRequiredValue
|
|
630
764
|
? Effect.succeed(inputRequiredValue.value)
|
|
631
765
|
: processResult
|
|
632
|
-
?
|
|
633
|
-
try: () => Promise.resolve(processResult(sentSecrets.redact(result), completed, sentSecrets, valueFormat, rawResult)),
|
|
634
|
-
catch: (error) => error,
|
|
635
|
-
})
|
|
766
|
+
? authCall(processResult, [sentSecrets.redact(result), completed, sentSecrets, valueFormat, rawResult])
|
|
636
767
|
: Effect.succeed(result);
|
|
637
768
|
const processed = yield* Effect.exit(timed((elapsed) => {
|
|
638
769
|
resultProcessingMs += elapsed;
|
|
@@ -671,9 +802,11 @@ export class InvocationService {
|
|
|
671
802
|
}
|
|
672
803
|
}).pipe(Effect.flatMap((outcome) => outcome.ok
|
|
673
804
|
? Effect.succeed(outcome)
|
|
674
|
-
: Effect.
|
|
675
|
-
...outcome,
|
|
676
|
-
error: await withAuthorizationHandoff(this.catalog.requestScope, outcome.error),
|
|
677
|
-
}))));
|
|
805
|
+
: withAuthorizationHandoffEffect(this.catalog.requestScope, outcome.error).pipe(Effect.map((error) => ({ ...outcome, error })))));
|
|
678
806
|
}
|
|
807
|
+
static nativePrograms = /* @__PURE__ */ (() => {
|
|
808
|
+
registerAuthMethod(InvocationService.prototype, "invoke", function (address, args, context) {
|
|
809
|
+
return this.pipeline(address, args, context);
|
|
810
|
+
}, InvocationService.prototype.invoke);
|
|
811
|
+
})();
|
|
679
812
|
}
|
package/dist/mcp-result.js
CHANGED
|
@@ -33,7 +33,3 @@ export function downstreamValue(kind, result) {
|
|
|
33
33
|
}
|
|
34
34
|
return { data: result, format: "json" };
|
|
35
35
|
}
|
|
36
|
-
/** Preserve the raw-value API for internal callers that already know its format. */
|
|
37
|
-
export function unwrapMcpResult(kind, result) {
|
|
38
|
-
return downstreamValue(kind, result).data;
|
|
39
|
-
}
|