@zackbart/connecta 0.34.1 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (429) hide show
  1. package/CHANGELOG.md +363 -0
  2. package/README.md +62 -106
  3. package/SECURITY.md +3 -2
  4. package/bin/connecta.mjs +41 -16
  5. package/bin/version.mjs +1 -1
  6. package/dist/access-tokens.d.ts +2 -1
  7. package/dist/access-tokens.js +66 -30
  8. package/dist/activity.d.ts +1 -1
  9. package/dist/activity.js +2 -0
  10. package/dist/agent-config.d.ts +23 -0
  11. package/dist/agent-config.js +59 -0
  12. package/dist/agent-description.js +16 -0
  13. package/dist/agent-identity.js +4 -4
  14. package/dist/agent-observation.js +39 -0
  15. package/dist/auth/clerk-sdk/client.d.ts +2 -0
  16. package/dist/auth/clerk-sdk/generated.js +554 -563
  17. package/dist/auth/clerk-transport.js +70 -3
  18. package/dist/auth/clerk.d.ts +13 -0
  19. package/dist/auth/clerk.js +653 -246
  20. package/dist/auth/cloudflare-access.js +46 -42
  21. package/dist/auth/downstream-client-metadata.js +2 -2
  22. package/dist/auth/downstream-oauth.js +2051 -1160
  23. package/dist/auth/downstream-revocation.js +88 -88
  24. package/dist/auth/native-call.js +20 -0
  25. package/dist/auth/native.js +184 -0
  26. package/dist/auth/oauth-start-reset.js +11 -5
  27. package/dist/auth/oauth-token-response.js +121 -102
  28. package/dist/auth/oauth-v2-migration.js +97 -78
  29. package/dist/auth/renewable-grant.js +10 -0
  30. package/dist/auth/static-oauth.js +663 -271
  31. package/dist/auth/vercel-agent.js +266 -0
  32. package/dist/auth-admission.js +204 -0
  33. package/dist/auth-elicitation.js +350 -241
  34. package/dist/authorization-handoff.js +164 -90
  35. package/dist/call-admission-context.js +3 -10
  36. package/dist/call-admission.js +2 -0
  37. package/dist/call-classification.js +9 -0
  38. package/dist/call-recovery.js +32 -13
  39. package/dist/canonical-json.js +8 -0
  40. package/dist/catalog-cache.js +369 -213
  41. package/dist/catalog-service.js +659 -530
  42. package/dist/catalog.js +5 -1
  43. package/dist/concurrency.js +0 -25
  44. package/dist/config-defaults.d.ts +1 -0
  45. package/dist/config-defaults.js +1 -0
  46. package/dist/config-schema.d.ts +3 -1
  47. package/dist/config-schema.js +13 -3
  48. package/dist/config.d.ts +40 -6
  49. package/dist/config.js +40 -26
  50. package/dist/connector-caller.d.ts +4 -0
  51. package/dist/connector-caller.js +17 -4
  52. package/dist/connectors/api-connector.d.ts +12 -7
  53. package/dist/connectors/api-connector.js +142 -56
  54. package/dist/connectors/guarded-fetch.d.ts +51 -17
  55. package/dist/connectors/guarded-fetch.js +215 -121
  56. package/dist/connectors/negotiation-cache.js +20 -10
  57. package/dist/connectors/remote-mcp.d.ts +10 -30
  58. package/dist/connectors/remote-mcp.js +955 -563
  59. package/dist/d1.js +10 -243
  60. package/dist/derived-context.js +5 -0
  61. package/dist/describe-config.d.ts +3 -1
  62. package/dist/describe-config.js +2 -0
  63. package/dist/downstream-input.js +347 -260
  64. package/dist/errors.d.ts +87 -6
  65. package/dist/errors.js +114 -4
  66. package/dist/execute.js +87 -50
  67. package/dist/executor-admission.js +3 -0
  68. package/dist/executors/quickjs.js +163 -112
  69. package/dist/guarded-transport.js +50 -0
  70. package/dist/guest-runtime.js +1 -1
  71. package/dist/http/client.js +439 -0
  72. package/dist/http/errors.js +558 -0
  73. package/dist/http/fields.js +49 -0
  74. package/dist/http/hatch-paging.js +318 -0
  75. package/dist/http/hatch.js +1279 -0
  76. package/dist/http/json-schema.js +267 -0
  77. package/dist/http/node-transport.js +241 -0
  78. package/dist/http/oauth.js +162 -0
  79. package/dist/http/paging.js +228 -0
  80. package/dist/http/provider.js +221 -0
  81. package/dist/http/safety.js +82 -0
  82. package/dist/http/services.js +79 -0
  83. package/dist/http/tools.js +383 -0
  84. package/dist/http/vendor-index.js +138 -0
  85. package/dist/http/vendor.js +448 -0
  86. package/dist/http/write-request.js +66 -0
  87. package/dist/http-policy.d.ts +11 -0
  88. package/dist/http-policy.js +45 -0
  89. package/dist/inbound-credential.js +1 -1
  90. package/dist/index.d.ts +2 -2
  91. package/dist/index.js +4 -0
  92. package/dist/invocation-auth.js +30 -19
  93. package/dist/invocation.js +218 -85
  94. package/dist/mcp-result.js +0 -4
  95. package/dist/membership.d.ts +4 -0
  96. package/dist/meta-output.js +1 -1
  97. package/dist/meta-tools.js +531 -406
  98. package/dist/native-recovery.js +52 -0
  99. package/dist/native-tools.js +382 -0
  100. package/dist/node.d.ts +4 -3
  101. package/dist/node.js +45 -8
  102. package/dist/oauth-handoff.js +177 -104
  103. package/dist/oauth-partition.js +6 -20
  104. package/dist/oauth-sealing.js +6 -7
  105. package/dist/operation-vocabulary.js +101 -0
  106. package/dist/operator-record.d.ts +2 -2
  107. package/dist/operator-record.js +17 -4
  108. package/dist/operator-ui/agents.d.ts +5 -4
  109. package/dist/operator-ui/agents.js +17 -7
  110. package/dist/operator-ui/config-summary.js +5 -4
  111. package/dist/operator-ui/connection-map.js +7 -5
  112. package/dist/operator-ui/contract.d.ts +7 -3
  113. package/dist/operator-ui/endpoints.js +4 -13
  114. package/dist/operator-ui/fix-prompts.d.ts +1 -1
  115. package/dist/operator-ui/fix-prompts.js +0 -8
  116. package/dist/operator-ui/generated.js +3 -3
  117. package/dist/operator-ui/model.d.ts +2 -3
  118. package/dist/operator-ui/overview.js +5 -2
  119. package/dist/operator-ui/setup-commands.js +0 -4
  120. package/dist/operator-ui/view.d.ts +2 -25
  121. package/dist/operator-ui/view.js +2 -76
  122. package/dist/program-writes.js +47 -26
  123. package/dist/provider-call-facts.d.ts +115 -0
  124. package/dist/provider-call-facts.js +182 -0
  125. package/dist/provider-call.d.ts +112 -0
  126. package/dist/provider-call.js +271 -0
  127. package/dist/provider.d.ts +65 -31
  128. package/dist/provider.js +116 -39
  129. package/dist/providers/_shared/google/hatch.js +91 -0
  130. package/dist/providers/_shared/google/kit.js +537 -0
  131. package/dist/providers/_shared/google/result-size.js +15 -2
  132. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  133. package/dist/providers/_shared/google/service-account.js +8 -4
  134. package/dist/providers/_shared/google/workspace.d.ts +6 -210
  135. package/dist/providers/_shared/google/workspace.js +6 -425
  136. package/dist/providers/_shared/rest/compact-index.js +86 -0
  137. package/dist/providers/_shared/rest/dispatch.js +3 -23
  138. package/dist/providers/_shared/rest/kit.js +546 -0
  139. package/dist/providers/_shared/rest/operation-index.js +230 -14
  140. package/dist/providers/_shared/rest/response-media.js +11 -0
  141. package/dist/providers/_shared/rest/tools.js +101 -78
  142. package/dist/providers/_shared/rest/value-safety.js +353 -195
  143. package/dist/providers/_shared/sensitive-fields.js +27 -0
  144. package/dist/providers/breeze/api.js +584 -0
  145. package/dist/providers/breeze/index.d.ts +1 -3
  146. package/dist/providers/breeze/index.js +67 -1420
  147. package/dist/providers/breeze/openapi.generated.js +60 -0
  148. package/dist/providers/breeze/skill.generated.js +2 -6
  149. package/dist/providers/breeze/tools.js +696 -0
  150. package/dist/providers/ccb/api.js +496 -0
  151. package/dist/providers/ccb/index.d.ts +15 -37
  152. package/dist/providers/ccb/index.js +77 -1702
  153. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  154. package/dist/providers/ccb/lib/constants.js +74 -0
  155. package/dist/providers/ccb/lib/guide.js +9 -0
  156. package/dist/providers/ccb/lib/verdicts.js +387 -0
  157. package/dist/providers/ccb/openapi.generated.js +397 -0
  158. package/dist/providers/ccb/safety.js +295 -0
  159. package/dist/providers/ccb/skill.generated.js +2 -6
  160. package/dist/providers/ccb/tools.js +853 -0
  161. package/dist/providers/cloudflare/api.js +1066 -0
  162. package/dist/providers/cloudflare/index.d.ts +5 -22
  163. package/dist/providers/cloudflare/index.js +79 -197
  164. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  165. package/dist/providers/cloudflare/lib/config.js +2 -0
  166. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  167. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  168. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  169. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  170. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  171. package/dist/providers/cloudflare/openapi.generated.js +4 -3
  172. package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
  173. package/dist/providers/cloudflare/skill.generated.js +2 -10
  174. package/dist/providers/cloudflare/tools.js +360 -0
  175. package/dist/providers/docs/api.js +51 -0
  176. package/dist/providers/docs/index.d.ts +1 -12
  177. package/dist/providers/docs/index.js +53 -1165
  178. package/dist/providers/docs/lib/constants.d.ts +18 -0
  179. package/dist/providers/docs/lib/constants.js +18 -0
  180. package/dist/providers/docs/lib/discovery.js +26 -0
  181. package/dist/providers/docs/lib/requests.js +48 -0
  182. package/dist/providers/docs/lib/schemas.js +168 -0
  183. package/dist/providers/docs/openapi.generated.js +16 -0
  184. package/dist/providers/docs/safety.js +54 -0
  185. package/dist/providers/docs/skill.generated.js +2 -6
  186. package/dist/providers/docs/tools.js +802 -0
  187. package/dist/providers/drive/api.js +177 -0
  188. package/dist/providers/drive/index.d.ts +1 -12
  189. package/dist/providers/drive/index.js +38 -1448
  190. package/dist/providers/drive/lib/constants.d.ts +6 -0
  191. package/dist/providers/drive/lib/constants.js +6 -0
  192. package/dist/providers/drive/lib/contracts.js +532 -0
  193. package/dist/providers/drive/lib/hatch.js +197 -0
  194. package/dist/providers/drive/openapi.generated.js +77 -0
  195. package/dist/providers/drive/safety.js +90 -0
  196. package/dist/providers/drive/skill.generated.js +2 -6
  197. package/dist/providers/drive/tools.js +1290 -0
  198. package/dist/providers/forms/api.js +71 -0
  199. package/dist/providers/forms/index.d.ts +1 -12
  200. package/dist/providers/forms/index.js +49 -1145
  201. package/dist/providers/forms/lib/constants.d.ts +14 -0
  202. package/dist/providers/forms/lib/constants.js +19 -0
  203. package/dist/providers/forms/lib/hatch.js +72 -0
  204. package/dist/providers/forms/openapi.generated.js +23 -0
  205. package/dist/providers/forms/safety.js +34 -0
  206. package/dist/providers/forms/skill.generated.js +2 -8
  207. package/dist/providers/forms/tools.js +1024 -0
  208. package/dist/providers/github/api.js +150 -0
  209. package/dist/providers/github/index.d.ts +4 -5
  210. package/dist/providers/github/index.js +30 -87
  211. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  212. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  213. package/dist/providers/github/lib/discovery.js +132 -0
  214. package/dist/providers/github/lib/operations.js +58 -0
  215. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  216. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  217. package/dist/providers/github/openapi.generated.js +1216 -0
  218. package/dist/providers/github/safety.js +216 -0
  219. package/dist/providers/github/skill.generated.js +2 -5
  220. package/dist/providers/github/tools.js +506 -0
  221. package/dist/providers/gmail/api.js +114 -0
  222. package/dist/providers/gmail/index.d.ts +1 -12
  223. package/dist/providers/gmail/index.js +73 -1621
  224. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  225. package/dist/providers/gmail/lib/constants.js +16 -0
  226. package/dist/providers/gmail/lib/index.js +173 -0
  227. package/dist/providers/gmail/lib/schemas.js +176 -0
  228. package/dist/providers/gmail/lib/threading.js +120 -0
  229. package/dist/providers/gmail/openapi.generated.js +92 -0
  230. package/dist/providers/gmail/safety.js +21 -0
  231. package/dist/providers/gmail/skill.generated.js +2 -6
  232. package/dist/providers/gmail/tools.js +1435 -0
  233. package/dist/providers/overflow/api.js +1008 -0
  234. package/dist/providers/overflow/index.d.ts +1 -17
  235. package/dist/providers/overflow/index.js +60 -1488
  236. package/dist/providers/overflow/skill.generated.js +2 -5
  237. package/dist/providers/overflow/tools.js +873 -0
  238. package/dist/providers/planning-center/api.js +467 -0
  239. package/dist/providers/planning-center/index.d.ts +4 -35
  240. package/dist/providers/planning-center/index.js +69 -2445
  241. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  242. package/dist/providers/planning-center/lib/constants.js +83 -0
  243. package/dist/providers/planning-center/lib/discovery.js +40 -0
  244. package/dist/providers/planning-center/lib/guide.js +39 -0
  245. package/dist/providers/planning-center/lib/json-api.js +83 -0
  246. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  247. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  248. package/dist/providers/planning-center/safety.js +188 -0
  249. package/dist/providers/planning-center/skill.generated.js +2 -27
  250. package/dist/providers/planning-center/tools.js +1010 -0
  251. package/dist/providers/sheets/api.js +133 -0
  252. package/dist/providers/sheets/index.d.ts +4 -16
  253. package/dist/providers/sheets/index.js +61 -1172
  254. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  255. package/dist/providers/sheets/lib/constants.js +14 -0
  256. package/dist/providers/sheets/lib/hatch.js +163 -0
  257. package/dist/providers/sheets/lib/requests.js +105 -0
  258. package/dist/providers/sheets/lib/schemas.js +138 -0
  259. package/dist/providers/sheets/openapi.generated.js +30 -0
  260. package/dist/providers/sheets/safety.js +27 -0
  261. package/dist/providers/sheets/skill.generated.js +2 -7
  262. package/dist/providers/sheets/tools.js +734 -0
  263. package/dist/providers/slides/api.js +147 -0
  264. package/dist/providers/slides/index.d.ts +2 -10
  265. package/dist/providers/slides/index.js +48 -2555
  266. package/dist/providers/slides/lib/constants.d.ts +11 -0
  267. package/dist/providers/slides/lib/constants.js +11 -0
  268. package/dist/providers/slides/lib/requests.js +60 -0
  269. package/dist/providers/slides/lib/schemas.js +565 -0
  270. package/dist/providers/slides/openapi.generated.js +18 -0
  271. package/dist/providers/slides/skill.generated.js +2 -6
  272. package/dist/providers/slides/tools.js +1968 -0
  273. package/dist/providers/stripe/api.js +401 -0
  274. package/dist/providers/stripe/index.d.ts +5 -27
  275. package/dist/providers/stripe/index.js +74 -210
  276. package/dist/providers/stripe/lib/config.d.ts +4 -0
  277. package/dist/providers/stripe/lib/config.js +2 -0
  278. package/dist/providers/stripe/openapi.generated.js +4 -3
  279. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  280. package/dist/providers/stripe/skill.generated.js +2 -11
  281. package/dist/providers/stripe/tools.js +77 -0
  282. package/dist/providers/tithely/api.js +531 -0
  283. package/dist/providers/tithely/index.d.ts +17 -0
  284. package/dist/providers/tithely/index.js +86 -965
  285. package/dist/providers/tithely/skill.generated.js +2 -5
  286. package/dist/providers/tithely/tools.js +426 -0
  287. package/dist/providers/vercel/api.js +551 -0
  288. package/dist/providers/vercel/index.d.ts +9 -25
  289. package/dist/providers/vercel/index.js +66 -260
  290. package/dist/providers/vercel/lib/config.d.ts +2 -0
  291. package/dist/providers/vercel/lib/config.js +2 -0
  292. package/dist/providers/vercel/lib/index.js +117 -0
  293. package/dist/providers/vercel/lib/logs.js +100 -0
  294. package/dist/providers/vercel/lib/pin.js +270 -0
  295. package/dist/providers/vercel/openapi.generated.js +10 -5
  296. package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
  297. package/dist/providers/vercel/skill.generated.js +2 -7
  298. package/dist/providers/vercel/tools.js +642 -0
  299. package/dist/redaction-identity.js +47 -0
  300. package/dist/redaction-marker.js +125 -0
  301. package/dist/registry.d.ts +21 -15
  302. package/dist/registry.js +409 -376
  303. package/dist/routes/access-tokens.js +1 -1
  304. package/dist/routes/actor-labels.js +1 -1
  305. package/dist/routes/connect.js +1 -1
  306. package/dist/routes/dispatch.js +103 -0
  307. package/dist/routes/mcp.js +170 -79
  308. package/dist/routes/oauth.js +3 -1
  309. package/dist/routes/operator.js +2 -2
  310. package/dist/routes/shared.d.ts +5 -4
  311. package/dist/routes/shared.js +244 -177
  312. package/dist/routes/skills.js +10 -8
  313. package/dist/routes/ui-config.js +27 -3
  314. package/dist/routes/ui-data.js +21 -20
  315. package/dist/routes/ui.js +1 -1
  316. package/dist/runtime/call-admission.js +0 -26
  317. package/dist/runtime/call-entry.js +30 -0
  318. package/dist/runtime/call.js +556 -0
  319. package/dist/runtime/connector-scope.js +7 -0
  320. package/dist/runtime/credential-probe.js +34 -0
  321. package/dist/runtime/executor.js +40 -0
  322. package/dist/runtime/native-storage.js +63 -0
  323. package/dist/runtime/registry.js +27 -0
  324. package/dist/runtime/request-scope.js +72 -0
  325. package/dist/runtime/run.js +15 -7
  326. package/dist/runtime/shared-read.js +34 -5
  327. package/dist/runtime/storage.js +34 -21
  328. package/dist/sent-secrets.d.ts +194 -37
  329. package/dist/sent-secrets.js +1743 -340
  330. package/dist/server.js +5 -56
  331. package/dist/skills.js +322 -222
  332. package/dist/sqlite.js +15 -14
  333. package/dist/storage/d1-copy.js +255 -0
  334. package/dist/storage/d1-driver.js +15 -0
  335. package/dist/storage/keys.js +28 -0
  336. package/dist/storage/sql-driver.js +88 -0
  337. package/dist/storage/sql-native.js +544 -0
  338. package/dist/storage/sql-schema.d.ts +6 -0
  339. package/dist/storage/sql-schema.js +15 -0
  340. package/dist/storage/sql.d.ts +14 -20
  341. package/dist/storage/sql.js +29 -535
  342. package/dist/timeout.js +0 -26
  343. package/dist/tool-safety.js +20 -10
  344. package/dist/types.d.ts +7 -109
  345. package/dist/upward.js +3 -0
  346. package/dist/usage-guide.js +3 -1
  347. package/dist/validate.d.ts +4 -2
  348. package/dist/validate.js +86 -9
  349. package/dist/version.d.ts +1 -1
  350. package/dist/version.js +1 -1
  351. package/dist/worker.js +312 -292
  352. package/dist/workspace-subject.js +5 -1
  353. package/dist/write-outcome.js +454 -34
  354. package/documentation/README.md +27 -7
  355. package/documentation/architecture.md +2547 -226
  356. package/documentation/auth.md +821 -98
  357. package/documentation/code-mode.md +49 -16
  358. package/documentation/deploying.md +164 -20
  359. package/documentation/hosted-mcp-migration.md +460 -0
  360. package/documentation/integrating.md +129 -144
  361. package/documentation/meta-tools.md +409 -32
  362. package/documentation/operating.md +27 -3
  363. package/documentation/operator-tests.md +3 -0
  364. package/documentation/operator-ui.md +68 -45
  365. package/documentation/provider-auth-migration.md +16 -63
  366. package/documentation/provider-migration-0.29.md +10 -5
  367. package/documentation/provider-migration-0.35.md +975 -0
  368. package/documentation/providers.md +965 -0
  369. package/examples/worker/AGENTS.md +23 -9
  370. package/examples/worker/README.md +254 -135
  371. package/examples/worker/alchemy.run.ts +116 -0
  372. package/examples/worker/gitignore +10 -0
  373. package/examples/worker/src/connecta.config.ts +23 -21
  374. package/examples/worker/wrangler.jsonc +14 -5
  375. package/guides/connecting-agents.md +157 -0
  376. package/guides/how-it-works.md +156 -0
  377. package/guides/safety-and-trust.md +153 -0
  378. package/package.json +27 -46
  379. package/templates/node/.env.example +3 -0
  380. package/templates/node/README.md +22 -2
  381. package/templates/node/docker-compose.yml +1 -0
  382. package/templates/node/package.json +3 -2
  383. package/templates/node/src/connecta.config.ts +8 -0
  384. package/dist/catalog-drift.d.ts +0 -138
  385. package/dist/catalog-drift.js +0 -578
  386. package/dist/providers/_shared/request-token.js +0 -83
  387. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  388. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  389. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  390. package/dist/providers/basecamp/index.d.ts +0 -45
  391. package/dist/providers/basecamp/index.js +0 -997
  392. package/dist/providers/basecamp/skill.generated.js +0 -9
  393. package/dist/providers/cloudflare/rest.d.ts +0 -37
  394. package/dist/providers/cloudflare/rest.js +0 -1097
  395. package/dist/providers/github/hosted.js +0 -218
  396. package/dist/providers/github/rest.js +0 -321
  397. package/dist/providers/infisical/index.d.ts +0 -7
  398. package/dist/providers/infisical/index.js +0 -713
  399. package/dist/providers/infisical/skill.generated.js +0 -8
  400. package/dist/providers/intercom/index.d.ts +0 -24
  401. package/dist/providers/intercom/index.js +0 -121
  402. package/dist/providers/intercom/skill.generated.js +0 -8
  403. package/dist/providers/linear/index.d.ts +0 -55
  404. package/dist/providers/linear/index.js +0 -537
  405. package/dist/providers/linear/skill.generated.js +0 -8
  406. package/dist/providers/mixpanel/index.d.ts +0 -59
  407. package/dist/providers/mixpanel/index.js +0 -472
  408. package/dist/providers/mixpanel/operations.js +0 -20
  409. package/dist/providers/mixpanel/rest.js +0 -603
  410. package/dist/providers/mixpanel/skill.generated.js +0 -10
  411. package/dist/providers/mixpanel/value-safety.js +0 -16
  412. package/dist/providers/notion/index.d.ts +0 -67
  413. package/dist/providers/notion/index.js +0 -368
  414. package/dist/providers/notion/openapi.generated.js +0 -75
  415. package/dist/providers/notion/rest.d.ts +0 -24
  416. package/dist/providers/notion/rest.js +0 -1226
  417. package/dist/providers/notion/skill.generated.js +0 -11
  418. package/dist/providers/notion/value-safety.js +0 -56
  419. package/dist/providers/revenuecat/index.d.ts +0 -51
  420. package/dist/providers/revenuecat/index.js +0 -697
  421. package/dist/providers/revenuecat/skill.generated.js +0 -11
  422. package/dist/providers/stripe/rest.d.ts +0 -14
  423. package/dist/providers/stripe/rest.js +0 -402
  424. package/dist/providers/vercel/rest.d.ts +0 -20
  425. package/dist/providers/vercel/rest.js +0 -1020
  426. package/dist/providers/wispr-flow/index.d.ts +0 -14
  427. package/dist/providers/wispr-flow/index.js +0 -117
  428. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  429. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -1,13 +1,34 @@
