@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.
Files changed (332) hide show
  1. package/CHANGELOG.md +855 -5
  2. package/README.md +125 -116
  3. package/bin/connecta.mjs +121 -139
  4. package/bin/meta-tool-names.d.mts +2 -0
  5. package/bin/meta-tool-names.mjs +9 -0
  6. package/bin/version.mjs +3 -0
  7. package/dist/access-tokens.js +53 -63
  8. package/dist/activity-request.js +8 -0
  9. package/dist/activity.d.ts +76 -42
  10. package/dist/activity.js +110 -42
  11. package/dist/argument-redaction.js +230 -0
  12. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  13. package/dist/auth/clerk-sdk/client.js +5 -0
  14. package/dist/auth/clerk-sdk/generated.js +1845 -0
  15. package/dist/auth/clerk-transport.js +19 -0
  16. package/dist/auth/clerk.d.ts +17 -7
  17. package/dist/auth/clerk.js +189 -107
  18. package/dist/auth/cloudflare-access.js +25 -26
  19. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  20. package/dist/auth/downstream-client-metadata.js +133 -0
  21. package/dist/auth/downstream-oauth.js +1584 -1580
  22. package/dist/auth/downstream-revocation.js +106 -0
  23. package/dist/auth/oauth-token-response.js +231 -0
  24. package/dist/auth/oauth-v2-migration.js +116 -0
  25. package/dist/auth/static-oauth.js +93 -71
  26. package/dist/auth-elicitation.js +288 -0
  27. package/dist/authorization-handoff.js +111 -0
  28. package/dist/branding.d.ts +8 -16
  29. package/dist/branding.js +26 -59
  30. package/dist/byte-read-response.js +39 -0
  31. package/dist/call-admission.js +12 -16
  32. package/dist/catalog-cache.js +739 -0
  33. package/dist/catalog-drift.d.ts +50 -15
  34. package/dist/catalog-drift.js +365 -128
  35. package/dist/catalog-fingerprint.js +2 -4
  36. package/dist/catalog-freshness.js +13 -0
  37. package/dist/catalog-limits.js +2 -2
  38. package/dist/catalog-service.js +409 -225
  39. package/dist/catalog.js +43 -84
  40. package/dist/concurrency.js +3 -4
  41. package/dist/config-defaults.d.ts +39 -0
  42. package/dist/config-defaults.js +46 -0
  43. package/dist/config-schema.d.ts +181 -0
  44. package/dist/config-schema.js +435 -0
  45. package/dist/config-value-sources.js +102 -0
  46. package/dist/config.d.ts +633 -0
  47. package/dist/config.js +508 -0
  48. package/dist/connector-access.d.ts +1 -0
  49. package/dist/connector-access.js +13 -15
  50. package/dist/connector-caller.d.ts +8 -0
  51. package/dist/connector-caller.js +15 -6
  52. package/dist/connector-scope.js +1 -1
  53. package/dist/connectors/api-connector.d.ts +59 -7
  54. package/dist/connectors/api-connector.js +106 -19
  55. package/dist/connectors/api.js +4 -1
  56. package/dist/connectors/guarded-fetch.d.ts +144 -0
  57. package/dist/connectors/guarded-fetch.js +87 -38
  58. package/dist/connectors/negotiation-cache.js +46 -0
  59. package/dist/connectors/option-shapes.js +27 -0
  60. package/dist/connectors/remote-mcp.d.ts +30 -4
  61. package/dist/connectors/remote-mcp.js +1774 -320
  62. package/dist/connectors/resource-uri.js +335 -0
  63. package/dist/credential-contract.d.ts +10 -0
  64. package/dist/credential-rules.js +1 -3
  65. package/dist/credential-url.js +108 -0
  66. package/dist/credentials.d.ts +6 -1
  67. package/dist/credentials.js +27 -18
  68. package/dist/d1.d.ts +130 -0
  69. package/dist/d1.js +290 -0
  70. package/dist/describe-config.d.ts +165 -0
  71. package/dist/describe-config.js +315 -0
  72. package/dist/described.js +106 -0
  73. package/dist/downstream-input-context.js +128 -0
  74. package/dist/downstream-input.js +325 -0
  75. package/dist/errors.d.ts +132 -31
  76. package/dist/errors.js +175 -87
  77. package/dist/execute.js +798 -318
  78. package/dist/executor-admission.d.ts +1 -0
  79. package/dist/executor-admission.js +11 -15
  80. package/dist/executor-contract.d.ts +20 -0
  81. package/dist/executor-contract.js +48 -0
  82. package/dist/executor-result.js +30 -13
  83. package/dist/executors/quickjs-child.js +15 -9
  84. package/dist/executors/quickjs-protocol.js +5 -6
  85. package/dist/executors/quickjs-runtime.js +152 -59
  86. package/dist/executors/quickjs.js +49 -39
  87. package/dist/favicon.js +1 -2
  88. package/dist/fix-prompt.js +1 -3
  89. package/dist/guest-runtime.js +238 -0
  90. package/dist/guest-types.d.ts +218 -0
  91. package/dist/guest-types.js +1 -0
  92. package/dist/html-security.js +24 -0
  93. package/dist/identity.js +1 -3
  94. package/dist/inbound-credential.js +52 -0
  95. package/dist/index.d.ts +24 -256
  96. package/dist/index.js +65 -478
  97. package/dist/invocation-auth.js +69 -0
  98. package/dist/invocation.js +262 -79
  99. package/dist/json-schema.d.ts +1 -1
  100. package/dist/mcp-client-context.js +35 -0
  101. package/dist/mcp-result.js +11 -7
  102. package/dist/meta-output.js +61 -0
  103. package/dist/meta-tool-names.d.ts +2 -0
  104. package/dist/meta-tool-names.js +3 -0
  105. package/dist/meta-tools.js +419 -272
  106. package/dist/module-contracts.d.ts +8 -23
  107. package/dist/node.d.ts +0 -2
  108. package/dist/node.js +2 -2
  109. package/dist/oauth-callback-outcome.js +1 -3
  110. package/dist/oauth-handoff.js +194 -0
  111. package/dist/oauth-sealing.js +20 -0
  112. package/dist/operator-record.d.ts +137 -0
  113. package/dist/operator-record.js +420 -0
  114. package/dist/operator-ui/assets.js +31 -0
  115. package/dist/operator-ui/contract.d.ts +72 -0
  116. package/dist/operator-ui/contract.js +1 -0
  117. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  118. package/dist/operator-ui/generated.js +5 -4
  119. package/dist/operator-ui/model.d.ts +12 -87
  120. package/dist/operator-ui/model.js +2 -9
  121. package/dist/operator-ui/setup-commands.js +1 -3
  122. package/dist/operator-ui/view.d.ts +323 -0
  123. package/dist/operator-ui/view.js +62 -189
  124. package/dist/page-styles.js +2 -3
  125. package/dist/program-result.js +5 -0
  126. package/dist/program-source.js +14 -2
  127. package/dist/{exempt-writes.js → program-writes.js} +60 -19
  128. package/dist/provider.d.ts +142 -0
  129. package/dist/provider.js +187 -0
  130. package/dist/providers/_shared/google/result-size.js +57 -0
  131. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  132. package/dist/providers/_shared/google/service-account.js +500 -0
  133. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  134. package/dist/providers/_shared/google/workspace.js +518 -0
  135. package/dist/providers/_shared/request-token.js +83 -0
  136. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  137. package/dist/providers/basecamp/index.js +997 -0
  138. package/dist/providers/basecamp/skill.generated.js +9 -0
  139. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  140. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  141. package/dist/providers/breeze/skill.generated.js +9 -0
  142. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  143. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  144. package/dist/providers/ccb/skill.generated.js +9 -0
  145. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  146. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  147. package/dist/providers/cloudflare/skill.generated.js +10 -0
  148. package/dist/providers/docs/index.d.ts +54 -0
  149. package/dist/providers/docs/index.js +1237 -0
  150. package/dist/providers/docs/skill.generated.js +9 -0
  151. package/dist/providers/drive/index.d.ts +53 -0
  152. package/dist/providers/drive/index.js +1523 -0
  153. package/dist/providers/drive/skill.generated.js +9 -0
  154. package/dist/providers/forms/index.d.ts +54 -0
  155. package/dist/providers/forms/index.js +1217 -0
  156. package/dist/providers/forms/skill.generated.js +11 -0
  157. package/dist/providers/github/auth.d.ts +32 -0
  158. package/dist/providers/github/auth.js +361 -0
  159. package/dist/providers/github/hosted.js +218 -0
  160. package/dist/providers/github/index.d.ts +13 -0
  161. package/dist/providers/github/index.js +140 -0
  162. package/dist/providers/github/key.js +100 -0
  163. package/dist/providers/github/rest.js +321 -0
  164. package/dist/providers/github/scope.d.ts +29 -0
  165. package/dist/providers/github/scope.js +124 -0
  166. package/dist/providers/github/skill.generated.js +8 -0
  167. package/dist/providers/gmail/index.d.ts +51 -0
  168. package/dist/providers/gmail/index.js +1436 -0
  169. package/dist/providers/gmail/skill.generated.js +9 -0
  170. package/dist/providers/infisical/index.d.ts +7 -0
  171. package/dist/providers/infisical/index.js +713 -0
  172. package/dist/providers/infisical/skill.generated.js +8 -0
  173. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  174. package/dist/providers/linear/index.js +352 -0
  175. package/dist/providers/linear/skill.generated.js +8 -0
  176. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  177. package/dist/providers/mixpanel/index.js +416 -0
  178. package/dist/providers/mixpanel/skill.generated.js +9 -0
  179. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  180. package/dist/providers/{notion.js → notion/index.js} +226 -239
  181. package/dist/providers/notion/skill.generated.js +10 -0
  182. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  183. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  184. package/dist/providers/overflow/skill.generated.js +8 -0
  185. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  186. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  187. package/dist/providers/planning-center/skill.generated.js +10 -0
  188. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  189. package/dist/providers/revenuecat/index.js +557 -0
  190. package/dist/providers/revenuecat/skill.generated.js +11 -0
  191. package/dist/providers/sheets/index.d.ts +54 -0
  192. package/dist/providers/sheets/index.js +1237 -0
  193. package/dist/providers/sheets/skill.generated.js +10 -0
  194. package/dist/providers/slides/index.d.ts +53 -0
  195. package/dist/providers/slides/index.js +2645 -0
  196. package/dist/providers/slides/skill.generated.js +9 -0
  197. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  198. package/dist/providers/stripe/index.js +266 -0
  199. package/dist/providers/stripe/skill.generated.js +11 -0
  200. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  201. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  202. package/dist/providers/tithely/skill.generated.js +8 -0
  203. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  204. package/dist/providers/vercel/index.js +1038 -0
  205. package/dist/providers/vercel/mcp-ownership.js +212 -0
  206. package/dist/providers/vercel/skill.generated.js +11 -0
  207. package/dist/registry.d.ts +110 -171
  208. package/dist/registry.js +588 -927
  209. package/dist/request-state.js +21 -0
  210. package/dist/resource-read.js +103 -0
  211. package/dist/result-shapes.js +17 -49
  212. package/dist/retired-toolkits.js +1 -1
  213. package/dist/routes/access-tokens.js +3 -6
  214. package/dist/routes/activity-context.js +21 -0
  215. package/dist/routes/activity-disclosure.js +40 -0
  216. package/dist/routes/activity.js +59 -21
  217. package/dist/routes/actor-labels.js +3 -50
  218. package/dist/routes/connect.js +221 -0
  219. package/dist/routes/credentials.js +15 -27
  220. package/dist/routes/mcp.js +365 -130
  221. package/dist/routes/oauth-client-metadata.js +15 -0
  222. package/dist/routes/oauth-management.js +45 -107
  223. package/dist/routes/oauth.js +110 -57
  224. package/dist/routes/operator.js +3 -3
  225. package/dist/routes/shared.d.ts +25 -64
  226. package/dist/routes/shared.js +160 -80
  227. package/dist/routes/skills.js +30 -0
  228. package/dist/routes/ui-config.js +225 -0
  229. package/dist/routes/ui-data.js +40 -35
  230. package/dist/routes/ui.js +66 -56
  231. package/dist/runtime/admission.js +4 -2
  232. package/dist/runtime/call-admission.js +3 -1
  233. package/dist/runtime/connector-scope.js +1 -4
  234. package/dist/runtime/run.js +10 -39
  235. package/dist/runtime/services.js +12 -117
  236. package/dist/runtime/shared-read.js +1 -3
  237. package/dist/runtime/storage.js +8 -1
  238. package/dist/sent-secrets.d.ts +93 -0
  239. package/dist/sent-secrets.js +1013 -0
  240. package/dist/server.js +28 -71
  241. package/dist/skills.js +416 -174
  242. package/dist/sqlite.d.ts +48 -0
  243. package/dist/sqlite.js +181 -0
  244. package/dist/storage/keys.js +443 -0
  245. package/dist/storage/memory.js +16 -4
  246. package/dist/storage/sql.d.ts +66 -0
  247. package/dist/storage/sql.js +543 -0
  248. package/dist/timeout.js +4 -2
  249. package/dist/tool-name.js +17 -0
  250. package/dist/tool-safety.d.ts +9 -35
  251. package/dist/tool-safety.js +20 -40
  252. package/dist/types.d.ts +336 -56
  253. package/dist/types.js +0 -1
  254. package/dist/ui.d.ts +7 -19
  255. package/dist/ui.js +76 -90
  256. package/dist/url-safety.js +3 -6
  257. package/dist/usage-guide.js +146 -0
  258. package/dist/validate.d.ts +7 -0
  259. package/dist/validate.js +309 -47
  260. package/dist/version.d.ts +1 -7
  261. package/dist/version.js +3 -7
  262. package/dist/worker.d.ts +11 -0
  263. package/dist/worker.js +435 -0
  264. package/documentation/README.md +63 -0
  265. package/documentation/architecture.md +696 -231
  266. package/documentation/auth.md +1069 -546
  267. package/documentation/code-mode.md +662 -356
  268. package/documentation/deploying.md +146 -0
  269. package/documentation/integrating.md +159 -0
  270. package/documentation/meta-tools.md +570 -216
  271. package/documentation/operating.md +135 -0
  272. package/documentation/operator-tests.md +97 -0
  273. package/documentation/operator-ui.md +132 -0
  274. package/documentation/provider-migration-0.29.md +65 -0
  275. package/examples/worker/AGENTS.md +34 -12
  276. package/examples/worker/README.md +293 -255
  277. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  278. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  279. package/examples/worker/src/connecta.config.ts +151 -0
  280. package/examples/worker/src/index.ts +14 -197
  281. package/examples/worker/wrangler.jsonc +15 -35
  282. package/package.json +126 -54
  283. package/templates/node/.dockerignore +1 -2
  284. package/templates/node/.env.example +20 -21
  285. package/templates/node/AGENTS.md +21 -17
  286. package/templates/node/Dockerfile +3 -5
  287. package/templates/node/README.md +140 -58
  288. package/templates/node/docker-compose.yml +11 -12
  289. package/templates/node/package.json +4 -2
  290. package/templates/node/src/connecta.config.ts +119 -0
  291. package/templates/node/src/index.ts +9 -154
  292. package/templates/node/src/provision-token.ts +17 -0
  293. package/AGENTS.md +0 -170
  294. package/dist/artifacts/connector.d.ts +0 -58
  295. package/dist/artifacts/connector.js +0 -730
  296. package/dist/artifacts/document.js +0 -86
  297. package/dist/artifacts/guide.js +0 -140
  298. package/dist/artifacts/html-scan.js +0 -329
  299. package/dist/artifacts/json.js +0 -78
  300. package/dist/artifacts/kv-store.d.ts +0 -20
  301. package/dist/artifacts/kv-store.js +0 -140
  302. package/dist/artifacts/markdown.js +0 -478
  303. package/dist/artifacts/module.d.ts +0 -37
  304. package/dist/artifacts/module.js +0 -66
  305. package/dist/artifacts/operations.d.ts +0 -197
  306. package/dist/artifacts/operations.js +0 -1002
  307. package/dist/artifacts/refresh.d.ts +0 -55
  308. package/dist/artifacts/refresh.js +0 -236
  309. package/dist/artifacts/routes.js +0 -206
  310. package/dist/artifacts/types.d.ts +0 -214
  311. package/dist/artifacts/types.js +0 -22
  312. package/dist/artifacts/validate.d.ts +0 -62
  313. package/dist/artifacts/validate.js +0 -651
  314. package/dist/artifacts.d.ts +0 -6
  315. package/dist/artifacts.js +0 -9
  316. package/dist/auth/bearer.d.ts +0 -11
  317. package/dist/auth/bearer.js +0 -50
  318. package/dist/providers/basecamp.js +0 -445
  319. package/dist/providers/linear.js +0 -217
  320. package/dist/providers/mixpanel.js +0 -247
  321. package/dist/providers/revenuecat.js +0 -317
  322. package/dist/providers/stripe.js +0 -253
  323. package/dist/providers/vercel.js +0 -1253
  324. package/dist/storage/file.d.ts +0 -15
  325. package/dist/storage/file.js +0 -455
  326. package/ethos.md +0 -93
  327. package/examples/worker/src/cloudflare-kv.ts +0 -43
  328. package/examples/worker/src/d1-activity-row.ts +0 -155
  329. package/examples/worker/src/d1-activity.ts +0 -147
  330. package/examples/worker/src/d1-storage.ts +0 -138
  331. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  332. 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
- * `{ args }` when the caller's arguments fit the shared echo budget,
49
- * `{}` when they do not. All or nothing: a clipped echo would be a *different*
50
- * call than the one that was refused, and the routes this feeds end at a human
51
- * approving one. Unserializable arguments are treated the same way as oversized
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
- /** Use at a fetch boundary where the destination and transport failure are known. */
67
- export declare function unavailableCallError(cause: unknown, host?: string, message?: string): ConnectorCallError;
68
- /** Agent-visible recovery class attached only to `auth_required` failures. */
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 fall back to a message-text heuristic, so a
73
- * connector whose legitimate error text mentions "timeout" is misread as a
74
- * retryable timeout — this class is the escape hatch. `retryable` defaults per
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
- * The caller's own arguments, echoed only when they fit
152
- * the shared echo budget — and then whole, never clipped. Absent
153
- * means "re-send exactly what you sent": a half-copied argument object
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
- * Details for a failure connecta itself framed — an address it could not
183
- * resolve, a tool it refuses to run — rather than one a connector threw.
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 {};