@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,420 @@
1
+ // What an operator may read about a failure: log records and status text.
2
+ // Web-API only — no node: imports here.
3
+ //
4
+ // Inbound identity-provider text never enters operator logs or status, even
5
+ // when it fits a grammar. Activity retains its separate typed actor fields.
6
+ //
7
+ // Threat model. INV-6 here covers records of calls and failures: logs,
8
+ // activity rows, status and statusFor, health, doctor output, and any native
9
+ // runtime output connecta triggers (workerd's console). Text a downstream
10
+ // server, HTTP API, or client authored, and anything derived from it, never
11
+ // reaches one as arbitrary text. Self-declared client name/version facts in
12
+ // activity use the bounded ASCII grammars in activityClientFact; invalid values are
13
+ // absent at recording, SQL write/read, and operator UI boundaries. Client
14
+ // capabilities and other metadata are never recorded. Every typed field
15
+ // connecta records is checked against a closed set or a grammar. Operator-
16
+ // authored code (custom connectors, plugin `status()`, decorators) is trusted
17
+ // to follow the contract, and connecta still checks
18
+ // the typed fields it records from it; deliberately adversarial operator code
19
+ // is out of scope.
20
+ //
21
+ // Catalog metadata a connector serves is configuration the operator chose to
22
+ // load, and agents already see it: a tool name that fits MCP's tool-name
23
+ // grammar, a description, and input and output schemas may appear in the
24
+ // authenticated operator page's catalog views, and such a name may be
25
+ // logged. A name outside the grammar is withheld everywhere operator-facing,
26
+ // the page included. Names containing C0, DEL, or C1 are dropped at catalog
27
+ // intake; spaces and non-ASCII names remain callable with withheld records.
28
+ //
29
+ // INV-6 says logs carry no arguments, results, code, or raw downstream error
30
+ // text. Filtering errors where they arise kept missing sources: a validator's
31
+ // diagnostic, a stream's TypeError, a JSON-RPC error the agent may see. So the
32
+ // rule is enforced here, at the sink. A failure reaches an operator only as
33
+ // the fields of a `FailureRecord`, which `failureRecord` alone builds: each
34
+ // one copied from an explicit list, checked against a constant table or a
35
+ // grammar, and never taken from an error's message, cause, stack, data,
36
+ // nested errors, or name. `logFailure` writes nothing else, and every status
37
+ // message about a failure comes from `describeFailure`
38
+ // (test/operator-record-sources.node.test.ts holds src/ to that).
39
+ //
40
+ // Provenance is by identity, never by shape. Text that merely looks like
41
+ // connecta's (an error named like one of its classes, a status sentence
42
+ // shaped like a record) carries no weight: a class label comes from the
43
+ // prototype chain, a status message from the status object connecta built.
44
+ //
45
+ // Agent-facing results are a different surface with a different rule: a
46
+ // downstream's own answer to a call may reach the agent that made it (see
47
+ // documentation/architecture.md, "Errors and records"). Core's sent-secrets
48
+ // boundary removes credentials used by the upstream request from those answers, including
49
+ // allowed diagnostics, tool results and nested error causes/data (INV-5).
50
+ import { classificationCode, ConnectorCallError, NETWORK_ERROR_CODES, WithheldTextError, } from "./errors.js";
51
+ const STEPS = new Set([
52
+ "MCP handshake",
53
+ "tools/list",
54
+ "tools/call",
55
+ "resources/read",
56
+ "OAuth discovery",
57
+ "OAuth client registration",
58
+ "OAuth token request",
59
+ "OAuth flow",
60
+ "handler",
61
+ ]);
62
+ /**
63
+ * Registered OAuth `error` codes: the pinned SDK's `OAuthErrorCode` values
64
+ * (RFC 6749, 6750, 7591, 8707, 9449) and RFC 7591's software-statement pair.
65
+ */
66
+ export const OAUTH_ERROR_CODES = new Set([
67
+ "invalid_request",
68
+ "invalid_client",
69
+ "invalid_grant",
70
+ "unauthorized_client",
71
+ "unsupported_grant_type",
72
+ "invalid_scope",
73
+ "access_denied",
74
+ "server_error",
75
+ "temporarily_unavailable",
76
+ "unsupported_response_type",
77
+ "unsupported_token_type",
78
+ "invalid_token",
79
+ "method_not_allowed",
80
+ "too_many_requests",
81
+ "invalid_client_metadata",
82
+ "invalid_redirect_uri",
83
+ "insufficient_scope",
84
+ "invalid_target",
85
+ "invalid_dpop_proof",
86
+ "use_dpop_nonce",
87
+ "invalid_software_statement",
88
+ "unapproved_software_statement",
89
+ ]);
90
+ const SOURCES = new Set([
91
+ "call_tool",
92
+ "call_destructive_tool",
93
+ "batch_call",
94
+ "execute_code",
95
+ "resume_execution",
96
+ ]);
97
+ const MODES = new Set(["continue", "restart"]);
98
+ const STATUS_STATES = new Set(["ok", "auth_required", "credential_required", "error"]);
99
+ /** A status state, or `failed` for a status that could not be read at all. */
100
+ const STATES = new Set([...STATUS_STATES, "failed"]);
101
+ const AUTH_DENIAL_REASONS = new Set([
102
+ "authentication_failed",
103
+ "authorization_header_invalid",
104
+ "token_type_mismatch",
105
+ "oauth_verification_failed",
106
+ "oauth_client_not_allowed",
107
+ "oauth_binding_mismatch",
108
+ "session_origin_mismatch",
109
+ "user_missing",
110
+ "email_lookup_failed",
111
+ "verified_email_invalid",
112
+ "email_domain_denied",
113
+ "gate_denied",
114
+ "gate_failed",
115
+ "pool_not_declared",
116
+ "pool_grant_denied",
117
+ "pool_grant_threw",
118
+ ]);
119
+ /** The registry's connector id grammar (src/registry.ts). */
120
+ const CONNECTOR_ID_RE = /^[a-z0-9_-]{1,64}$/;
121
+ /** MCP's tool name grammar (SEP-986). */
122
+ const TOOL_NAME_RE = /^[A-Za-z0-9_.-]{1,128}$/;
123
+ const LABEL_RE = /^[A-Za-z][A-Za-z0-9]{0,63}$/;
124
+ /** What stands in for a name connecta could not vouch for. */
125
+ const UNLISTED_TOOL = "<unlisted>";
126
+ /**
127
+ * A catalog entry whose name breaks MCP's tool-name grammar. MCP does not
128
+ * require that grammar and real servers ship spaced or non-ASCII names, so
129
+ * the catalog keeps such a tool callable (src/connector-access.ts); only its
130
+ * name stays out of records.
131
+ */
132
+ const WITHHELD_TOOL = "<withheld>";
133
+ const UNKNOWN_CONNECTOR = "<unknown>";
134
+ // ---------------------------------------------------------------------------
135
+ // Error class labels, by identity
136
+ /** Prototype → label. Labels are literals in source, never read from an error. */
137
+ const labels = new Map();
138
+ const labelNames = new Set(["DOMException", "AbortError", "TimeoutError"]);
139
+ /**
140
+ * Name `ctor`'s instances `label` in records and fixed text. Call once, at
141
+ * module scope, beside the class. An error whose prototype chain reaches no
142
+ * labelled class has no label; its `name` is never consulted.
143
+ */
144
+ export function labelErrorClass(ctor, label) {
145
+ if (!LABEL_RE.test(label))
146
+ throw new TypeError(`Invalid error class label "${label}"`);
147
+ labels.set(ctor.prototype, label);
148
+ labelNames.add(label);
149
+ }
150
+ for (const [ctor, label] of [
151
+ [Error, "Error"],
152
+ [TypeError, "TypeError"],
153
+ [RangeError, "RangeError"],
154
+ [SyntaxError, "SyntaxError"],
155
+ [ReferenceError, "ReferenceError"],
156
+ [URIError, "URIError"],
157
+ [EvalError, "EvalError"],
158
+ [AggregateError, "AggregateError"],
159
+ [ConnectorCallError, "ConnectorCallError"],
160
+ [WithheldTextError, "WithheldTextError"],
161
+ ]) {
162
+ labelErrorClass(ctor, label);
163
+ }
164
+ /** The two DOMException names a record tells apart; any other is `DOMException`. */
165
+ const DOM_EXCEPTION_LABELS = new Set(["AbortError", "TimeoutError"]);
166
+ /**
167
+ * The label of the nearest labelled class on `failure`'s prototype chain.
168
+ * A genuine DOMException is told as `AbortError` or `TimeoutError` when its
169
+ * name is exactly one of those, else `DOMException`.
170
+ */
171
+ export function errorLabel(failure) {
172
+ if (failure === null || typeof failure !== "object")
173
+ return undefined;
174
+ try {
175
+ for (let proto = Object.getPrototypeOf(failure); proto !== null; proto = Object.getPrototypeOf(proto)) {
176
+ if (typeof DOMException === "function" && proto === DOMException.prototype) {
177
+ const name = failure.name;
178
+ return DOM_EXCEPTION_LABELS.has(name) ? name : "DOMException";
179
+ }
180
+ const label = labels.get(proto);
181
+ if (label)
182
+ return label;
183
+ }
184
+ }
185
+ catch {
186
+ // A proxy, or a DOMException-shaped object without its internal slot.
187
+ }
188
+ return undefined;
189
+ }
190
+ function origin(value) {
191
+ if (typeof value !== "string")
192
+ return undefined;
193
+ try {
194
+ const url = new URL(value);
195
+ return (url.protocol === "https:" || url.protocol === "http:") && url.origin.length <= 253 ? url.origin : undefined;
196
+ }
197
+ catch {
198
+ return undefined;
199
+ }
200
+ }
201
+ function httpStatus(value) {
202
+ return Number.isInteger(value) && value >= 100 && value <= 599
203
+ ? value
204
+ : undefined;
205
+ }
206
+ function member(value, table) {
207
+ return typeof value === "string" && table.has(value) ? value : undefined;
208
+ }
209
+ function checkedFacts(facts) {
210
+ if (!facts)
211
+ return {};
212
+ return {
213
+ ...defined("step", member(facts.step, STEPS)),
214
+ ...defined("origin", origin(facts.origin)),
215
+ ...defined("httpStatus", httpStatus(facts.httpStatus)),
216
+ ...defined("oauthError", member(facts.oauthError, OAUTH_ERROR_CODES)),
217
+ ...defined("errorClass", member(facts.errorClass, labelNames)),
218
+ };
219
+ }
220
+ function defined(key, value) {
221
+ return (value === undefined ? {} : { [key]: value });
222
+ }
223
+ /**
224
+ * Facts attached to failures by identity, never as properties: nothing about
225
+ * the failure's own shape changes, and nothing a caller can see grows.
226
+ */
227
+ const attached = new WeakMap();
228
+ /** Note what connecta knows about `failure` for any record made of it later. */
229
+ export function attachFailureFacts(failure, facts) {
230
+ if (failure !== null && typeof failure === "object") {
231
+ attached.set(failure, { ...attached.get(failure), ...checkedFacts(facts) });
232
+ }
233
+ return failure;
234
+ }
235
+ /** The facts and class label of `from`, kept for `to` (its classified form, say). */
236
+ export function carryFailureFacts(from, to) {
237
+ if (to === null || typeof to !== "object")
238
+ return to;
239
+ const facts = from !== null && typeof from === "object" ? attached.get(from) : undefined;
240
+ const errorClass = errorLabel(from);
241
+ // A class carried here already (the original behind a rebuilt error) wins.
242
+ const carried = { ...(errorClass ? { errorClass } : {}), ...facts };
243
+ attached.set(to, { ...carried, ...attached.get(to) });
244
+ return to;
245
+ }
246
+ const built = new WeakSet();
247
+ function count(value, max) {
248
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= max
249
+ ? Math.round(value)
250
+ : undefined;
251
+ }
252
+ function connectorId(value) {
253
+ if (value === undefined)
254
+ return undefined;
255
+ return typeof value === "string" && CONNECTOR_ID_RE.test(value) ? value : UNKNOWN_CONNECTOR;
256
+ }
257
+ /**
258
+ * How an operator record names a catalog entry: its name when it fits MCP's
259
+ * tool-name grammar, else `<withheld>`. Activity rows and the operator page's
260
+ * catalog use it too, so a withheld name appears nowhere an operator reads.
261
+ */
262
+ export function recordedToolName(entry) {
263
+ return typeof entry.name === "string" && TOOL_NAME_RE.test(entry.name) ? entry.name : WITHHELD_TOOL;
264
+ }
265
+ function checkedSubject(subject) {
266
+ const tool = subject.tool;
267
+ return {
268
+ ...defined("reason", member(subject.reason, AUTH_DENIAL_REASONS)),
269
+ ...defined("connector", connectorId(subject.connector)),
270
+ ...("tool" in subject
271
+ ? { tool: tool === null || typeof tool !== "object" ? UNLISTED_TOOL : recordedToolName(tool) }
272
+ : {}),
273
+ ...defined("source", member(subject.source, SOURCES)),
274
+ ...defined("state", member(subject.state, STATES)),
275
+ ...defined("mode", member(subject.mode, MODES)),
276
+ ...defined("attempts", count(subject.attempts, 1_000)),
277
+ ...defined("durationMs", count(subject.durationMs, 86_400_000)),
278
+ };
279
+ }
280
+ /**
281
+ * Classifications connecta computed (`CallErrorDetails` it built), by
282
+ * identity. A plain object is read as one only when registered here: a thrown
283
+ * object shaped like a classification is just a thrown object.
284
+ */
285
+ const classifications = new WeakSet();
286
+ /** Vouch for `details` as a classification connecta itself computed. */
287
+ export function classifiedFailure(details) {
288
+ classifications.add(details);
289
+ return details;
290
+ }
291
+ /** The typed classification `failure` carries. */
292
+ function classification(failure) {
293
+ let typed;
294
+ if (failure instanceof ConnectorCallError)
295
+ typed = failure;
296
+ else if (failure instanceof WithheldTextError) {
297
+ typed = { retryable: failure.retryable, ...(failure.timeout ? { code: "timeout" } : {}) };
298
+ }
299
+ else if (failure !== null && typeof failure === "object" && classifications.has(failure)) {
300
+ typed = failure;
301
+ }
302
+ const details = typed?.details !== null && typeof typed?.details === "object"
303
+ ? typed.details
304
+ : undefined;
305
+ return {
306
+ ...defined("code", classificationCode(typed?.code)),
307
+ ...defined("retryable", typeof typed?.retryable === "boolean" ? typed.retryable : undefined),
308
+ ...defined("origin", origin(details?.host)),
309
+ ...defined("errno", member(details?.code, NETWORK_ERROR_CODES)),
310
+ };
311
+ }
312
+ /**
313
+ * The record of a failure: the subject's listed fields, each checked; the
314
+ * facts attached to the failure; its class label; and the typed
315
+ * classification it carries (a `ConnectorCallError`'s code, retryability,
316
+ * origin, and errno, or those of a `CallErrorDetails` registered with
317
+ * `classifiedFailure`). Never its
318
+ * text, and nothing else from `subject`.
319
+ */
320
+ export function failureRecord(subject, failure) {
321
+ const facts = failure !== null && typeof failure === "object" ? attached.get(failure) : undefined;
322
+ const errorClass = errorLabel(failure);
323
+ const record = Object.freeze({
324
+ ...checkedSubject(subject),
325
+ ...classification(failure),
326
+ ...(errorClass ? { errorClass } : {}),
327
+ ...checkedFacts(facts),
328
+ });
329
+ built.add(record);
330
+ return record;
331
+ }
332
+ /** What is logged in place of a record `failureRecord` did not build. */
333
+ const REJECTED_RECORD = Object.freeze({ record: "<rejected>" });
334
+ /**
335
+ * Log one failure as `[connecta] <event>` and its record. A record
336
+ * `failureRecord` did not build is replaced, whatever its shape, by a fixed
337
+ * `{ record: "<rejected>" }`. Never throws: it runs on failure paths, and a
338
+ * logger that throws is contained here.
339
+ */
340
+ export function logFailure(logger, event, record, level = "warn") {
341
+ try {
342
+ logger[level](`[connecta] ${event}`, built.has(record) ? record : REJECTED_RECORD);
343
+ }
344
+ catch {
345
+ // A failing logger cannot add a failure of its own.
346
+ }
347
+ }
348
+ /**
349
+ * A failure in connecta's words, from its record alone:
350
+ * `Connector "<id>" <step> with <origin> failed with HTTP <status> (<class>, <code>, <errno>).`
351
+ * Used for status messages, and for agent-facing text where the error's own
352
+ * may not pass.
353
+ */
354
+ export function describeFailure(connectorId, failure) {
355
+ const record = failureRecord({ connector: connectorId }, failure);
356
+ const where = record.step
357
+ ? ` ${record.step}${record.origin ? ` with ${record.origin}` : ""}`
358
+ : record.origin
359
+ ? ` request to ${record.origin}`
360
+ : "";
361
+ const status = record.httpStatus ? ` with HTTP ${record.httpStatus}` : "";
362
+ const oauth = record.oauthError ? ` and OAuth error ${record.oauthError}` : "";
363
+ const kind = [record.errorClass, record.code, record.errno].filter(Boolean).join(", ");
364
+ return `Connector "${record.connector}"${where} failed${status}${oauth}${kind ? ` (${kind})` : ""}.`;
365
+ }
366
+ const ownStatuses = new WeakMap();
367
+ function snapshot(status, failure) {
368
+ return {
369
+ state: member(status.state, STATUS_STATES) ?? "error",
370
+ ...(typeof status.message === "string" ? { message: status.message } : {}),
371
+ ...(["cimd", "dcr", "static"].includes(status.registrationPath ?? "")
372
+ ? { registrationPath: status.registrationPath }
373
+ : {}),
374
+ ...(status.resourceTemplateRefusals
375
+ ? {
376
+ resourceTemplateRefusals: [
377
+ ...new Set(status.resourceTemplateRefusals.filter((code) => code === "resource_template_ambiguous" || code === "resource_match_budget_exceeded")),
378
+ ],
379
+ }
380
+ : {}),
381
+ ...(failure === undefined ? {} : { failure }),
382
+ };
383
+ }
384
+ /** A status whose message connecta wrote, such as an auth_required notice. */
385
+ export function ownStatus(status) {
386
+ ownStatuses.set(status, snapshot(status));
387
+ return status;
388
+ }
389
+ /** An error status described from `failure`'s record, remembering the failure. */
390
+ export function failureStatus(connectorId, failure) {
391
+ const status = { state: "error", message: describeFailure(connectorId, failure) };
392
+ ownStatuses.set(status, snapshot(status, failure));
393
+ return status;
394
+ }
395
+ /**
396
+ * `status` rebuilt from what connecta approved. A status connecta wrote
397
+ * (`ownStatus`, `failureStatus`) is rebuilt from the snapshot taken then,
398
+ * whatever has since been assigned to it. Any other status contributes its
399
+ * typed state alone: a plugin `status()` seam returns its author's text,
400
+ * which can quote a downstream, and connecta cannot vouch for it whatever it
401
+ * looks like.
402
+ */
403
+ export function boundedStatus(status) {
404
+ const own = status !== null && typeof status === "object" ? ownStatuses.get(status) : undefined;
405
+ if (own) {
406
+ const rebuilt = {
407
+ state: own.state,
408
+ ...(own.resourceTemplateRefusals ? { resourceTemplateRefusals: [...own.resourceTemplateRefusals] } : {}),
409
+ ...(own.message === undefined ? {} : { message: own.message }),
410
+ ...(own.registrationPath === undefined ? {} : { registrationPath: own.registrationPath }),
411
+ };
412
+ ownStatuses.set(rebuilt, own);
413
+ return rebuilt;
414
+ }
415
+ return { state: member(status?.state, STATUS_STATES) ?? "error" };
416
+ }
417
+ /** The failure connecta saw behind a status it described, if any. */
418
+ export function statusFailure(status) {
419
+ return ownStatuses.get(status)?.failure;
420
+ }
@@ -0,0 +1,31 @@
1
+ import { OPERATOR_UI_ASSETS } from "./generated.js";
2
+ /** Web APIs only: identical bytes and cache semantics on Node and Workers. */
3
+ export function operatorAsset(request, path) {
4
+ if (request.method !== "GET" && request.method !== "HEAD") {
5
+ return new Response(null, { status: 405, headers: { Allow: "GET, HEAD", "Cache-Control": "no-store" } });
6
+ }
7
+ const asset = Object.hasOwn(OPERATOR_UI_ASSETS, path) ? OPERATOR_UI_ASSETS[path] : undefined;
8
+ if (!asset)
9
+ return new Response(request.method === "HEAD" ? null : "Not Found", {
10
+ status: 404,
11
+ headers: { "Cache-Control": "no-store" },
12
+ });
13
+ const headers = {
14
+ "Content-Type": asset.type,
15
+ "Cache-Control": "public, max-age=31536000, immutable",
16
+ "X-Content-Type-Options": "nosniff",
17
+ ETag: `"${path.slice(path.lastIndexOf("/") + 1)}"`,
18
+ };
19
+ if (request.headers
20
+ .get("If-None-Match")
21
+ ?.split(",")
22
+ .some((tag) => tag.trim().replace(/^W\//, "") === headers.ETag || tag.trim() === "*")) {
23
+ return new Response(null, { status: 304, headers });
24
+ }
25
+ const body = request.method === "HEAD"
26
+ ? null
27
+ : asset.binary
28
+ ? Uint8Array.from(atob(asset.body), (char) => char.charCodeAt(0))
29
+ : asset.body;
30
+ return new Response(body, { headers });
31
+ }
@@ -0,0 +1,72 @@
1
+ import type { ActivityOutcome } from "../activity.js";
2
+ import type { ConnectaConfigDescription } from "../describe-config.js";
3
+ import type { ConnectorStatus, ConnectorToolDescription } from "../types.js";
4
+ import type { UiProblem } from "./model.js";
5
+ /** The registry's final verdict, independent of the viewer's pool trust. */
6
+ export interface OperatorTool extends ConnectorToolDescription {
7
+ address: string;
8
+ classification: "read" | "write";
9
+ }
10
+ export interface OperatorLastCall {
11
+ at: string;
12
+ outcome: ActivityOutcome;
13
+ }
14
+ export interface OperatorConnectorOverlay {
15
+ id: string;
16
+ status: ConnectorStatus["state"];
17
+ /** Selected downstream client mechanism, when a client has been selected. */
18
+ auth?: {
19
+ registrationPath: NonNullable<ConnectorStatus["registrationPath"]>;
20
+ };
21
+ problem?: UiProblem;
22
+ tools: OperatorTool[];
23
+ /** Age of the last complete listing. Static or unobserved catalogs have no age. */
24
+ catalogAgeMs: number | null;
25
+ /** Latest visible call in the bounded activity window, or null when unknown. */
26
+ lastCall: OperatorLastCall | null;
27
+ }
28
+ /** Whole connector access, or exact tool grants. Guarded grants still require a live read verdict. */
29
+ export interface OperatorGrant {
30
+ connectorId: string;
31
+ tools: "all" | Array<{
32
+ name: string;
33
+ requireReadOnly: boolean;
34
+ }>;
35
+ }
36
+ export interface OperatorPool {
37
+ name: string;
38
+ path: string;
39
+ trust: "trusted" | "read-only";
40
+ /** Pool access intersected with the caller's grants. */
41
+ grants: OperatorGrant[];
42
+ }
43
+ export interface OperatorViewer {
44
+ interactive: boolean;
45
+ grants: OperatorGrant[];
46
+ /** Trust of the root /mcp endpoint. */
47
+ trust: "trusted" | "read-only";
48
+ pools: OperatorPool[];
49
+ permissions: {
50
+ activity: boolean;
51
+ accessTokenManagement: boolean;
52
+ connectors: Array<{
53
+ id: string;
54
+ use: boolean;
55
+ manageSharedAuth: boolean;
56
+ connectPersonal: boolean;
57
+ }>;
58
+ };
59
+ }
60
+ export interface OperatorUiContract {
61
+ schemaVersion: 1;
62
+ /** describeConfig's allowlisted snapshot, restricted to this caller's view. */
63
+ config: ConnectaConfigDescription;
64
+ /** Leaf provenance, built from the same allowlisted snapshot and scoped with it. */
65
+ configSources?: Record<string, "default" | "config">;
66
+ live: {
67
+ connectors: OperatorConnectorOverlay[];
68
+ /** Last-call lookup scans at most 1,000 recent rows. No payload or actor is returned. */
69
+ activity: "available" | "unconfigured" | "forbidden" | "unavailable";
70
+ };
71
+ you: OperatorViewer;
72
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { UiCredentialProblem, UiProblem } from "./model.js";
2
+ /**
3
+ * Every failure the Connections page can show, with the prompt its "Copy fix
4
+ * prompt" button puts on the clipboard.
5
+ *
6
+ * The signature is the guarantee. A prompt is looked up by kind and may name
7
+ * the connector's configured id; there is no argument an error message, a
8
+ * downstream body, or a credential could travel through, so none can reach a
9
+ * coding agent's context by way of this page. The notice beside it is fixed
10
+ * copy too wherever a downstream could have written the failure (see
11
+ * `refusedNotice` in view.ts); what remains are the page's own sentences and a
12
+ * credential form's field checks.
13
+ */
14
+ export type FixPromptKind = UiProblem | UiCredentialProblem | "credential_test_failed" | "oauth_action_failed" | "catalog_drift";
15
+ /** The clipboard text for one failure. Fixed by kind; see `CATALOGUE`. */
16
+ export declare function fixPrompt(kind: FixPromptKind, connectorId?: string): string;
17
+ /** Every kind, so a test can walk the whole catalogue. */
18
+ export declare const FIX_PROMPT_KINDS: FixPromptKind[];