1
1
  import { auth, OAuthError, UnauthorizedError } from "@modelcontextprotocol/client";
2
+ import { Effect } from "effect";
2
3
  import { byteReadResponse } from "../byte-read-response.js";
3
- import { sentSecretsFor, sentSecretsFetch, shortSecretWarning } from "../sent-secrets.js";
4
- import { ConnectorCallError, msg } from "../errors.js";
5
- import { oauthPartitionFor, retainOAuthPartition, retainingOAuthPartition } from "../oauth-partition.js";
4
+ import { deriveContext } from "../derived-context.js";
5
+ import { ConnectorCallError, msg, resendAdviceOf, withResendAdvice } from "../errors.js";
6
+ import { guardedTransport, sendCredentialPost } from "../guarded-transport.js";
7
+ import { oauthPartitionFor, retainOAuthPartition } from "../oauth-partition.js";
6
8
  import { oauthSealerFor } from "../oauth-sealing.js";
7
9
  import { describeFailure } from "../operator-record.js";
8
- import { assertOAuthScope, authorizingContext, KvOAuthProvider, refreshCoordinatorsByPartition, } from "./downstream-oauth.js";
10
+ import { assertProviderCallOpen, bindProviderResponse, dispatchProviderRequest, providerRequest, readRequest, recordingFetch, recordProviderResponse, renewedAfterUnauthorized, takeProviderHandoff, vendorRefusal, } from "../provider-call.js";
11
+ import { viewSignal } from "../runtime/call.js";
12
+ import { runEdge } from "../runtime/run.js";
13
+ import { SharedRead } from "../runtime/shared-read.js";
14
+ import { sentSecretsFetch, sentSecretsFor, shortSecretWarning } from "../sent-secrets.js";
15
+ import { unsentAfterUnauthorized } from "../write-outcome.js";
9
16
  import { trackRemoteClientRequest } from "./downstream-client-metadata.js";
