@zackbart/connecta 0.28.1 → 0.30.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 +855 -5
- package/README.md +125 -116
- package/bin/connecta.mjs +121 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/argument-redaction.js +230 -0
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1584 -1580
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +93 -71
- package/dist/auth-elicitation.js +288 -0
- package/dist/authorization-handoff.js +111 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +409 -225
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +633 -0
- package/dist/config.js +508 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +59 -7
- package/dist/connectors/api-connector.js +106 -19
- package/dist/connectors/api.js +4 -1
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +87 -38
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1774 -320
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +128 -0
- package/dist/downstream-input.js +325 -0
- package/dist/errors.d.ts +132 -31
- package/dist/errors.js +175 -87
- package/dist/execute.js +798 -318
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +30 -13
- package/dist/executors/quickjs-child.js +15 -9
- package/dist/executors/quickjs-protocol.js +5 -6
- package/dist/executors/quickjs-runtime.js +152 -59
- package/dist/executors/quickjs.js +49 -39
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +238 -0
- package/dist/guest-types.d.ts +218 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +24 -256
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +262 -79
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +61 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +419 -272
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +194 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-result.js +5 -0
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +60 -19
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/_shared/request-token.js +83 -0
- package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
- package/dist/providers/{breeze.js → breeze/index.js} +184 -140
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
- package/dist/providers/{ccb.js → ccb/index.js} +271 -116
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/infisical/index.d.ts +7 -0
- package/dist/providers/infisical/index.js +713 -0
- package/dist/providers/infisical/skill.generated.js +8 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
- package/dist/providers/{overflow.js → overflow/index.js} +74 -111
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
- package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
- package/dist/providers/{tithely.js → tithely/index.js} +60 -91
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +110 -171
- package/dist/registry.js +588 -927
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +103 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +365 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +110 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +93 -0
- package/dist/sent-secrets.js +1013 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +416 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +181 -0
- package/dist/storage/keys.js +443 -0
- package/dist/storage/memory.js +16 -4
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +543 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +336 -56
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +146 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +435 -0
- package/documentation/README.md +63 -0
- package/documentation/architecture.md +696 -231
- package/documentation/auth.md +1069 -546
- package/documentation/code-mode.md +662 -356
- package/documentation/deploying.md +146 -0
- package/documentation/integrating.md +159 -0
- package/documentation/meta-tools.md +570 -216
- package/documentation/operating.md +135 -0
- package/documentation/operator-tests.md +97 -0
- package/documentation/operator-ui.md +132 -0
- package/documentation/provider-migration-0.29.md +65 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +293 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +126 -54
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +140 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/basecamp.js +0 -445
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { inputRequired, specTypeSchemas, ProtocolError, INVALID_PARAMS, CLIENT_CAPABILITIES_META_KEY, PROTOCOL_VERSION_META_KEY, } from "@modelcontextprotocol/server";
|
|
2
|
+
import { ConnectorCallError } from "./errors.js";
|
|
3
|
+
import { sentSecretsForRequest } from "./sent-secrets.js";
|
|
4
|
+
import { inputRetryKeys } from "./storage/keys.js";
|
|
5
|
+
import { REQUEST_STATE_TTL_MS, MAX_INPUT_ROUNDS, invalidRequestState, requestDigest, stateObject, } from "./request-state.js";
|
|
6
|
+
import { bindDownstreamCapabilities, downstreamInputCapabilities, bindDownstreamContinuation, clearDownstreamContinuation, downstreamArgumentEcho, assertNoPrivateStateEchoes, assertDownstreamOutputSafe, } from "./downstream-input-context.js";
|
|
7
|
+
const MAX_PAYLOAD_BYTES = 64 * 1024;
|
|
8
|
+
export const MAX_RELAY_STATE_CHARS = 128 * 1024;
|
|
9
|
+
const MAX_REQUESTS = 16;
|
|
10
|
+
const PURPOSE = "connecta:downstream-input:v1";
|
|
11
|
+
const pendingInputs = new WeakMap();
|
|
12
|
+
const modernRequests = new WeakSet();
|
|
13
|
+
function withinBudget(value) {
|
|
14
|
+
try {
|
|
15
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength <= MAX_PAYLOAD_BYTES;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function namespace(connector, index) {
|
|
22
|
+
return `downstream/${connector}/${index}`;
|
|
23
|
+
}
|
|
24
|
+
function invalidInput() {
|
|
25
|
+
throw new ConnectorCallError("input_required_invalid", "The downstream returned malformed or unsafe input requests.");
|
|
26
|
+
}
|
|
27
|
+
/** Capture opaque state before any result redaction or unwrapping can alter it. */
|
|
28
|
+
export async function captureDownstreamInput(scope, connector, address, raw, secrets) {
|
|
29
|
+
if (!withinBudget(raw))
|
|
30
|
+
throw new ConnectorCallError("input_required_limit", "The downstream input payload exceeds 64 KiB.");
|
|
31
|
+
if (!stateObject(raw) ||
|
|
32
|
+
(raw.requestState !== undefined && typeof raw.requestState !== "string") ||
|
|
33
|
+
(raw.inputRequests !== undefined && !stateObject(raw.inputRequests)) ||
|
|
34
|
+
(raw.requestState === undefined && raw.inputRequests === undefined))
|
|
35
|
+
return invalidInput();
|
|
36
|
+
const entries = Object.entries(raw.inputRequests ?? {});
|
|
37
|
+
if (!entries.length && raw.requestState === undefined)
|
|
38
|
+
return invalidInput();
|
|
39
|
+
if (entries.length > MAX_REQUESTS)
|
|
40
|
+
throw new ConnectorCallError("input_required_limit", "The downstream requested more than 16 inputs.");
|
|
41
|
+
const inputRequests = Object.create(null);
|
|
42
|
+
const inputs = Object.create(null);
|
|
43
|
+
const elicitation = downstreamInputCapabilities(scope).elicitation;
|
|
44
|
+
for (const [index, [key, request]] of entries.entries()) {
|
|
45
|
+
if (!key || key.length > 256 || !stateObject(request))
|
|
46
|
+
return invalidInput();
|
|
47
|
+
if (request.method !== "elicitation/create") {
|
|
48
|
+
throw new ConnectorCallError("input_required_unsupported", "Connecta relays downstream form and URL elicitation only.");
|
|
49
|
+
}
|
|
50
|
+
if (!stateObject(request.params))
|
|
51
|
+
return invalidInput();
|
|
52
|
+
const params = request.params;
|
|
53
|
+
const mode = params.mode ?? "form";
|
|
54
|
+
if (mode !== "form" && mode !== "url")
|
|
55
|
+
return invalidInput();
|
|
56
|
+
if (!elicitation?.[mode])
|
|
57
|
+
throw new ConnectorCallError("input_required_unsupported", "The client did not declare support for the downstream elicitation kind.");
|
|
58
|
+
if (mode === "url") {
|
|
59
|
+
if (typeof params.message !== "string" || typeof params.url !== "string")
|
|
60
|
+
return invalidInput();
|
|
61
|
+
try {
|
|
62
|
+
const url = new URL(params.url);
|
|
63
|
+
// An opaque downstream nonce is retained. Credentials are never put
|
|
64
|
+
// in browser URLs; refusal preserves semantics instead of rewriting it.
|
|
65
|
+
if (url.protocol !== "https:" ||
|
|
66
|
+
url.username ||
|
|
67
|
+
url.password ||
|
|
68
|
+
[...params.url].some((char) => char.charCodeAt(0) <= 0x20 || char.charCodeAt(0) === 0x7f) ||
|
|
69
|
+
secrets.containsUrl(params.url) ||
|
|
70
|
+
secrets.redact(params.url) !== params.url)
|
|
71
|
+
return invalidInput();
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return invalidInput();
|
|
75
|
+
}
|
|
76
|
+
inputRequests[namespace(connector, index)] = inputRequired.elicitUrl({
|
|
77
|
+
message: `Downstream ${connector}: ${params.message}`,
|
|
78
|
+
url: params.url,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const parsed = await specTypeSchemas.ElicitRequestFormParams["~standard"].validate(params);
|
|
83
|
+
if (parsed.issues)
|
|
84
|
+
return invalidInput();
|
|
85
|
+
// A schema controls the accepted answer. Refuse redaction changes rather
|
|
86
|
+
// than asking the host to submit different field names or enum values.
|
|
87
|
+
if (secrets.redact(parsed.value.requestedSchema) !== parsed.value.requestedSchema)
|
|
88
|
+
return invalidInput();
|
|
89
|
+
inputRequests[namespace(connector, index)] = inputRequired.elicit({
|
|
90
|
+
message: `Downstream ${connector}: ${parsed.value.message}`,
|
|
91
|
+
requestedSchema: parsed.value.requestedSchema,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
inputs[namespace(connector, index)] = { key, mode };
|
|
95
|
+
}
|
|
96
|
+
// Opaque state is private even when a downstream echoes it in a prompt,
|
|
97
|
+
// URL, or schema. Keep the continuation intact and refuse the public prompt.
|
|
98
|
+
assertDownstreamOutputSafe(scope, inputRequests);
|
|
99
|
+
assertNoPrivateStateEchoes(inputRequests, raw.requestState !== undefined ? [raw.requestState] : []);
|
|
100
|
+
if (pendingInputs.has(scope))
|
|
101
|
+
return invalidInput();
|
|
102
|
+
pendingInputs.set(scope, {
|
|
103
|
+
connector,
|
|
104
|
+
address,
|
|
105
|
+
...(raw.requestState !== undefined ? { requestState: raw.requestState } : {}),
|
|
106
|
+
inputRequests,
|
|
107
|
+
inputs,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function failure(code, message) {
|
|
111
|
+
const structuredContent = { ok: false, error: { code, message, retryable: false } };
|
|
112
|
+
return { content: [{ type: "text", text: JSON.stringify(structuredContent) }], structuredContent, isError: true };
|
|
113
|
+
}
|
|
114
|
+
/** MCP-only continuation coordination. No downstream prompt enters operator records. */
|
|
115
|
+
export class DownstreamElicitation {
|
|
116
|
+
options;
|
|
117
|
+
constructor(options) {
|
|
118
|
+
this.options = options;
|
|
119
|
+
}
|
|
120
|
+
bind(context) {
|
|
121
|
+
const envelope = context.mcpReq.envelope;
|
|
122
|
+
const declared = envelope?.[CLIENT_CAPABILITIES_META_KEY];
|
|
123
|
+
const elicitation = stateObject(declared) ? declared.elicitation : undefined;
|
|
124
|
+
const modern = envelope?.[PROTOCOL_VERSION_META_KEY] === "2026-07-28";
|
|
125
|
+
if (modern)
|
|
126
|
+
modernRequests.add(this.options.requestScope);
|
|
127
|
+
else
|
|
128
|
+
modernRequests.delete(this.options.requestScope);
|
|
129
|
+
bindDownstreamCapabilities(this.options.requestScope, modern && stateObject(elicitation)
|
|
130
|
+
? {
|
|
131
|
+
elicitation: {
|
|
132
|
+
...(stateObject(elicitation.form) || Object.keys(elicitation).length === 0 ? { form: {} } : {}),
|
|
133
|
+
...(stateObject(elicitation.url) ? { url: {} } : {}),
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
: {});
|
|
137
|
+
}
|
|
138
|
+
async verify(wrapper, context) {
|
|
139
|
+
const { vault, registry, principal, endpoint } = this.options;
|
|
140
|
+
if (!vault?.open ||
|
|
141
|
+
!principal ||
|
|
142
|
+
typeof wrapper.connector !== "string" ||
|
|
143
|
+
!registry.getConnector(wrapper.connector) ||
|
|
144
|
+
typeof wrapper.sealed !== "string" ||
|
|
145
|
+
wrapper.sealed.length > MAX_RELAY_STATE_CHARS)
|
|
146
|
+
return invalidRequestState();
|
|
147
|
+
let state;
|
|
148
|
+
try {
|
|
149
|
+
state = JSON.parse(await vault.open(wrapper.connector, PURPOSE, wrapper.sealed));
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return invalidRequestState();
|
|
153
|
+
}
|
|
154
|
+
if (!stateObject(state) ||
|
|
155
|
+
state.version !== 2 ||
|
|
156
|
+
state.kind !== "downstream" ||
|
|
157
|
+
state.principal !== principal ||
|
|
158
|
+
state.endpoint !== endpoint ||
|
|
159
|
+
state.connector !== wrapper.connector ||
|
|
160
|
+
typeof state.address !== "string" ||
|
|
161
|
+
typeof state.target !== "string" ||
|
|
162
|
+
!state.target.startsWith(`${state.connector}.`) ||
|
|
163
|
+
!["call_tool", "call_destructive_tool"].includes(String(state.tool)) ||
|
|
164
|
+
typeof state.digest !== "string" ||
|
|
165
|
+
!/^[a-f0-9]{64}$/.test(state.digest) ||
|
|
166
|
+
!Number.isSafeInteger(state.expiresAt) ||
|
|
167
|
+
state.expiresAt <= Date.now() ||
|
|
168
|
+
state.expiresAt > Date.now() + REQUEST_STATE_TTL_MS ||
|
|
169
|
+
!Number.isInteger(state.round) ||
|
|
170
|
+
state.round < 1 ||
|
|
171
|
+
state.round > MAX_INPUT_ROUNDS ||
|
|
172
|
+
typeof state.nonce !== "string" ||
|
|
173
|
+
!/^[a-f0-9-]{36}$/.test(state.nonce) ||
|
|
174
|
+
(state.requestState !== undefined && typeof state.requestState !== "string") ||
|
|
175
|
+
!Array.isArray(state.previousStates) ||
|
|
176
|
+
state.previousStates.length >= state.round ||
|
|
177
|
+
!state.previousStates.every((value) => typeof value === "string") ||
|
|
178
|
+
!withinBudget([...state.previousStates, ...(state.requestState !== undefined ? [state.requestState] : [])]) ||
|
|
179
|
+
!stateObject(state.inputs) ||
|
|
180
|
+
Object.keys(state.inputs).length > MAX_REQUESTS ||
|
|
181
|
+
!Object.entries(state.inputs).every(([wireKey, binding], index) => wireKey === namespace(wrapper.connector, index) &&
|
|
182
|
+
stateObject(binding) &&
|
|
183
|
+
typeof binding.key === "string" &&
|
|
184
|
+
binding.key.length > 0 &&
|
|
185
|
+
binding.key.length <= 256 &&
|
|
186
|
+
(binding.mode === "url" || binding.mode === "form")) ||
|
|
187
|
+
context.mcpReq.method !== "tools/call" ||
|
|
188
|
+
context.http?.req?.headers.get("Mcp-Name") !== state.tool)
|
|
189
|
+
return invalidRequestState();
|
|
190
|
+
return state;
|
|
191
|
+
}
|
|
192
|
+
async resume(state, tool, args, context, operation) {
|
|
193
|
+
if (state.tool !== tool || state.address !== args.address || state.digest !== (await requestDigest(tool, args)))
|
|
194
|
+
return failure("invalid_request_state", "Invalid or expired requestState");
|
|
195
|
+
const responses = Object.create(null);
|
|
196
|
+
const elicitation = downstreamInputCapabilities(this.options.requestScope).elicitation;
|
|
197
|
+
for (const [wireKey, { key, mode }] of Object.entries(state.inputs)) {
|
|
198
|
+
if (!elicitation?.[mode])
|
|
199
|
+
return failure("input_required_unsupported", "The client did not declare support for this continuation's input kind.");
|
|
200
|
+
const response = context.mcpReq.inputResponses?.[wireKey];
|
|
201
|
+
if (response === undefined)
|
|
202
|
+
continue;
|
|
203
|
+
const parsed = await specTypeSchemas.ElicitResult["~standard"].validate(response);
|
|
204
|
+
if (parsed.issues)
|
|
205
|
+
throw new ProtocolError(INVALID_PARAMS, "Invalid downstream input response", {
|
|
206
|
+
reason: "invalid_input_response",
|
|
207
|
+
});
|
|
208
|
+
// URL acknowledgements and decline/cancel never carry form data.
|
|
209
|
+
responses[key] =
|
|
210
|
+
mode === "url" || parsed.value.action !== "accept" ? { action: parsed.value.action } : parsed.value;
|
|
211
|
+
}
|
|
212
|
+
if (!withinBudget(responses))
|
|
213
|
+
throw new ProtocolError(INVALID_PARAMS, "Input responses exceed 64 KiB", { reason: "invalid_input_response" });
|
|
214
|
+
const storage = this.options.registry.contextFor(state.connector, this.options.endpoint, this.options.requestScope).storage;
|
|
215
|
+
// Claim before any downstream dispatch, including decline/cancel. A failed
|
|
216
|
+
// or ambiguous continuation must not leave a reusable write permission.
|
|
217
|
+
if (!(await storage.compareAndSet(inputRetryKeys.used(state.nonce), null, "used", {
|
|
218
|
+
ttlSeconds: REQUEST_STATE_TTL_MS / 1000,
|
|
219
|
+
})))
|
|
220
|
+
return invalidRequestState();
|
|
221
|
+
bindDownstreamContinuation(this.options.requestScope, {
|
|
222
|
+
connector: state.connector,
|
|
223
|
+
address: state.target,
|
|
224
|
+
write: state.tool === "call_destructive_tool",
|
|
225
|
+
privateStates: [...state.previousStates, ...(state.requestState !== undefined ? [state.requestState] : [])],
|
|
226
|
+
input: {
|
|
227
|
+
...(state.requestState !== undefined ? { requestState: state.requestState } : {}),
|
|
228
|
+
inputResponses: responses,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
try {
|
|
232
|
+
const result = await this.finish(tool, args, context, await operation(), state);
|
|
233
|
+
if (!result.isError || !stateObject(result.structuredContent))
|
|
234
|
+
return result;
|
|
235
|
+
const error = result.structuredContent.error;
|
|
236
|
+
if (!stateObject(error))
|
|
237
|
+
return result;
|
|
238
|
+
// The nonce is spent even when the downstream failure would normally
|
|
239
|
+
// allow a retry. Preserve write reconciliation and auth prerequisites.
|
|
240
|
+
const echoed = downstreamArgumentEcho(this.options.requestScope);
|
|
241
|
+
const structuredContent = {
|
|
242
|
+
...result.structuredContent,
|
|
243
|
+
error: {
|
|
244
|
+
...error,
|
|
245
|
+
retryable: false,
|
|
246
|
+
...(state.tool === "call_tool" && error.nextAction === undefined && error.retry === undefined
|
|
247
|
+
? {
|
|
248
|
+
nextAction: {
|
|
249
|
+
tool: state.tool,
|
|
250
|
+
arguments: { address: state.address, ...echoed },
|
|
251
|
+
purpose: "Re-issue the original direct call without requestState or inputResponses to start a fresh input round. Do not resend this continuation." +
|
|
252
|
+
(echoed.argsRedacted
|
|
253
|
+
? " Re-send the original arguments; sensitive fields are omitted."
|
|
254
|
+
: args.args !== undefined && !("args" in echoed)
|
|
255
|
+
? " Use the exact arguments you sent; they could not be echoed safely."
|
|
256
|
+
: ""),
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
: {}),
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
return { ...result, structuredContent, content: [{ type: "text", text: JSON.stringify(structuredContent) }] };
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
clearDownstreamContinuation(this.options.requestScope);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async finish(tool, args, context, result, previous) {
|
|
269
|
+
const scope = this.options.requestScope;
|
|
270
|
+
const pending = pendingInputs.get(scope);
|
|
271
|
+
pendingInputs.delete(scope);
|
|
272
|
+
if (!pending)
|
|
273
|
+
return result;
|
|
274
|
+
const { vault, principal, endpoint } = this.options;
|
|
275
|
+
if (!modernRequests.has(scope) ||
|
|
276
|
+
!vault?.requestStateKey ||
|
|
277
|
+
!vault.seal ||
|
|
278
|
+
!vault.open ||
|
|
279
|
+
!principal ||
|
|
280
|
+
!["call_tool", "call_destructive_tool"].includes(tool))
|
|
281
|
+
return failure("input_required_unsupported", "Downstream input requires an authenticated direct call and a sealing vault.");
|
|
282
|
+
if ((previous?.round ?? 0) >= MAX_INPUT_ROUNDS)
|
|
283
|
+
return failure("input_required_round_limit", "The downstream still requires input after three rounds. Start a new direct request.");
|
|
284
|
+
const previousStates = [
|
|
285
|
+
...new Set([
|
|
286
|
+
...(previous?.previousStates ?? []),
|
|
287
|
+
...(previous?.requestState !== undefined ? [previous.requestState] : []),
|
|
288
|
+
]),
|
|
289
|
+
].filter((value) => value !== pending.requestState);
|
|
290
|
+
if (!withinBudget([...previousStates, ...(pending.requestState !== undefined ? [pending.requestState] : [])])) {
|
|
291
|
+
return failure("input_required_limit", "The downstream private state history exceeds 64 KiB.");
|
|
292
|
+
}
|
|
293
|
+
const state = {
|
|
294
|
+
version: 2,
|
|
295
|
+
kind: "downstream",
|
|
296
|
+
principal,
|
|
297
|
+
endpoint,
|
|
298
|
+
connector: pending.connector,
|
|
299
|
+
address: String(args.address),
|
|
300
|
+
target: pending.address,
|
|
301
|
+
tool: tool,
|
|
302
|
+
digest: await requestDigest(tool, args),
|
|
303
|
+
round: (previous?.round ?? 0) + 1,
|
|
304
|
+
expiresAt: previous?.expiresAt ?? Date.now() + REQUEST_STATE_TTL_MS,
|
|
305
|
+
nonce: crypto.randomUUID(),
|
|
306
|
+
...(pending.requestState !== undefined ? { requestState: pending.requestState } : {}),
|
|
307
|
+
previousStates,
|
|
308
|
+
inputs: pending.inputs,
|
|
309
|
+
};
|
|
310
|
+
const requestState = await this.options.mint({
|
|
311
|
+
version: 2,
|
|
312
|
+
kind: "downstream",
|
|
313
|
+
connector: pending.connector,
|
|
314
|
+
sealed: await vault.seal(pending.connector, PURPOSE, JSON.stringify(state)),
|
|
315
|
+
}, context);
|
|
316
|
+
if (requestState.length > MAX_RELAY_STATE_CHARS)
|
|
317
|
+
return failure("input_required_limit", "The sealed downstream state exceeds 128 KiB.");
|
|
318
|
+
// Opaque echoes and mutable schemas/URLs were refused during capture.
|
|
319
|
+
// Downstream prompt messages are prose under the payload rules.
|
|
320
|
+
return sentSecretsForRequest(scope).redact(inputRequired({
|
|
321
|
+
...(Object.keys(pending.inputRequests).length ? { inputRequests: pending.inputRequests } : {}),
|
|
322
|
+
requestState,
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ExecutorAdmissionErrorCode } from "./executor-admission.js";
|
|
2
|
+
import type { JsonSchema } from "./types.js";
|
|
1
3
|
/**
|
|
2
4
|
* Machine-readable classification of a failed connector tool call.
|
|
3
5
|
*
|
|
@@ -5,7 +7,7 @@
|
|
|
5
7
|
* naming a cause — the rule provider conventions call H11, defined in
|
|
6
8
|
* `test/provider-conventions.test.ts`.
|
|
7
9
|
*/
|
|
8
|
-
export type ConnectorCallErrorCode = "timeout" | "auth_required" | "rate_limited" | "unavailable" | "invalid_args"
|
|
10
|
+
export type ConnectorCallErrorCode = "timeout" | "auth_required" | "downstream_oauth_required" | "oauth_revocation_failed" | "provider_permission_denied" | "rate_limited" | "unavailable" | "invalid_args"
|
|
9
11
|
/**
|
|
10
12
|
* Provider-owned absence: the caller re-addresses rather than waiting,
|
|
11
13
|
* re-authorizing, or repairing arguments, and a program looping over ids can
|
|
@@ -22,7 +24,18 @@ export type ConnectorCallErrorCode = "timeout" | "auth_required" | "rate_limited
|
|
|
22
24
|
* change, and retries with the base `current` reports — repeating the same
|
|
23
25
|
* call can never succeed, and waiting will not help either.
|
|
24
26
|
*/
|
|
25
|
-
| "conflict" | "input_required_unsupported" | "connector_call_failed";
|
|
27
|
+
| "conflict" | "resource_template_ambiguous" | "resource_match_budget_exceeded" | "input_required_unsupported" | "input_required_invalid" | "input_required_limit" | "connector_call_failed";
|
|
28
|
+
/**
|
|
29
|
+
* Every classification code connecta assigns: a connector's typed codes, the
|
|
30
|
+
* executor's admission codes, and the refusals connecta frames itself.
|
|
31
|
+
* Operator records and activity rows carry a code only when it is one of
|
|
32
|
+
* these (INV-6), so a code forwarded from a downstream is never recorded.
|
|
33
|
+
* Adding a member fails typecheck until {@link CLASSIFICATION_CODE_TABLE}
|
|
34
|
+
* lists it.
|
|
35
|
+
*/
|
|
36
|
+
export type ClassificationCode = ConnectorCallErrorCode | ExecutorAdmissionErrorCode | "cancelled" | "unknown_address" | "unknown_tool" | "ambiguous_tool_alias" | "catalog_lookup_failed" | "result_too_large" | "destructive_tool_requires_approval" | "auth_replay_refused" | "write_outcome_unknown" | "result_processing_failed" | "budget_exceeded" | "executor_failed" | "program_error";
|
|
37
|
+
/** `value` when it is a code connecta assigns, else undefined. */
|
|
38
|
+
export declare function classificationCode(value: unknown): ClassificationCode | undefined;
|
|
26
39
|
/** One bounded, payload-free explanation of an input-schema mismatch. */
|
|
27
40
|
export interface ArgumentValidationIssue {
|
|
28
41
|
/** JSON Pointer into the submitted arguments; "/" means the root value. */
|
|
@@ -37,6 +50,26 @@ export interface ArgumentValidationDetails {
|
|
|
37
50
|
/** More findings existed but were omitted from the bounded response. */
|
|
38
51
|
truncated?: true;
|
|
39
52
|
}
|
|
53
|
+
/** Agent-only facts from the published schema, never operator record fields. */
|
|
54
|
+
export interface ArgumentRepairDetails {
|
|
55
|
+
acceptedKeys?: string[];
|
|
56
|
+
issues: Array<{
|
|
57
|
+
path: string;
|
|
58
|
+
receivedType: string;
|
|
59
|
+
acceptedKeys?: string[];
|
|
60
|
+
enumValues?: unknown[];
|
|
61
|
+
bounds?: Record<string, number>;
|
|
62
|
+
}>;
|
|
63
|
+
conditionalRequirements?: Array<{
|
|
64
|
+
path: string;
|
|
65
|
+
condition: unknown;
|
|
66
|
+
required: string[];
|
|
67
|
+
}>;
|
|
68
|
+
/** Synthesized from the schema and checked by the same validator. */
|
|
69
|
+
example?: unknown;
|
|
70
|
+
exampleUnavailable?: string;
|
|
71
|
+
truncated?: true;
|
|
72
|
+
}
|
|
40
73
|
export declare const MAX_ARGUMENT_VALIDATION_ISSUES = 3;
|
|
41
74
|
/**
|
|
42
75
|
* Clamp a string to a UTF-8 byte budget, appending `…` when it clipped.
|
|
@@ -45,33 +78,72 @@ export declare const MAX_ARGUMENT_VALIDATION_ISSUES = 3;
|
|
|
45
78
|
*/
|
|
46
79
|
export declare function boundedEchoText(value: string, maxBytes?: number): string;
|
|
47
80
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* ones — there is nothing honest to put in the field.
|
|
81
|
+
* Snapshot the caller's arguments after bounded schema-aware writeOnly omission,
|
|
82
|
+
* then apply the shared byte budget without clipping. Unresolved sensitivity
|
|
83
|
+
* omits the entire echo. argsRedacted marks partial or withheld arguments as
|
|
84
|
+
* reconciliation context; any retry must use the caller's original arguments.
|
|
53
85
|
*/
|
|
54
|
-
export declare function echoedCallArgs(args: unknown): {
|
|
86
|
+
export declare function echoedCallArgs(args: unknown, schema?: JsonSchema): {
|
|
55
87
|
args?: unknown;
|
|
88
|
+
argsRedacted?: true;
|
|
56
89
|
};
|
|
57
90
|
/** Optional transport diagnostics; never a URL path or raw runtime message. */
|
|
58
91
|
interface UnavailableDetails {
|
|
92
|
+
/** Guest program diagnostics; returned to the agent, never operator records. */
|
|
93
|
+
name?: string;
|
|
94
|
+
line?: number | null;
|
|
95
|
+
hint?: string;
|
|
96
|
+
/** Host-measured call deadline diagnostics; operation may contain caller text. */
|
|
97
|
+
operation?: string;
|
|
98
|
+
stage?: string;
|
|
99
|
+
elapsedMs?: number;
|
|
100
|
+
deadlineMs?: number;
|
|
59
101
|
/** HTTP(S) origin only, at most 253 UTF-8 bytes. */
|
|
60
102
|
host?: string;
|
|
61
103
|
/** Validated network errno or `timeout`, at most 32 bytes. */
|
|
62
104
|
code?: string;
|
|
63
105
|
}
|
|
106
|
+
/** The network errnos `networkErrorCode` reports, and `timeout`. */
|
|
107
|
+
export declare const NETWORK_ERROR_CODES: ReadonlySet<string>;
|
|
64
108
|
/** Runtime fields only: provider prose cannot supply an errno or a deadline. */
|
|
65
109
|
export declare function networkErrorCode(error: unknown): string | undefined;
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Use at a fetch boundary where the destination and transport failure are
|
|
112
|
+
* known. The runtime's error is read for its errno and dropped, never kept as
|
|
113
|
+
* `cause`: its message and cause chain can quote the URL, headers, or body of
|
|
114
|
+
* the request that failed, and a logger that renders an error renders its
|
|
115
|
+
* cause. The origin and errno in `details` are what survives.
|
|
116
|
+
*
|
|
117
|
+
* The one failure kept is the request's own abort reason, when that is what
|
|
118
|
+
* the fetch rejected with: the caller wrote it, not the downstream, and it is
|
|
119
|
+
* how a cancelled request tells its reason from a network failure.
|
|
120
|
+
*/
|
|
121
|
+
export declare function unavailableCallError(failure: unknown, host?: string, message?: string, signal?: AbortSignal): ConnectorCallError;
|
|
122
|
+
/**
|
|
123
|
+
* A failure told in connecta's words because the original's text came from a
|
|
124
|
+
* downstream or authorization server, which can echo what the request carried
|
|
125
|
+
* or plant whatever it likes. There is no `cause`: the original is dropped
|
|
126
|
+
* whole, never masked.
|
|
127
|
+
*
|
|
128
|
+
* It is classified exactly as the original would have been. That verdict is
|
|
129
|
+
* read from the original once, here, and kept as two flags, so withholding
|
|
130
|
+
* the text moves no failure between `timeout`, retryable, and the caller's
|
|
131
|
+
* fallback code. Never wrap a `ConnectorCallError`: it already speaks in
|
|
132
|
+
* connecta's words and carries its own code.
|
|
133
|
+
*/
|
|
134
|
+
export declare class WithheldTextError extends Error {
|
|
135
|
+
/** The original read as a timeout. */
|
|
136
|
+
readonly timeout: boolean;
|
|
137
|
+
readonly retryable: boolean;
|
|
138
|
+
constructor(message: string, original: unknown);
|
|
139
|
+
}
|
|
140
|
+
/** Agent-visible recovery class for downstream credential failures. */
|
|
69
141
|
export type AuthRecoveryMode = "oauth" | "operator_config" | "unavailable";
|
|
70
142
|
/**
|
|
71
143
|
* Throw from `Connector.callTool` (or anything beneath it) to classify a
|
|
72
|
-
* failure exactly. Untyped errors
|
|
73
|
-
*
|
|
74
|
-
*
|
|
144
|
+
* failure exactly. Untyped errors are non-retryable unless a runtime errno
|
|
145
|
+
* or abort identifies a transport failure. This class supplies the connector's
|
|
146
|
+
* reviewed verdict. `retryable` defaults per
|
|
75
147
|
* code (timeout, rate_limited, and unavailable retry; the rest do not) and may
|
|
76
148
|
* be overridden.
|
|
77
149
|
*
|
|
@@ -93,6 +165,7 @@ export declare class ConnectorCallError extends Error {
|
|
|
93
165
|
readonly retryAfterMs: number | undefined;
|
|
94
166
|
/** Bounded schema findings for `invalid_args`; never submitted values. */
|
|
95
167
|
readonly validation: ArgumentValidationDetails | undefined;
|
|
168
|
+
readonly repair: ArgumentRepairDetails | undefined;
|
|
96
169
|
/** Sanitized transport diagnostics for `unavailable` only. */
|
|
97
170
|
readonly details: UnavailableDetails | undefined;
|
|
98
171
|
/**
|
|
@@ -105,12 +178,30 @@ export declare class ConnectorCallError extends Error {
|
|
|
105
178
|
retryAfterMs?: number;
|
|
106
179
|
cause?: unknown;
|
|
107
180
|
validation?: ArgumentValidationDetails;
|
|
181
|
+
repair?: ArgumentRepairDetails;
|
|
108
182
|
details?: UnavailableDetails;
|
|
109
183
|
current?: Readonly<Record<string, number>>;
|
|
110
184
|
});
|
|
111
185
|
}
|
|
186
|
+
/** Secret-free payload returned by authorize_connector and auth failures. */
|
|
187
|
+
export interface AuthorizationHandoff {
|
|
188
|
+
connector: string;
|
|
189
|
+
recovery: AuthRecoveryMode;
|
|
190
|
+
message?: string;
|
|
191
|
+
status?: "auth_required";
|
|
192
|
+
authorizationUrl?: string;
|
|
193
|
+
operatorUrl?: string;
|
|
194
|
+
instructions?: string;
|
|
195
|
+
credential?: {
|
|
196
|
+
label: string;
|
|
197
|
+
fields: {
|
|
198
|
+
name: string;
|
|
199
|
+
guidance?: string;
|
|
200
|
+
}[];
|
|
201
|
+
};
|
|
202
|
+
}
|
|
112
203
|
/** The `error` object surfaced in value-mode call results and rejected promises. */
|
|
113
|
-
export interface CallErrorDetails {
|
|
204
|
+
export interface CallErrorDetails extends Partial<AuthorizationHandoff> {
|
|
114
205
|
/** Sanitized transport diagnostics, absent when the runtime supplies none. */
|
|
115
206
|
details?: UnavailableDetails;
|
|
116
207
|
code: string;
|
|
@@ -120,6 +211,7 @@ export interface CallErrorDetails {
|
|
|
120
211
|
retryAfterMs?: number;
|
|
121
212
|
/** Bounded input-schema findings; paths and expectations, never values. */
|
|
122
213
|
validation?: ArgumentValidationDetails;
|
|
214
|
+
repair?: ArgumentRepairDetails;
|
|
123
215
|
/** On a `conflict`: where each thing the write touched stands now. */
|
|
124
216
|
current?: Readonly<Record<string, number>>;
|
|
125
217
|
/** Connector whose failed operation needs recovery. */
|
|
@@ -128,6 +220,8 @@ export interface CallErrorDetails {
|
|
|
128
220
|
operation?: string;
|
|
129
221
|
/** Which safe recovery path `authorize_connector` will return. */
|
|
130
222
|
recovery?: AuthRecoveryMode;
|
|
223
|
+
/** An auth failure followed a write's downstream send. Reconcile before retry. */
|
|
224
|
+
reconciliationRequired?: true;
|
|
131
225
|
/** The single model-facing entry point for every credential class. */
|
|
132
226
|
nextAction?: {
|
|
133
227
|
tool: "authorize_connector";
|
|
@@ -144,16 +238,17 @@ export interface CallErrorDetails {
|
|
|
144
238
|
};
|
|
145
239
|
purpose: string;
|
|
146
240
|
} | {
|
|
147
|
-
tool: "call_destructive_tool";
|
|
241
|
+
tool: "call_tool" | "call_destructive_tool";
|
|
148
242
|
arguments: {
|
|
149
243
|
address: string;
|
|
150
244
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* routed into a human approval prompt would describe a call nobody made.
|
|
245
|
+
* A schema-filtered snapshot within the shared byte budget, never
|
|
246
|
+
* clipped. With argsRedacted, this is partial reconciliation context.
|
|
247
|
+
* If absent or redacted, retry only with the original arguments.
|
|
155
248
|
*/
|
|
156
249
|
args?: unknown;
|
|
250
|
+
/** Sensitive fields were omitted, or sensitivity could not be resolved. */
|
|
251
|
+
argsRedacted?: true;
|
|
157
252
|
};
|
|
158
253
|
purpose: string;
|
|
159
254
|
} | {
|
|
@@ -175,19 +270,25 @@ export interface CallErrorDetails {
|
|
|
175
270
|
addresses: string[];
|
|
176
271
|
purpose: string;
|
|
177
272
|
};
|
|
273
|
+
/** Configured connectors in this request's registry view only. */
|
|
274
|
+
configuredConnectors?: string[];
|
|
275
|
+
/**
|
|
276
|
+
* Agent-only uncertain write, never copied to an operator record. argsRedacted
|
|
277
|
+
* marks partial or withheld schema-filtered arguments; argsOmitted means args
|
|
278
|
+
* is absent. Reconciliation and any explicit retry require the original input.
|
|
279
|
+
*/
|
|
280
|
+
uncertainCall?: {
|
|
281
|
+
address: string;
|
|
282
|
+
args?: unknown;
|
|
283
|
+
argsOmitted?: true;
|
|
284
|
+
argsRedacted?: true;
|
|
285
|
+
};
|
|
178
286
|
/** Explicit retry guidance; recovery never retries or mutates by itself. */
|
|
179
287
|
retry?: string;
|
|
180
288
|
}
|
|
181
|
-
/**
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
export declare function framingError(code: string, message: string): CallErrorDetails;
|
|
186
|
-
/**
|
|
187
|
-
* Classify a value thrown by a connector call. A `ConnectorCallError` is
|
|
188
|
-
* authoritative; anything else falls back to the historical message-text
|
|
189
|
-
* heuristic.
|
|
190
|
-
*/
|
|
191
|
-
export declare function classifyCallError(err: unknown, fallbackCode?: string): CallErrorDetails;
|
|
289
|
+
/** Retryability of connecta framing depends only on its code. */
|
|
290
|
+
export declare function framingError(code: ClassificationCode, message: string): CallErrorDetails;
|
|
291
|
+
/** Typed connector verdicts and runtime facts only. Prose never changes a verdict. */
|
|
292
|
+
export declare function classifyCallError(err: unknown, fallbackCode?: ClassificationCode): CallErrorDetails;
|
|
192
293
|
export declare function msg(err: unknown): string;
|
|
193
294
|
export {};
|