17
+ import { assertOAuthScope, authorizingContext, codeExchangeResponse, KvOAuthProvider, refreshCoordinatorsByPartition, } from "./downstream-oauth.js";
18
+ import { authView } from "./native-call.js";
19
+ import { authCall, authFlow, authInvoke, authMethod, authOwnMethod, authProgram, authValue, registerAuth, registerAuthMethod, } from "./native.js";
10
20
  import { trackOAuthStartReset } from "./oauth-start-reset.js";
21
+ import { bindRenewableGrant, OAUTH_CHALLENGE_MAX_BYTES } from "./renewable-grant.js";
22
+ /**
23
+ * Token-request header values Connecta itself sends, by lowercase name: JSON
24
+ * content negotiation and the media types maintained providers configure.
25
+ * Registering one would redact a vendor's name wherever its fragments recur.
26
+ * Any other configured value, under any name, may carry a credential and is
27
+ * registered.
28
+ */
29
+ export const PUBLIC_TOKEN_REQUEST_HEADERS = new Map([
30
+ ["accept", new Set(["application/json", "application/vnd.ccbchurch.v2+json"])],
31
+ ]);
11
32
  /**
12
33
  * Downstream OAuth for a hand-written `api()` connector: the authorization
13
34
  * code grant against endpoints and a pre-registered client the deployment
@@ -159,8 +180,8 @@ class StaticOAuthProvider extends KvOAuthProvider {
159
180
  settings;
160
181
  /** Set only for the code exchange of a PKCE-less flow. */
161
182
  exchangeCode;
162
- constructor(connectorId, ctx, coordinator, settings) {
163
- super(connectorId, ctx.storage, `${ctx.baseUrl}/oauth/callback/${connectorId}`, coordinator, ctx.allowAuthorization === true, oauthSealerFor(ctx), ctx.signal,
183
+ constructor(connectorId, ctx, coordinator, settings, requestOwned = false) {
184
+ super(connectorId, ctx.storage, `${ctx.baseUrl}/oauth/callback/${connectorId}`, coordinator, ctx.allowAuthorization === true, oauthSealerFor(ctx), viewSignal(ctx, requestOwned),
164
185
  // No client binding: the client is configuration, so there is no
165
186
  // registration for a restart to carry forward or discard.
166
187
  undefined, (reset) => trackOAuthStartReset(ctx.requestScope ?? ctx, reset), undefined, settings.scope, undefined, { secrets: sentSecretsFor(ctx) });
@@ -171,15 +192,8 @@ class StaticOAuthProvider extends KvOAuthProvider {
171
192
  * never written to storage, sealed or otherwise: it is deployment config,
172
193
  * not grant state, and an operator disconnect cannot delete it.
173
194
  */
174
- async clientInformation() {
175
- const { settings } = this;
176
- return {
177
- client_id: settings.clientId,
178
- ...(settings.clientSecret !== undefined ? { client_secret: settings.clientSecret } : {}),
179
- redirect_uris: [this.redirectUrl],
180
- token_endpoint_auth_method: settings.clientAuthentication,
181
- issuer: settings.identity,
182
- };
195
+ clientInformation() {
196
+ return runEdge(authOwnMethod(StaticOAuthProvider.prototype, this, "clientInformation", []));
183
197
  }
184
198
  configuredAuthorizationBinding() {
185
199
  return {
@@ -188,8 +202,8 @@ class StaticOAuthProvider extends KvOAuthProvider {
188
202
  tokenEndpoint: this.settings.tokenEndpoint.href,
189
203
  };
190
204
  }
191
- async saveClientInformation() {
192
- // Nothing to save: the SDK only saves a client it registered or stamped.
205
+ saveClientInformation() {
206
+ return runEdge(authOwnMethod(StaticOAuthProvider.prototype, this, "saveClientInformation", []));
193
207
  }
194
208
  /**
195
209
  * Discovery as a constant. Supplying both halves keeps the SDK from
@@ -198,28 +212,15 @@ class StaticOAuthProvider extends KvOAuthProvider {
198
212
  * The metadata names no `issuer`, so the SDK binds grants to the configured
199
213
  * token endpoint and does not demand an RFC 9207 `iss` it cannot know.
200
214
  */
201
- async discoveryState() {
202
- const { settings } = this;
203
- const metadata = {
204
- authorization_endpoint: settings.authorizationEndpoint.href,
205
- token_endpoint: settings.tokenEndpoint.href,
206
- response_types_supported: ["code"],
207
- grant_types_supported: ["authorization_code", "refresh_token"],
208
- code_challenge_methods_supported: ["S256"],
209
- token_endpoint_auth_methods_supported: [settings.clientAuthentication],
210
- };
211
- return {
212
- authorizationServerUrl: settings.identity,
213
- authorizationServerMetadata: metadata,
214
- resourceMetadata: { resource: settings.serverUrl.origin },
215
- };
215
+ discoveryState() {
216
+ return runEdge(authOwnMethod(StaticOAuthProvider.prototype, this, "discoveryState", []));
216
217
  }
217
- async saveDiscoveryState() {
218
- // Configuration is the discovery; there is nothing to remember.
218
+ saveDiscoveryState() {
219
+ return runEdge(authOwnMethod(StaticOAuthProvider.prototype, this, "saveDiscoveryState", []));
219
220
  }
220
221
  /** No RFC 8707 `resource` parameter: a plain REST API names no resource. */
221
- async validateResourceURL() {
222
- return undefined;
222
+ validateResourceURL() {
223
+ return runEdge(authOwnMethod(StaticOAuthProvider.prototype, this, "validateResourceURL", []));
223
224
  }
224
225
  /**
225
226
  * The code exchange without `code_verifier`, for a flow whose authorization
@@ -239,18 +240,122 @@ class StaticOAuthProvider extends KvOAuthProvider {
239
240
  exchanging(code) {
240
241
  this.exchangeCode = code;
241
242
  }
242
- async redirectToAuthorization(authorizationUrl) {
243
- for (const [name, value] of this.settings.authorizationParams) {
244
- authorizationUrl.searchParams.set(name, value);
245
- }
246
- if (!this.settings.pkce) {
247
- authorizationUrl.searchParams.delete("code_challenge");
248
- authorizationUrl.searchParams.delete("code_challenge_method");
249
- }
250
- await super.redirectToAuthorization(authorizationUrl);
243
+ redirectToAuthorization(authorizationUrl) {
244
+ return runEdge(authOwnMethod(StaticOAuthProvider.prototype, this, "redirectToAuthorization", [authorizationUrl]));
245
+ }
246
+ static {
247
+ registerAuthMethod(StaticOAuthProvider.prototype, "clientInformation", function () {
248
+ return authValue(() => {
249
+ const { settings } = this;
250
+ return {
251
+ client_id: settings.clientId,
252
+ ...(settings.clientSecret !== undefined ? { client_secret: settings.clientSecret } : {}),
253
+ redirect_uris: [this.redirectUrl],
254
+ token_endpoint_auth_method: settings.clientAuthentication,
255
+ issuer: settings.identity,
256
+ };
257
+ });
258
+ }, StaticOAuthProvider.prototype.clientInformation);
259
+ registerAuthMethod(StaticOAuthProvider.prototype, "saveClientInformation", function () {
260
+ // Nothing to save: the SDK only saves a client it registered or stamped.
261
+ return Effect.void;
262
+ }, StaticOAuthProvider.prototype.saveClientInformation);
263
+ registerAuthMethod(StaticOAuthProvider.prototype, "discoveryState", function () {
264
+ return authValue(() => {
265
+ const { settings } = this;
266
+ const metadata = {
267
+ authorization_endpoint: settings.authorizationEndpoint.href,
268
+ token_endpoint: settings.tokenEndpoint.href,
269
+ response_types_supported: ["code"],
270
+ grant_types_supported: ["authorization_code", "refresh_token"],
271
+ code_challenge_methods_supported: ["S256"],
272
+ token_endpoint_auth_methods_supported: [settings.clientAuthentication],
273
+ };
274
+ return {
275
+ authorizationServerUrl: settings.identity,
276
+ authorizationServerMetadata: metadata,
277
+ resourceMetadata: { resource: settings.serverUrl.origin },
278
+ };
279
+ });
280
+ }, StaticOAuthProvider.prototype.discoveryState);
281
+ registerAuthMethod(StaticOAuthProvider.prototype, "saveDiscoveryState", function () {
282
+ // Configuration is the discovery; there is nothing to remember.
283
+ return Effect.void;
284
+ }, StaticOAuthProvider.prototype.saveDiscoveryState);
285
+ registerAuthMethod(StaticOAuthProvider.prototype, "validateResourceURL", function () {
286
+ return authValue(() => {
287
+ return undefined;
288
+ });
289
+ }, StaticOAuthProvider.prototype.validateResourceURL);
290
+ registerAuthMethod(StaticOAuthProvider.prototype, "redirectToAuthorization", function (authorizationUrl) {
291
+ return authFlow(function* () {
292
+ for (const [name, value] of this.settings.authorizationParams) {
293
+ authorizationUrl.searchParams.set(name, value);
294
+ }
295
+ if (!this.settings.pkce) {
296
+ authorizationUrl.searchParams.delete("code_challenge");
297
+ authorizationUrl.searchParams.delete("code_challenge_method");
298
+ }
299
+ (yield authCall(KvOAuthProvider.prototype.redirectToAuthorization, [authorizationUrl], this));
300
+ }.bind(this));
301
+ }, StaticOAuthProvider.prototype.redirectToAuthorization);
251
302
  }
252
303
  }
253
304
  const AUTH_REQUIRED_MESSAGE = "Authorization required — open the URL to connect.";
305
+ /** Release the resource body before renewal, bounding even the facade's discarded challenge. */
306
+ function readChallenge(response, signal, method) {
307
+ return runEdge(authProgram(readChallengeNative, [response, signal, method]));
308
+ }
309
+ const readChallengeNative = /* @__PURE__ */ registerAuth(readChallenge, function (response, signal, method) {
310
+ return authFlow(function* () {
311
+ if (method?.toUpperCase() === "HEAD") {
312
+ void response.body?.cancel().catch(() => { });
313
+ return;
314
+ }
315
+ const oversized = () => new ConnectorCallError("connector_call_failed", "The OAuth challenge exceeded its response ceiling.", {
316
+ retryable: false,
317
+ });
318
+ if (Number(response.headers.get("content-length")) > OAUTH_CHALLENGE_MAX_BYTES) {
319
+ void response.body?.cancel().catch(() => { });
320
+ throw oversized();
321
+ }
322
+ if (!response.body)
323
+ return;
324
+ const reader = response.body.getReader();
325
+ const cancel = () => {
326
+ void reader.cancel().catch(() => { });
327
+ };
328
+ signal.addEventListener("abort", cancel, { once: true });
329
+ if (signal.aborted)
330
+ cancel();
331
+ let total = 0;
332
+ try {
333
+ while (true) {
334
+ let chunk;
335
+ try {
336
+ chunk = (yield authInvoke(reader, "read", []));
337
+ }
338
+ catch {
339
+ // Like the guarded client, a broken challenge body cannot replace its 401.
340
+ break;
341
+ }
342
+ if (signal.aborted)
343
+ throw signal.reason;
344
+ if (chunk.done)
345
+ break;
346
+ total += chunk.value.byteLength;
347
+ if (total > OAUTH_CHALLENGE_MAX_BYTES) {
348
+ cancel();
349
+ throw oversized();
350
+ }
351
+ }
352
+ }
353
+ finally {
354
+ signal.removeEventListener("abort", cancel);
355
+ reader.releaseLock();
356
+ }
357
+ });
358
+ });
254
359
  /** The grant behind one `api()` connector's `oauth`, as the hooks it exposes. */
255
360
  export function staticOAuth(id, config) {
256
361
  const settings = settingsFor(id, config);
@@ -261,266 +366,553 @@ export function staticOAuth(id, config) {
261
366
  const rejectedScopes = new WeakSet();
262
367
  const scopeOf = (ctx) => ctx.requestScope ?? ctx;
263
368
  const warnShortSecret = shortSecretWarning();
264
- const providerFor = (ctx) => {
369
+ const providerFor = (ctx, requestOwned = false) => {
265
370
  warnShortSecret(settings.clientSecret, ctx.logger);
266
- return new StaticOAuthProvider(id, ctx, coordinatorFor(ctx), settings);
371
+ return new StaticOAuthProvider(id, ctx, coordinatorFor(ctx), settings, requestOwned);
267
372
  };
268
373
  /**
269
- * The global `fetch`, read per request, with the configured token-request
374
+ * The owned credential transport, with the configured token-request
270
375
  * headers laid over the SDK's own. With discovery and registration answered
271
376
  * from configuration the token endpoint is the only URL the SDK fetches here;
272
377
  * the check keeps the headers on it even if that ever changes.
273
378
  */
274
- const tokenEndpointFetch = async (ctx, input, init = {}) => {
275
- const url = new URL(input);
276
- if (settings.tokenRequestHeaders.length === 0 || `${url.origin}${url.pathname}` !== settings.identity) {
277
- trackRemoteClientRequest(sentSecretsFor(ctx), input, init);
278
- return byteReadResponse(await sentSecretsFetch(ctx)(input, { ...init, redirect: "manual" }));
279
- }
280
- const headers = new Headers(init.headers);
281
- for (const [name, value] of settings.tokenRequestHeaders)
282
- headers.set(name, value);
283
- for (const [, value] of settings.tokenRequestHeaders)
284
- sentSecretsFor(ctx).header(value);
285
- trackRemoteClientRequest(sentSecretsFor(ctx), input, { ...init, headers });
286
- return byteReadResponse(await sentSecretsFetch(ctx)(input, { ...init, headers, redirect: "manual" }));
287
- };
379
+ const tokenEndpointFetch = authMethod((secrets, input, init = {}) => {
380
+ return authFlow(function* () {
381
+ const url = new URL(input);
382
+ const request = { ...init, redirect: "manual" };
383
+ if (settings.tokenRequestHeaders.length && `${url.origin}${url.pathname}` === settings.identity) {
384
+ const headers = new Headers(init.headers);
385
+ for (const [name, value] of settings.tokenRequestHeaders)
386
+ headers.set(name, value);
387
+ for (const [name, value] of settings.tokenRequestHeaders)
388
+ if (!PUBLIC_TOKEN_REQUEST_HEADERS.get(name.toLowerCase())?.has(value))
389
+ secrets.header(value);
390
+ request.headers = headers;
391
+ }
392
+ trackRemoteClientRequest(secrets, input, request);
393
+ // The coordinator or code-exchange wrapper authorizes this send. Refresh
394
+ // claim submission may outlive the caller; this transport uses no
395
+ // request-owned capability and changes no closed resource-call ledger.
396
+ const dispatch = {
397
+ dispatched: false,
398
+ auxiliary: true,
399
+ provider: id,
400
+ method: request.method ?? "GET",
401
+ };
402
+ secrets.request(input, request);
403
+ const response = (yield authCall(sendCredentialPost, [
404
+ input,
405
+ request,
406
+ () => dispatchProviderRequest(undefined, dispatch, request.headers),
407
+ ]));
408
+ recordProviderResponse(dispatch, response.status);
409
+ return byteReadResponse(response);
410
+ });
411
+ });
288
412
  /**
289
413
  * The SDK's `auth()` over this provider. The token endpoint is fetched
290
414
  * through the refresh coordinator, with permanent spent fingerprints,
291
415
  * rotation commits, and re-consent verdicts. Credential-bearing requests
292
416
  * never follow redirects in either OAuth adapter.
293
417
  */
294
- const runAuth = (provider, ctx, exchange) => auth(provider, {
295
- serverUrl: settings.serverUrl,
296
- ...exchange,
297
- fetchFn: coordinatorFor(ctx).coordinatedFetch(provider, (input, init) => tokenEndpointFetch(ctx, input, init), ctx.signal, ctx.defer),
418
+ const runAuth = authMethod((provider, ctx, exchange, requestOwned = false) => {
419
+ return authFlow(function* () {
420
+ const secrets = sentSecretsFor(ctx);
421
+ return (yield authCall(auth, [
422
+ provider,
423
+ {
424
+ serverUrl: settings.serverUrl,
425
+ ...exchange,
426
+ fetchFn: coordinatorFor(ctx).coordinatedFetch(provider, authMethod((input, init) => {
427
+ return authFlow(function* () {
428
+ if (exchange) {
429
+ // A code exchange has no submitted refresh claim transferring
430
+ // ownership. Only explicit scope revocation stops a callback:
431
+ // the production callback route remains uninterruptible and
432
+ // does not revoke its own scope while exchanging a code.
433
+ return (yield authView(ctx, authMethod((signal) => {
434
+ return authFlow(function* () {
435
+ assertProviderCallOpen(ctx, id, true);
436
+ const response = (yield authCall(tokenEndpointFetch, [
437
+ secrets,
438
+ input,
439
+ {
440
+ ...init,
441
+ signal: init?.signal ? AbortSignal.any([init.signal, signal]) : signal,
442
+ },
443
+ ]));
444
+ // An override can ignore abort. Never hand its late tokens
445
+ // to the SDK, which would otherwise persist both credentials.
446
+ if (signal.aborted) {
447
+ void response.body?.cancel().catch(() => { });
448
+ throw signal.reason;
449
+ }
450
+ // The SDK clones and reads this body after fetch resolves.
451
+ // Revocation must cancel that read even for a custom fetch.
452
+ return codeExchangeResponse(response, signal);
453
+ });
454
+ }), true));
455
+ }
456
+ return (yield authCall(tokenEndpointFetch, [secrets, input, init]));
457
+ });
458
+ }), viewSignal(ctx, requestOwned || exchange !== undefined), ctx.defer, undefined, () => {
459
+ guardedTransport(true);
460
+ }),
461
+ },
462
+ ]));
463
+ });
298
464
  });
299
465
  // No cause, as in remoteMcp(): the `UnauthorizedError` class is the whole
300
466
  // verdict, and a logger rendering the chain renders whatever it says.
301
467
  const authRequiredError = () => new ConnectorCallError("downstream_oauth_required", `Connector "${id}" requires authorization — call authorize_connector({ connector: "${id}" }) and open the returned URL.`);
302
468
  const disconnectedMessage = `Connector "${id}" was disconnected by an operator — explicitly start authorization to reconnect it.`;
303
469
  /**
304
- * One request through the owner's grant. A 401 earns exactly one recovery:
305
- * a token another request has already rotated in, or else one coordinated
306
- * refresh. A second 401 is `auth_required`, latched for the request scope
307
- * so a program's later calls do not each spend a refresh on it.
470
+ * The token to send after `spent` drew a 401: one another request has
471
+ * already rotated in, or else one coordinated refresh.
308
472
  */
309
- const authorizedFetch = async (ctx, input, init = {}) => {
310
- const url = new URL(input);
311
- if (!settings.apiOrigins.has(url.origin)) {
312
- throw new ConnectorCallError("connector_call_failed", `Connector "${id}" sends its OAuth access token only to its declared ` +
313
- `apiOrigins, and ${url.origin} is not one of them.`, { retryable: false });
314
- }
315
- if (init.body instanceof ReadableStream) {
316
- throw new TypeError(`Connector "${id}": ctx.oauth.fetch may replay a request once after a ` +
317
- "refresh, so it cannot send a stream body. Buffer the body first.");
318
- }
319
- const headers = new Headers(init.headers);
320
- if (headers.has("authorization")) {
321
- throw new ConnectorCallError("connector_call_failed", `Connector "${id}": a request through ctx.oauth.fetch may not set ` +
322
- "Authorization; the connector's grant owns it.", { retryable: false });
323
- }
324
- const signal = init.signal && ctx.signal ? AbortSignal.any([init.signal, ctx.signal]) : (init.signal ?? ctx.signal);
325
- const send = async (accessToken) => {
326
- sentSecretsFor(ctx).add(accessToken);
327
- const sent = new Headers(headers);
328
- sent.set("Authorization", `Bearer ${accessToken}`);
329
- // The token rides to a declared origin and no further: a redirect is
330
- // handed back to the handler unfollowed rather than re-sent anywhere.
331
- // The handler reads the answer with `.json()` or `.text()`, which read
332
- // bytes here: workerd quotes a text read's non-text Content-Type in
333
- // its own log, out of the handler's reach.
334
- const response = await sentSecretsFetch(ctx)(url, {
335
- ...init,
336
- headers: sent,
337
- redirect: "manual",
338
- ...(signal ? { signal } : {}),
473
+ const renewedToken = authMethod((provider, ctx, spent, requestOwned = false) => {
474
+ return authFlow(function* () {
475
+ // Only renewal whose claim CAS was submitted may survive this call.
476
+ // Race each preparation read too, and never resume SDK work or consume a
477
+ // rotated token after closure, even if a storage promise completes late.
478
+ const live = authMethod((work) => {
479
+ return authFlow(function* () {
480
+ if (ctx.signal?.aborted)
481
+ throw ctx.signal.reason;
482
+ const value = (yield authView(ctx, work, requestOwned));
483
+ if (ctx.signal?.aborted)
484
+ throw ctx.signal.reason;
485
+ return value;
486
+ });
339
487
  });
340
- return byteReadResponse(response);
488
+ const current = (yield authCall(live, [
489
+ authMethod(() => {
490
+ return authInvoke(provider, "tokens", [issuerContext]);
491
+ }),
492
+ ]));
493
+ if (current && current.access_token !== spent)
494
+ return current.access_token;
495
+ if ((yield authCall(live, [
496
+ authMethod(() => {
497
+ return authCall(runAuth, [provider, ctx, undefined, requestOwned]);
498
+ }),
499
+ ])) !== "AUTHORIZED")
500
+ throw authRequiredError();
501
+ const refreshed = (yield authCall(live, [
502
+ authMethod(() => {
503
+ return authInvoke(provider, "tokens", [issuerContext]);
504
+ }),
505
+ ]));
506
+ if (!refreshed)
507
+ throw authRequiredError();
508
+ return refreshed.access_token;
509
+ });
510
+ });
511
+ /** One call's grant use; submitted refresh work keeps the coordinator's owner. */
512
+ const grantFor = (ctx, requestOwned = false) => {
513
+ let provider;
514
+ const use = authMethod((work) => {
515
+ return authView(ctx, authMethod((signal) => {
516
+ return authFlow(function* () {
517
+ assertProviderCallOpen(ctx, id, requestOwned);
518
+ if (rejectedScopes.has(scopeOf(ctx)))
519
+ throw authRequiredError();
520
+ const live = deriveContext(ctx, { signal });
521
+ const release = retainOAuthPartition(oauthPartitionFor(live));
522
+ try {
523
+ provider ??= new SharedRead(() => authView(live, authMethod(() => {
524
+ return authFlow(function* () {
525
+ const value = providerFor(live, requestOwned);
526
+ const epoch = (yield authInvoke(value, "beginFlow", []));
527
+ if (value.isOperatorDisconnectedEpoch(epoch)) {
528
+ throw new ConnectorCallError("downstream_oauth_required", disconnectedMessage);
529
+ }
530
+ return value;
531
+ });
532
+ }), requestOwned), undefined, ctx.requestScope ?? ctx);
533
+ return (yield authCall(work, [(yield provider.join(signal)), live]));
534
+ }
535
+ catch (error) {
536
+ if (signal.aborted)
537
+ throw signal.reason;
538
+ if (error instanceof UnauthorizedError) {
539
+ rejectedScopes.add(scopeOf(ctx));
540
+ throw authRequiredError();
541
+ }
542
+ throw error;
543
+ }
544
+ finally {
545
+ release();
546
+ }
547
+ });
548
+ }), requestOwned);
549
+ });
550
+ return {
551
+ token: authMethod(() => {
552
+ return authCall(use, [
553
+ authMethod((provider) => {
554
+ return authFlow(function* () {
555
+ const tokens = (yield authInvoke(provider, "tokens", [issuerContext]));
556
+ if (!tokens)
557
+ throw authRequiredError();
558
+ return tokens.access_token;
559
+ });
560
+ }),
561
+ ]);
562
+ }),
563
+ refresh: authMethod((stale) => {
564
+ return authCall(use, [
565
+ authMethod((provider, live) => {
566
+ return authCall(renewedToken, [provider, live, stale, requestOwned]);
567
+ }),
568
+ ]);
569
+ }),
570
+ rejected: authMethod(() => {
571
+ return authView(ctx, authMethod(() => {
572
+ return authValue(() => {
573
+ rejectedScopes.add(scopeOf(ctx));
574
+ throw authRequiredError();
575
+ });
576
+ }), requestOwned);
577
+ }),
341
578
  };
342
- if (rejectedScopes.has(scopeOf(ctx)))
343
- throw authRequiredError();
344
- const release = retainOAuthPartition(oauthPartitionFor(ctx));
345
- try {
346
- if (signal?.aborted)
347
- throw signal.reason;
579
+ };
580
+ /**
581
+ * One request through the owner's grant. A 401 earns exactly one recovery
582
+ * ({@link renewedToken}). A read is then sent once more; a write never is
583
+ * (INV-9), since a server may act on a request before it rejects the
584
+ * token. Whatever recovery meets is never reported as retryable: an
585
+ * authorization verdict keeps its code, and anything else (a disconnect or
586
+ * reset racing the refresh) leaves the outcome unknown. A second 401 is
587
+ * `auth_required`, latched for the request scope so a program's later
588
+ * calls do not each spend a refresh on it.
589
+ */
590
+ const authorizedFetch = authMethod((ctx, input, init = {}) => {
591
+ return authFlow(function* () {
592
+ let prepared = takeProviderHandoff(init);
593
+ const auxiliary = prepared?.auxiliary === true;
594
+ // Own the complete token preparation and fetch helper. The coordinator
595
+ // still owns a submitted refresh claim independently of this wait.
596
+ return (yield authView(ctx, authMethod((owned) => {
597
+ return authFlow(function* () {
598
+ // A call that already ended reads no token and sends nothing more.
599
+ assertProviderCallOpen(ctx, id, auxiliary);
600
+ const owner = prepared;
601
+ const url = new URL(input);
602
+ if (!settings.apiOrigins.has(url.origin)) {
603
+ throw new ConnectorCallError("connector_call_failed", `Connector "${id}" sends its OAuth access token only to its declared ` +
604
+ `apiOrigins, and ${url.origin} is not one of them.`, { retryable: false });
605
+ }
606
+ if (init.body instanceof ReadableStream) {
607
+ throw new TypeError(`Connector "${id}": ctx.oauth.fetch may replay a request once after a ` +
608
+ "refresh, so it cannot send a stream body. Buffer the body first.");
609
+ }
610
+ const headers = new Headers(init.headers);
611
+ if (headers.has("authorization")) {
612
+ throw new ConnectorCallError("connector_call_failed", `Connector "${id}": a request through ctx.oauth.fetch may not set ` +
613
+ "Authorization; the connector's grant owns it.", { retryable: false });
614
+ }
615
+ const signal = init.signal ? AbortSignal.any([init.signal, owned]) : owned;
616
+ ctx = deriveContext(ctx, { signal });
617
+ const detachAnswers = [];
618
+ const send = authMethod((accessToken) => {
619
+ return authFlow(function* () {
620
+ if (signal?.aborted)
621
+ throw signal.reason;
622
+ sentSecretsFor(ctx).add(accessToken);
623
+ const sent = new Headers(headers);
624
+ sent.set("Authorization", `Bearer ${accessToken}`);
625
+ // The token rides to a declared origin and no further: a redirect is
626
+ // handed back to the handler unfollowed rather than re-sent anywhere.
627
+ // The handler reads the answer with `.json()` or `.text()`, which read
628
+ // bytes here: workerd quotes a text read's non-text Content-Type in
629
+ // its own log, out of the handler's reach.
630
+ const request = {
631
+ ...init,
632
+ headers: sent,
633
+ redirect: "manual",
634
+ ...(signal ? { signal } : {}),
635
+ };
636
+ const dispatch = prepared ?? providerRequest(ctx, auxiliary, id);
637
+ prepared = undefined;
638
+ dispatchProviderRequest(ctx, dispatch, sent, request.method ?? "GET");
639
+ // A native waiter can close before fetch settles. This leaf
640
+ // observer records only immutable response facts, and cancels
641
+ // a late body; it never resumes request-owned preparation.
642
+ const pending = sentSecretsFetch(ctx)(url, request).then((response) => {
643
+ recordProviderResponse(dispatch, response.status);
644
+ if (response.status === 401) {
645
+ // Closure can settle the handler before renewal's catch maps
646
+ // this answer. Preserve its OAuth verdict in the ledger first;
647
+ // only the final write decider reports it. A live renewal keeps
648
+ // its own precise verdict and never replays a write.
649
+ const abandoned = () => {
650
+ vendorRefusal(unsentAfterUnauthorized(`Connector "${id}"'s API`, signal.reason, "downstream_oauth_required"), dispatch);
651
+ };
652
+ if (signal.aborted)
653
+ abandoned();
654
+ else {
655
+ signal.addEventListener("abort", abandoned, { once: true });
656
+ detachAnswers.push(() => signal.removeEventListener("abort", abandoned));
657
+ }
658
+ }
659
+ // A custom fetch may answer after revocation. Its physical status
660
+ // remains a fact, but its body belongs to no live helper or handler.
661
+ if (signal.aborted)
662
+ void response.body?.cancel().catch(() => { });
663
+ return response;
664
+ });
665
+ const response = (yield authValue(() => pending));
666
+ if (signal.aborted)
667
+ throw signal.reason;
668
+ const answer = byteReadResponse(response);
669
+ bindProviderResponse(answer, dispatch, owner);
670
+ return { response: answer, dispatch };
671
+ });
672
+ });
673
+ const release = retainOAuthPartition(oauthPartitionFor(ctx));
674
+ try {
675
+ if (signal?.aborted)
676
+ throw signal.reason;
677
+ const grant = grantFor(ctx, auxiliary);
678
+ const stale = (yield authInvoke(grant, "token", []));
679
+ const { response: first, dispatch } = (yield authCall(send, [stale]));
680
+ if (first.status !== 401)
681
+ return first;
682
+ try {
683
+ (yield authCall(readChallenge, [first, signal, init.method]));
684
+ }
685
+ catch (error) {
686
+ if (signal.aborted)
687
+ throw signal.reason;
688
+ if (!readRequest(dispatch)) {
689
+ throw vendorRefusal(unsentAfterUnauthorized(`Connector "${id}"'s API`, error, "downstream_oauth_required"), dispatch);
690
+ }
691
+ throw error;
692
+ }
693
+ if (!readRequest(dispatch)) {
694
+ let renewal;
695
+ try {
696
+ const fresh = (yield authInvoke(grant, "refresh", [stale]));
697
+ if (signal?.aborted)
698
+ throw signal.reason;
699
+ // The call's ledger keeps this 401's verdict, or that renewal
700
+ // replaced the token this request sent (Q8). The same token back
701
+ // renewed nothing.
702
+ renewedAfterUnauthorized(dispatch, stale, fresh);
703
+ if (fresh === stale)
704
+ renewal = authRequiredError();
705
+ }
706
+ catch (error) {
707
+ if (error instanceof UnauthorizedError)
708
+ rejectedScopes.add(scopeOf(ctx));
709
+ renewal = error instanceof UnauthorizedError ? authRequiredError() : error;
710
+ }
711
+ throw vendorRefusal(unsentAfterUnauthorized(`Connector "${id}"'s API`, renewal, "downstream_oauth_required"), dispatch);
712
+ }
713
+ const accessToken = (yield authInvoke(grant, "refresh", [stale]));
714
+ if (signal?.aborted)
715
+ throw signal.reason;
716
+ renewedAfterUnauthorized(dispatch, stale, accessToken);
717
+ const { response: second } = (yield authCall(send, [accessToken]));
718
+ if (second.status !== 401)
719
+ return second;
720
+ void second.body?.cancel().catch(() => { });
721
+ return (yield authInvoke(grant, "rejected", []));
722
+ }
723
+ catch (error) {
724
+ // The SDK can replace cancellation with its passive-consent refusal,
725
+ // and write recovery normally replaces errors with a 401 verdict. A
726
+ // closed capability must still fail promptly with its own close reason;
727
+ // the invocation's ledger retains the write's uncertainty and handoff.
728
+ if (signal?.aborted)
729
+ throw signal.reason;
730
+ if (error instanceof UnauthorizedError) {
731
+ rejectedScopes.add(scopeOf(ctx));
732
+ throw authRequiredError();
733
+ }
734
+ throw error;
735
+ }
736
+ finally {
737
+ for (const detach of detachAnswers)
738
+ detach();
739
+ release();
740
+ }
741
+ });
742
+ }), auxiliary));
743
+ });
744
+ });
745
+ const status = authMethod((ctx) => {
746
+ return authFlow(function* () {
747
+ if (rejectedScopes.has(scopeOf(ctx))) {
748
+ return {
749
+ state: "auth_required",
750
+ message: "Authorization required \u2014 the downstream rejected this connector's OAuth grant.",
751
+ };
752
+ }
753
+ // A status read never starts authorization: this provider cannot, and
754
+ // nothing here calls the SDK. A stored grant for the configured token
755
+ // endpoint is healthy until a call says otherwise — the downstream is
756
+ // asked nothing.
348
757
  const provider = providerFor(ctx);
349
- // Bind this call's reads and writes to the live epoch: a reset landing
350
- // meanwhile fails the call rather than handing it another flow's grant.
351
- const epoch = await provider.beginFlow();
352
- if (provider.isOperatorDisconnectedEpoch(epoch)) {
353
- throw new ConnectorCallError("downstream_oauth_required", disconnectedMessage);
758
+ if ((yield authInvoke(provider, "operatorDisconnected", []))) {
759
+ return { state: "auth_required", message: disconnectedMessage };
354
760
  }
355
- const tokens = await provider.tokens(issuerContext);
356
- if (!tokens)
357
- throw authRequiredError();
358
- const first = await send(tokens.access_token);
359
- if (first.status !== 401)
360
- return first;
361
- void first.body?.cancel().catch(() => { });
362
- let accessToken;
363
- const current = await provider.tokens(issuerContext);
364
- if (current && current.access_token !== tokens.access_token) {
365
- accessToken = current.access_token;
761
+ return (yield authInvoke(provider, "tokens", [issuerContext]))
762
+ ? { state: "ok" }
763
+ : { state: "auth_required", message: AUTH_REQUIRED_MESSAGE };
764
+ });
765
+ });
766
+ const startAuth = authMethod((original, opts) => {
767
+ return authFlow(function* () {
768
+ const ctx = authorizingContext(original);
769
+ const provider = providerFor(ctx);
770
+ if (ctx.signal?.aborted)
771
+ throw ctx.signal.reason;
772
+ const disconnected = opts?.force
773
+ ? false
774
+ : (yield authInvoke(provider, "operatorDisconnected", []));
775
+ if (ctx.signal?.aborted)
776
+ throw ctx.signal.reason;
777
+ if (opts?.force || disconnected) {
778
+ (yield authInvoke(provider, "resetAuthorization", []));
779
+ rejectedScopes.delete(scopeOf(original));
366
780
  }
367
781
  else {
368
- if ((await runAuth(provider, ctx)) !== "AUTHORIZED")
369
- throw authRequiredError();
370
- const refreshed = await provider.tokens(issuerContext);
371
- if (!refreshed)
372
- throw authRequiredError();
373
- accessToken = refreshed.access_token;
374
- }
375
- const second = await send(accessToken);
376
- if (second.status !== 401)
377
- return second;
378
- void second.body?.cancel().catch(() => { });
379
- rejectedScopes.add(scopeOf(ctx));
380
- throw authRequiredError();
381
- }
382
- catch (error) {
383
- if (error instanceof UnauthorizedError) {
384
- rejectedScopes.add(scopeOf(ctx));
385
- throw authRequiredError();
782
+ // Hand back a recent consent URL rather than overwrite the verifier
783
+ // the operator may be mid-consent on.
784
+ const pending = (yield authInvoke(provider, "reusablePendingAuthorizationUrl", []));
785
+ if (pending) {
786
+ return {
787
+ state: "auth_required",
788
+ authorizationUrl: pending,
789
+ authorizationReused: true,
790
+ message: AUTH_REQUIRED_MESSAGE,
791
+ };
792
+ }
386
793
  }
387
- throw error;
388
- }
389
- finally {
390
- release();
391
- }
392
- };
393
- const status = async (ctx) => {
394
- if (rejectedScopes.has(scopeOf(ctx))) {
395
- return {
396
- state: "auth_required",
397
- message: "Authorization required — the downstream rejected this connector's OAuth grant.",
398
- };
399
- }
400
- // A status read never starts authorization: this provider cannot, and
401
- // nothing here calls the SDK. A stored grant for the configured token
402
- // endpoint is healthy until a call says otherwise — the downstream is
403
- // asked nothing.
404
- const provider = providerFor(ctx);
405
- if (await provider.operatorDisconnected()) {
406
- return { state: "auth_required", message: disconnectedMessage };
407
- }
408
- return (await provider.tokens(issuerContext))
409
- ? { state: "ok" }
410
- : { state: "auth_required", message: AUTH_REQUIRED_MESSAGE };
411
- };
412
- const startAuth = async (original, opts) => {
413
- const ctx = authorizingContext(original);
414
- const provider = providerFor(ctx);
415
- if (ctx.signal?.aborted)
416
- throw ctx.signal.reason;
417
- const disconnected = opts?.force ? false : await provider.operatorDisconnected();
418
- if (ctx.signal?.aborted)
419
- throw ctx.signal.reason;
420
- if (opts?.force || disconnected) {
421
- await provider.resetAuthorization();
422
- rejectedScopes.delete(scopeOf(original));
423
- }
424
- else {
425
- // Hand back a recent consent URL rather than overwrite the verifier
426
- // the operator may be mid-consent on.
427
- const pending = await provider.reusablePendingAuthorizationUrl();
428
- if (pending) {
794
+ if (ctx.signal?.aborted)
795
+ throw ctx.signal.reason;
796
+ try {
797
+ // A grant from a since-repointed token endpoint reads as absent here,
798
+ // and the consent this start begins replaces it.
799
+ (yield authInvoke(provider, "beginFlow", []));
800
+ if ((yield authInvoke(provider, "tokens", [issuerContext]))) {
801
+ return { state: "ok", message: "Already authorized \u2014 connection is healthy." };
802
+ }
803
+ if ((yield authCall(runAuth, [provider, ctx])) === "AUTHORIZED") {
804
+ return { state: "ok", message: "Already authorized \u2014 connection is healthy." };
805
+ }
806
+ const authorizationUrl = (yield authInvoke(provider, "consentUrl", []));
429
807
  return {
430
808
  state: "auth_required",
431
- authorizationUrl: pending,
432
- authorizationReused: true,
809
+ ...(authorizationUrl !== undefined ? { authorizationUrl } : {}),
433
810
  message: AUTH_REQUIRED_MESSAGE,
434
811
  };
435
812
  }
436
- }
437
- if (ctx.signal?.aborted)
438
- throw ctx.signal.reason;
439
- try {
440
- // A grant from a since-repointed token endpoint reads as absent here,
441
- // and the consent this start begins replaces it.
442
- await provider.beginFlow();
443
- if (await provider.tokens(issuerContext)) {
444
- return { state: "ok", message: "Already authorized — connection is healthy." };
445
- }
446
- if ((await runAuth(provider, ctx)) === "AUTHORIZED") {
447
- return { state: "ok", message: "Already authorized — connection is healthy." };
448
- }
449
- const authorizationUrl = await provider.consentUrl();
450
- return {
451
- state: "auth_required",
452
- ...(authorizationUrl !== undefined ? { authorizationUrl } : {}),
453
- message: AUTH_REQUIRED_MESSAGE,
454
- };
455
- }
456
- catch (error) {
457
- if (error instanceof UnauthorizedError) {
458
- return { state: "auth_required", message: AUTH_REQUIRED_MESSAGE };
813
+ catch (error) {
814
+ if (error instanceof UnauthorizedError) {
815
+ return { state: "auth_required", message: AUTH_REQUIRED_MESSAGE };
816
+ }
817
+ // Reaches the agent through authorize_connector. Text already in
818
+ // connecta's words passes with its advice to try again, since this is
819
+ // no write's failure (token responses are rebuilt before the SDK reads
820
+ // them); anything else is told from its record.
821
+ return {
822
+ state: "error",
823
+ message: error instanceof ConnectorCallError || error instanceof OAuthError
824
+ ? withResendAdvice(msg(error), resendAdviceOf(error))
825
+ : describeFailure(id, error),
826
+ };
459
827
  }
460
- // Reaches the agent through authorize_connector. Text already in
461
- // connecta's words passes (token responses are rebuilt before the SDK
462
- // reads them); anything else is told from its record.
463
- return {
464
- state: "error",
465
- message: error instanceof ConnectorCallError || error instanceof OAuthError ? msg(error) : describeFailure(id, error),
466
- };
467
- }
468
- };
469
- const disconnectAuth = async (ctx) => {
470
- rejectedScopes.delete(scopeOf(ctx));
471
- await providerFor(ctx).resetAuthorization(true);
472
- };
473
- const verifyState = async (state, ctx) => {
474
- // The provider that verified the state captured its flow's epoch, and the
475
- // exchange must write under exactly that one.
476
- const provider = providerFor(ctx);
477
- callbackProviders.set(scopeOf(ctx), provider);
478
- return provider.verifyState(state);
479
- };
480
- const finishAuth = async (code, ctx, callbackParams) => {
481
- const provider = callbackProviders.get(scopeOf(ctx)) ?? providerFor(ctx);
482
- callbackProviders.delete(scopeOf(ctx));
483
- const authorizationCode = callbackParams?.get("code") ?? code;
484
- const iss = callbackParams?.get("iss") ?? undefined;
485
- // Every exchange names and claims its own consent, including without PKCE.
486
- const consent = callbackParams?.get("state") ?? null;
487
- if (!consent || !(await provider.verifyCallbackState(consent))) {
488
- throw new ConnectorCallError("connector_call_failed", `Connector "${id}" authorization callback matches no pending consent; nothing was exchanged.`);
489
- }
490
- provider.validateCallbackIssuer(callbackParams?.get("iss") ?? null);
491
- provider.exchanging(authorizationCode);
492
- await provider.bindFlow();
493
- const result = await runAuth(provider, ctx, {
494
- authorizationCode,
495
- ...(iss !== undefined ? { iss } : {}),
496
828
  });
497
- if (result !== "AUTHORIZED") {
498
- throw new UnauthorizedError("Failed to authorize");
499
- }
500
- rejectedScopes.delete(scopeOf(ctx));
501
- };
829
+ });
830
+ const disconnectAuth = authMethod((ctx) => {
831
+ return authFlow(function* () {
832
+ rejectedScopes.delete(scopeOf(ctx));
833
+ (yield authInvoke(providerFor(ctx), "resetAuthorization", [true]));
834
+ });
835
+ });
836
+ const verifyState = authMethod((state, ctx) => {
837
+ return authFlow(function* () {
838
+ // The provider that verified the state captured its flow's epoch, and the
839
+ // exchange must write under exactly that one.
840
+ const provider = providerFor(ctx);
841
+ callbackProviders.set(scopeOf(ctx), provider);
842
+ return (yield authInvoke(provider, "verifyState", [state]));
843
+ });
844
+ });
845
+ const finishAuth = authMethod((code, ctx, callbackParams) => {
846
+ return authView(ctx, authMethod(() => {
847
+ return authFlow(function* () {
848
+ const provider = callbackProviders.get(scopeOf(ctx)) ?? providerFor(ctx);
849
+ callbackProviders.delete(scopeOf(ctx));
850
+ const authorizationCode = callbackParams?.get("code") ?? code;
851
+ const iss = callbackParams?.get("iss") ?? undefined;
852
+ // Every exchange names and claims its own consent, including without PKCE.
853
+ const consent = callbackParams?.get("state") ?? null;
854
+ if (!consent || !(yield authInvoke(provider, "verifyCallbackState", [consent]))) {
855
+ throw new ConnectorCallError("connector_call_failed", `Connector "${id}" authorization callback matches no pending consent; nothing was exchanged.`);
856
+ }
857
+ provider.validateCallbackIssuer(callbackParams?.get("iss") ?? null);
858
+ provider.exchanging(authorizationCode);
859
+ (yield authInvoke(provider, "bindFlow", []));
860
+ const result = (yield authCall(runAuth, [
861
+ provider,
862
+ ctx,
863
+ {
864
+ authorizationCode,
865
+ ...(iss !== undefined ? { iss } : {}),
866
+ },
867
+ ]));
868
+ if (result !== "AUTHORIZED") {
869
+ throw new UnauthorizedError("Failed to authorize");
870
+ }
871
+ rejectedScopes.delete(scopeOf(ctx));
872
+ });
873
+ }), true);
874
+ });
502
875
  return {
503
- status: retainingOAuthPartition(status, 0),
504
- startAuth: retainingOAuthPartition(startAuth, 0),
505
- disconnectAuth: retainingOAuthPartition(disconnectAuth, 0),
506
- verifyState: retainingOAuthPartition(verifyState, 1),
507
- verifyCallbackIssuer: retainingOAuthPartition(async (issuer, ctx) => {
508
- const provider = callbackProviders.get(scopeOf(ctx));
509
- if (!provider)
510
- return false;
511
- provider.validateCallbackIssuer(issuer);
512
- return true;
513
- }, 1),
514
- consumeAuthError: retainingOAuthPartition(async (ctx) => {
515
- const provider = callbackProviders.get(scopeOf(ctx));
516
- callbackProviders.delete(scopeOf(ctx));
517
- if (!provider)
518
- throw new ConnectorCallError("connector_call_failed", "OAuth error callback matches no pending consent.");
519
- await provider.consumeAuthError();
520
- }, 0),
521
- finishAuth: retainingOAuthPartition(finishAuth, 1),
522
- access: (ctx) => ({
523
- fetch: (input, init) => authorizedFetch(ctx, input, init),
524
- }),
876
+ status: retainAuthPartition(status, 0),
877
+ startAuth: retainAuthPartition(startAuth, 0),
878
+ disconnectAuth: retainAuthPartition(disconnectAuth, 0),
879
+ verifyState: retainAuthPartition(verifyState, 1),
880
+ verifyCallbackIssuer: retainAuthPartition(authMethod((issuer, ctx) => {
881
+ return authValue(() => {
882
+ const provider = callbackProviders.get(scopeOf(ctx));
883
+ if (!provider)
884
+ return false;
885
+ provider.validateCallbackIssuer(issuer);
886
+ return true;
887
+ });
888
+ }), 1),
889
+ consumeAuthError: retainAuthPartition(authMethod((ctx) => {
890
+ return authFlow(function* () {
891
+ const provider = callbackProviders.get(scopeOf(ctx));
892
+ callbackProviders.delete(scopeOf(ctx));
893
+ if (!provider)
894
+ throw new ConnectorCallError("connector_call_failed", "OAuth error callback matches no pending consent.");
895
+ (yield authInvoke(provider, "consumeAuthError", []));
896
+ });
897
+ }), 0),
898
+ finishAuth: retainAuthPartition(finishAuth, 1),
899
+ access: (ctx) => bindRenewableGrant({
900
+ fetch: recordingFetch(authMethod((input, init) => {
901
+ return authCall(authorizedFetch, [ctx, input, init]);
902
+ })),
903
+ }, grantFor(ctx)),
525
904
  };
526
905
  }
906
+ // Hook calls keep the existing owner partition pinned while their native
907
+ // program settles. Each Promise shell enters its own registered program.
908
+ function retainAuthPartition(operation, contextIndex = 0) {
909
+ return authMethod((...args) => authFlow(function* () {
910
+ const release = retainOAuthPartition(oauthPartitionFor(args[contextIndex]));
911
+ try {
912
+ return (yield authCall(operation, args));
913
+ }
914
+ finally {
915
+ release();
916
+ }
917
+ }));
918
+ }