@zackbart/connecta 0.34.1 → 0.36.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 (441) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/README.md +65 -105
  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 +2044 -1161
  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 +751 -281
  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 +29 -7
  53. package/dist/connectors/api-connector.js +145 -57
  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 +227 -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 +379 -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/contract.d.ts +8 -3
  109. package/dist/operator-ui/generated.js +4 -4
  110. package/dist/operator-ui/model.d.ts +2 -3
  111. package/dist/program-writes.js +47 -26
  112. package/dist/provider-call-facts.d.ts +115 -0
  113. package/dist/provider-call-facts.js +182 -0
  114. package/dist/provider-call.d.ts +112 -0
  115. package/dist/provider-call.js +271 -0
  116. package/dist/provider.d.ts +65 -31
  117. package/dist/provider.js +116 -39
  118. package/dist/providers/_shared/google/hatch.js +91 -0
  119. package/dist/providers/_shared/google/kit.js +427 -0
  120. package/dist/providers/_shared/google/result-size.js +15 -2
  121. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  122. package/dist/providers/_shared/google/service-account.js +8 -4
  123. package/dist/providers/_shared/google/workspace.d.ts +6 -210
  124. package/dist/providers/_shared/google/workspace.js +6 -425
  125. package/dist/providers/_shared/rest/compact-index.js +86 -0
  126. package/dist/providers/_shared/rest/kit.js +146 -0
  127. package/dist/providers/_shared/rest/operation-index.js +230 -14
  128. package/dist/providers/_shared/rest/response-media.js +11 -0
  129. package/dist/providers/_shared/rest/value-safety.js +353 -195
  130. package/dist/providers/_shared/sensitive-fields.js +27 -0
  131. package/dist/providers/breeze/api.js +584 -0
  132. package/dist/providers/breeze/index.d.ts +1 -3
  133. package/dist/providers/breeze/index.js +67 -1420
  134. package/dist/providers/breeze/openapi.generated.js +60 -0
  135. package/dist/providers/breeze/skill.generated.js +2 -6
  136. package/dist/providers/breeze/tools.js +696 -0
  137. package/dist/providers/ccb/api.js +496 -0
  138. package/dist/providers/ccb/index.d.ts +15 -37
  139. package/dist/providers/ccb/index.js +77 -1702
  140. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  141. package/dist/providers/ccb/lib/constants.js +74 -0
  142. package/dist/providers/ccb/lib/guide.js +9 -0
  143. package/dist/providers/ccb/lib/verdicts.js +387 -0
  144. package/dist/providers/ccb/openapi.generated.js +397 -0
  145. package/dist/providers/ccb/safety.js +295 -0
  146. package/dist/providers/ccb/skill.generated.js +2 -6
  147. package/dist/providers/ccb/tools.js +853 -0
  148. package/dist/providers/cloudflare/api.js +1066 -0
  149. package/dist/providers/cloudflare/index.d.ts +5 -22
  150. package/dist/providers/cloudflare/index.js +79 -197
  151. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  152. package/dist/providers/cloudflare/lib/config.js +2 -0
  153. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  154. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  155. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  156. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  157. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  158. package/dist/providers/cloudflare/openapi.generated.js +4 -3
  159. package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
  160. package/dist/providers/cloudflare/skill.generated.js +2 -10
  161. package/dist/providers/cloudflare/tools.js +360 -0
  162. package/dist/providers/docs/api.js +51 -0
  163. package/dist/providers/docs/index.d.ts +1 -12
  164. package/dist/providers/docs/index.js +53 -1165
  165. package/dist/providers/docs/lib/constants.d.ts +18 -0
  166. package/dist/providers/docs/lib/constants.js +18 -0
  167. package/dist/providers/docs/lib/discovery.js +26 -0
  168. package/dist/providers/docs/lib/requests.js +48 -0
  169. package/dist/providers/docs/lib/schemas.js +168 -0
  170. package/dist/providers/docs/openapi.generated.js +16 -0
  171. package/dist/providers/docs/safety.js +54 -0
  172. package/dist/providers/docs/skill.generated.js +2 -6
  173. package/dist/providers/docs/tools.js +801 -0
  174. package/dist/providers/drive/api.js +177 -0
  175. package/dist/providers/drive/index.d.ts +1 -12
  176. package/dist/providers/drive/index.js +38 -1448
  177. package/dist/providers/drive/lib/constants.d.ts +6 -0
  178. package/dist/providers/drive/lib/constants.js +6 -0
  179. package/dist/providers/drive/lib/contracts.js +532 -0
  180. package/dist/providers/drive/lib/hatch.js +197 -0
  181. package/dist/providers/drive/openapi.generated.js +77 -0
  182. package/dist/providers/drive/safety.js +90 -0
  183. package/dist/providers/drive/skill.generated.js +2 -6
  184. package/dist/providers/drive/tools.js +1290 -0
  185. package/dist/providers/forms/api.js +71 -0
  186. package/dist/providers/forms/index.d.ts +1 -12
  187. package/dist/providers/forms/index.js +49 -1145
  188. package/dist/providers/forms/lib/constants.d.ts +14 -0
  189. package/dist/providers/forms/lib/constants.js +19 -0
  190. package/dist/providers/forms/lib/hatch.js +72 -0
  191. package/dist/providers/forms/openapi.generated.js +23 -0
  192. package/dist/providers/forms/safety.js +34 -0
  193. package/dist/providers/forms/skill.generated.js +2 -8
  194. package/dist/providers/forms/tools.js +1023 -0
  195. package/dist/providers/github/api.js +150 -0
  196. package/dist/providers/github/index.d.ts +4 -5
  197. package/dist/providers/github/index.js +30 -87
  198. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  199. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  200. package/dist/providers/github/lib/discovery.js +132 -0
  201. package/dist/providers/github/lib/operations.js +58 -0
  202. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  203. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  204. package/dist/providers/github/openapi.generated.js +1216 -0
  205. package/dist/providers/github/safety.js +216 -0
  206. package/dist/providers/github/skill.generated.js +2 -5
  207. package/dist/providers/github/tools.js +506 -0
  208. package/dist/providers/gmail/api.js +114 -0
  209. package/dist/providers/gmail/index.d.ts +1 -12
  210. package/dist/providers/gmail/index.js +73 -1621
  211. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  212. package/dist/providers/gmail/lib/constants.js +16 -0
  213. package/dist/providers/gmail/lib/index.js +173 -0
  214. package/dist/providers/gmail/lib/schemas.js +176 -0
  215. package/dist/providers/gmail/lib/threading.js +120 -0
  216. package/dist/providers/gmail/openapi.generated.js +92 -0
  217. package/dist/providers/gmail/safety.js +21 -0
  218. package/dist/providers/gmail/skill.generated.js +2 -6
  219. package/dist/providers/gmail/tools.js +1435 -0
  220. package/dist/providers/overflow/api.js +1008 -0
  221. package/dist/providers/overflow/index.d.ts +1 -17
  222. package/dist/providers/overflow/index.js +60 -1488
  223. package/dist/providers/overflow/skill.generated.js +2 -5
  224. package/dist/providers/overflow/tools.js +873 -0
  225. package/dist/providers/planning-center/api.js +467 -0
  226. package/dist/providers/planning-center/index.d.ts +4 -35
  227. package/dist/providers/planning-center/index.js +69 -2445
  228. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  229. package/dist/providers/planning-center/lib/constants.js +83 -0
  230. package/dist/providers/planning-center/lib/discovery.js +40 -0
  231. package/dist/providers/planning-center/lib/guide.js +39 -0
  232. package/dist/providers/planning-center/lib/json-api.js +83 -0
  233. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  234. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  235. package/dist/providers/planning-center/safety.js +188 -0
  236. package/dist/providers/planning-center/skill.generated.js +2 -27
  237. package/dist/providers/planning-center/tools.js +1010 -0
  238. package/dist/providers/quickbooks/api.js +299 -0
  239. package/dist/providers/quickbooks/index.d.ts +13 -0
  240. package/dist/providers/quickbooks/index.js +93 -0
  241. package/dist/providers/quickbooks/lib/query.js +118 -0
  242. package/dist/providers/quickbooks/lib/writes.js +168 -0
  243. package/dist/providers/quickbooks/safety.js +222 -0
  244. package/dist/providers/quickbooks/skill.generated.js +5 -0
  245. package/dist/providers/quickbooks/tools.js +117 -0
  246. package/dist/providers/sheets/api.js +133 -0
  247. package/dist/providers/sheets/index.d.ts +4 -16
  248. package/dist/providers/sheets/index.js +61 -1172
  249. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  250. package/dist/providers/sheets/lib/constants.js +14 -0
  251. package/dist/providers/sheets/lib/hatch.js +163 -0
  252. package/dist/providers/sheets/lib/requests.js +105 -0
  253. package/dist/providers/sheets/lib/schemas.js +138 -0
  254. package/dist/providers/sheets/openapi.generated.js +30 -0
  255. package/dist/providers/sheets/safety.js +27 -0
  256. package/dist/providers/sheets/skill.generated.js +2 -7
  257. package/dist/providers/sheets/tools.js +733 -0
  258. package/dist/providers/slides/api.js +147 -0
  259. package/dist/providers/slides/index.d.ts +2 -10
  260. package/dist/providers/slides/index.js +48 -2555
  261. package/dist/providers/slides/lib/constants.d.ts +11 -0
  262. package/dist/providers/slides/lib/constants.js +11 -0
  263. package/dist/providers/slides/lib/requests.js +60 -0
  264. package/dist/providers/slides/lib/schemas.js +565 -0
  265. package/dist/providers/slides/openapi.generated.js +18 -0
  266. package/dist/providers/slides/skill.generated.js +2 -6
  267. package/dist/providers/slides/tools.js +1968 -0
  268. package/dist/providers/stripe/api.js +401 -0
  269. package/dist/providers/stripe/index.d.ts +5 -27
  270. package/dist/providers/stripe/index.js +74 -210
  271. package/dist/providers/stripe/lib/config.d.ts +4 -0
  272. package/dist/providers/stripe/lib/config.js +2 -0
  273. package/dist/providers/stripe/openapi.generated.js +4 -3
  274. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  275. package/dist/providers/stripe/skill.generated.js +2 -11
  276. package/dist/providers/stripe/tools.js +77 -0
  277. package/dist/providers/tithely/api.js +531 -0
  278. package/dist/providers/tithely/index.d.ts +17 -0
  279. package/dist/providers/tithely/index.js +86 -965
  280. package/dist/providers/tithely/skill.generated.js +2 -5
  281. package/dist/providers/tithely/tools.js +426 -0
  282. package/dist/providers/vercel/api.js +551 -0
  283. package/dist/providers/vercel/index.d.ts +9 -25
  284. package/dist/providers/vercel/index.js +65 -260
  285. package/dist/providers/vercel/lib/config.d.ts +2 -0
  286. package/dist/providers/vercel/lib/config.js +2 -0
  287. package/dist/providers/vercel/lib/index.js +117 -0
  288. package/dist/providers/vercel/lib/logs.js +100 -0
  289. package/dist/providers/vercel/lib/pin.js +270 -0
  290. package/dist/providers/vercel/openapi.generated.js +10 -5
  291. package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
  292. package/dist/providers/vercel/skill.generated.js +2 -7
  293. package/dist/providers/vercel/tools.js +642 -0
  294. package/dist/redaction-identity.js +47 -0
  295. package/dist/redaction-marker.js +125 -0
  296. package/dist/registry.d.ts +21 -15
  297. package/dist/registry.js +409 -376
  298. package/dist/routes/access-tokens.js +1 -1
  299. package/dist/routes/actor-labels.js +1 -1
  300. package/dist/routes/connect.js +1 -1
  301. package/dist/routes/dispatch.js +103 -0
  302. package/dist/routes/mcp.js +170 -79
  303. package/dist/routes/oauth.js +6 -1
  304. package/dist/routes/operator.js +2 -2
  305. package/dist/routes/shared.d.ts +5 -4
  306. package/dist/routes/shared.js +244 -177
  307. package/dist/routes/skills.js +10 -8
  308. package/dist/routes/ui-config.js +27 -3
  309. package/dist/routes/ui-data.js +21 -20
  310. package/dist/routes/ui.js +1 -1
  311. package/dist/runtime/call-admission.js +0 -26
  312. package/dist/runtime/call-entry.js +30 -0
  313. package/dist/runtime/call.js +552 -0
  314. package/dist/runtime/connector-scope.js +7 -0
  315. package/dist/runtime/credential-probe.js +34 -0
  316. package/dist/runtime/executor.js +40 -0
  317. package/dist/runtime/native-storage.js +63 -0
  318. package/dist/runtime/registry.js +27 -0
  319. package/dist/runtime/request-scope.js +72 -0
  320. package/dist/runtime/run.js +15 -7
  321. package/dist/runtime/shared-read.js +34 -5
  322. package/dist/runtime/storage.js +34 -21
  323. package/dist/sent-secrets.d.ts +194 -37
  324. package/dist/sent-secrets.js +1743 -340
  325. package/dist/server.js +5 -56
  326. package/dist/skills.js +322 -223
  327. package/dist/sqlite.js +15 -14
  328. package/dist/storage/d1-copy.js +255 -0
  329. package/dist/storage/d1-driver.js +15 -0
  330. package/dist/storage/keys.js +28 -0
  331. package/dist/storage/sql-driver.js +88 -0
  332. package/dist/storage/sql-native.js +544 -0
  333. package/dist/storage/sql-schema.d.ts +6 -0
  334. package/dist/storage/sql-schema.js +15 -0
  335. package/dist/storage/sql.d.ts +14 -20
  336. package/dist/storage/sql.js +29 -535
  337. package/dist/timeout.js +0 -26
  338. package/dist/tool-safety.js +20 -10
  339. package/dist/types.d.ts +9 -109
  340. package/dist/ui.d.ts +1 -1
  341. package/dist/ui.js +0 -1
  342. package/dist/upward.js +3 -0
  343. package/dist/usage-guide.js +3 -1
  344. package/dist/validate.d.ts +4 -2
  345. package/dist/validate.js +86 -9
  346. package/dist/version.d.ts +1 -1
  347. package/dist/version.js +1 -1
  348. package/dist/worker.js +312 -292
  349. package/dist/workspace-subject.js +5 -1
  350. package/dist/write-outcome.js +454 -34
  351. package/documentation/README.md +28 -7
  352. package/documentation/architecture.md +2409 -255
  353. package/documentation/auth.md +867 -114
  354. package/documentation/code-mode.md +49 -16
  355. package/documentation/deploying.md +164 -20
  356. package/documentation/hosted-mcp-migration.md +460 -0
  357. package/documentation/integrating.md +129 -144
  358. package/documentation/meta-tools.md +409 -32
  359. package/documentation/operating.md +27 -3
  360. package/documentation/operator-tests.md +3 -0
  361. package/documentation/operator-ui.md +84 -48
  362. package/documentation/provider-auth-migration.md +16 -63
  363. package/documentation/provider-migration-0.29.md +10 -5
  364. package/documentation/provider-migration-0.35.md +975 -0
  365. package/documentation/providers.md +1347 -0
  366. package/examples/worker/AGENTS.md +23 -9
  367. package/examples/worker/README.md +254 -135
  368. package/examples/worker/alchemy.run.ts +116 -0
  369. package/examples/worker/gitignore +10 -0
  370. package/examples/worker/src/connecta.config.ts +23 -21
  371. package/examples/worker/wrangler.jsonc +14 -5
  372. package/guides/connecting-agents.md +157 -0
  373. package/guides/how-it-works.md +156 -0
  374. package/guides/safety-and-trust.md +153 -0
  375. package/package.json +30 -50
  376. package/templates/node/.env.example +3 -0
  377. package/templates/node/README.md +22 -2
  378. package/templates/node/docker-compose.yml +1 -0
  379. package/templates/node/package.json +3 -2
  380. package/templates/node/src/connecta.config.ts +8 -0
  381. package/dist/catalog-drift.d.ts +0 -138
  382. package/dist/catalog-drift.js +0 -578
  383. package/dist/operator-ui/agents.d.ts +0 -74
  384. package/dist/operator-ui/agents.js +0 -104
  385. package/dist/operator-ui/config-summary.js +0 -565
  386. package/dist/operator-ui/connection-map.js +0 -376
  387. package/dist/operator-ui/endpoints.js +0 -221
  388. package/dist/operator-ui/fix-prompts.d.ts +0 -18
  389. package/dist/operator-ui/fix-prompts.js +0 -87
  390. package/dist/operator-ui/health.js +0 -52
  391. package/dist/operator-ui/member-page.js +0 -97
  392. package/dist/operator-ui/overview.js +0 -123
  393. package/dist/operator-ui/setup-commands.js +0 -51
  394. package/dist/operator-ui/view.d.ts +0 -401
  395. package/dist/operator-ui/view.js +0 -705
  396. package/dist/providers/_shared/request-token.js +0 -83
  397. package/dist/providers/_shared/rest/dispatch.js +0 -41
  398. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  399. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  400. package/dist/providers/_shared/rest/tools.js +0 -569
  401. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  402. package/dist/providers/basecamp/index.d.ts +0 -45
  403. package/dist/providers/basecamp/index.js +0 -997
  404. package/dist/providers/basecamp/skill.generated.js +0 -9
  405. package/dist/providers/cloudflare/rest.d.ts +0 -37
  406. package/dist/providers/cloudflare/rest.js +0 -1097
  407. package/dist/providers/github/hosted.js +0 -218
  408. package/dist/providers/github/rest.js +0 -321
  409. package/dist/providers/infisical/index.d.ts +0 -7
  410. package/dist/providers/infisical/index.js +0 -713
  411. package/dist/providers/infisical/skill.generated.js +0 -8
  412. package/dist/providers/intercom/index.d.ts +0 -24
  413. package/dist/providers/intercom/index.js +0 -121
  414. package/dist/providers/intercom/skill.generated.js +0 -8
  415. package/dist/providers/linear/index.d.ts +0 -55
  416. package/dist/providers/linear/index.js +0 -537
  417. package/dist/providers/linear/skill.generated.js +0 -8
  418. package/dist/providers/mixpanel/index.d.ts +0 -59
  419. package/dist/providers/mixpanel/index.js +0 -472
  420. package/dist/providers/mixpanel/operations.js +0 -20
  421. package/dist/providers/mixpanel/rest.js +0 -603
  422. package/dist/providers/mixpanel/skill.generated.js +0 -10
  423. package/dist/providers/mixpanel/value-safety.js +0 -16
  424. package/dist/providers/notion/index.d.ts +0 -67
  425. package/dist/providers/notion/index.js +0 -368
  426. package/dist/providers/notion/openapi.generated.js +0 -75
  427. package/dist/providers/notion/rest.d.ts +0 -24
  428. package/dist/providers/notion/rest.js +0 -1226
  429. package/dist/providers/notion/skill.generated.js +0 -11
  430. package/dist/providers/notion/value-safety.js +0 -56
  431. package/dist/providers/revenuecat/index.d.ts +0 -51
  432. package/dist/providers/revenuecat/index.js +0 -697
  433. package/dist/providers/revenuecat/skill.generated.js +0 -11
  434. package/dist/providers/stripe/rest.d.ts +0 -14
  435. package/dist/providers/stripe/rest.js +0 -402
  436. package/dist/providers/vercel/rest.d.ts +0 -20
  437. package/dist/providers/vercel/rest.js +0 -1020
  438. package/dist/providers/wispr-flow/index.d.ts +0 -14
  439. package/dist/providers/wispr-flow/index.js +0 -117
  440. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  441. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -1,7 +1,11 @@
1
- import { authorizationHandoff, bindAuthorizationHandoffs } from "./authorization-handoff.js";
1
+ import { upwardStep } from "./upward.js";
2
+ import { authCall, authInvoke, authFlow, authMethod, authStorage, registerAuth } from "./auth/native.js";
3
+ import { registryProgram } from "./runtime/registry.js";
4
+ import { runEdge } from "./runtime/run.js";
5
+ import { authorizationHandoff, authorizeConnector, bindAuthorizationHandoffs } from "./authorization-handoff.js";
2
6
  import { bindMcpClient } from "./mcp-client-context.js";
3
7
  import { CONFIG_DEFAULTS } from "./config-defaults.js";
4
- import { z } from "zod";
8
+ import * as z from "zod";
5
9
  import { advertisedSchema } from "./advertised-schema.js";
6
10
  import { boundedDiscoveryText, CatalogService, DEFAULT_SEARCH_LIMIT, DiscoveryPolicyError, flatSearchResult, MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES, MAX_SEARCH_LIMIT, } from "./catalog-service.js";
7
11
  import { resolveDiscoveryConcurrency } from "./concurrency.js";
@@ -43,24 +47,26 @@ function discoveryErrorResult(error) {
43
47
  result.isError = true;
44
48
  return result;
45
49
  }
46
- async function discoveryResult(operation, hint) {
47
- try {
48
- const value = await operation();
49
- const text = boundedDiscoveryText(value, hint);
50
- const result = jsonResult(value, text);
51
- const bytes = enc.encode(JSON.stringify(result)).length;
52
- if (bytes > MAX_DISCOVERY_RESULT_BYTES) {
53
- throw new DiscoveryPolicyError("result_too_large", `Discovery result is ${bytes} UTF-8 bytes, over the ${MAX_DISCOVERY_RESULT_BYTES}-byte ceiling. ${hint}`);
50
+ const discoveryResult = function (operation, hint) {
51
+ return authFlow(function* () {
52
+ try {
53
+ const value = yield* upwardStep(authCall(operation, []));
54
+ const text = boundedDiscoveryText(value, hint);
55
+ const result = jsonResult(value, text);
56
+ const bytes = enc.encode(JSON.stringify(result)).length;
57
+ if (bytes > MAX_DISCOVERY_RESULT_BYTES) {
58
+ throw new DiscoveryPolicyError("result_too_large", `Discovery result is ${bytes} UTF-8 bytes, over the ${MAX_DISCOVERY_RESULT_BYTES}-byte ceiling. ${hint}`);
59
+ }
60
+ return result;
54
61
  }
55
- return result;
56
- }
57
- catch (err) {
58
- if (err instanceof DiscoveryPolicyError) {
59
- return discoveryErrorResult(err);
62
+ catch (err) {
63
+ if (err instanceof DiscoveryPolicyError) {
64
+ return discoveryErrorResult(err);
65
+ }
66
+ throw err;
60
67
  }
61
- throw err;
62
- }
63
- }
68
+ });
69
+ };
64
70
  /** True if `b` is a UTF-8 continuation byte (0b10xxxxxx). */
65
71
  function isContinuationByte(b) {
66
72
  return b !== undefined && (b & 0xc0) === 0x80;
@@ -84,7 +90,7 @@ function isValidResultOffset(value) {
84
90
  * (issue #38). An offset at or past `bytes.length` is left alone — there is no
85
91
  * character there to split.
86
92
  */
87
- export function alignStartToCharBoundary(bytes, offset) {
93
+ function alignStartToCharBoundary(bytes, offset) {
88
94
  let o = offset;
89
95
  while (o > 0 && isContinuationByte(bytes[o]))
90
96
  o--;
@@ -92,7 +98,7 @@ export function alignStartToCharBoundary(bytes, offset) {
92
98
  }
93
99
  /** End boundary for UTF-8-safe, forward-progressing result pages. See
94
100
  * documentation/meta-tools.md#result-representation. */
95
- export function alignEndToCharBoundary(bytes, offset, end, total) {
101
+ function alignEndToCharBoundary(bytes, offset, end, total) {
96
102
  if (end >= total)
97
103
  return total;
98
104
  // A window that reaches no further than `offset` yields no bytes and no
@@ -172,6 +178,14 @@ function isWellFormedText(text) {
172
178
  */
173
179
  const READ_REDUCE_FIRST = "To find something specific, repeat this read inside execute_code with connecta.call and filter or search the result there, returning only what matters";
174
180
  function pagingHint(results, totalBytes, preview) {
181
+ if (results.pagingTool === "read_result") {
182
+ const shown = preview.kind === "prefix"
183
+ ? `Bytes 0-${preview.bytes} of ${totalBytes} follow; page the rest`
184
+ : preview.kind === "text-of-envelope"
185
+ ? `Its text follows; page the full ${totalBytes}-byte content array as JSON`
186
+ : `Page all ${totalBytes} bytes`;
187
+ return `${results.write ? WRITE_ALREADY_RAN + " " : ""}${shown} with read_result using nextAction.`;
188
+ }
175
189
  if (!results.write) {
176
190
  if (results.binding.program)
177
191
  return "filter/map/slice data inside execute_code and return only what you need; page this completed return with connecta.result using nextAction.";
@@ -199,136 +213,155 @@ function pagingHint(results, totalBytes, preview) {
199
213
  * documentation/meta-tools.md#result-representation). It is one compact JSON
200
214
  * line with no raw newline, so the preview starts after the first `\n`.
201
215
  */
202
- async function stashResult(bytes, results, preview, rawText) {
203
- const totalBytes = bytes.length;
204
- // Direct pages promise offsets into the original UTF-8 text. JSON-escaping
205
- // raw text would change that contract; never stash the encoder's replacement
206
- // characters as if they were the completed call's output.
207
- if (rawText !== undefined && !isWellFormedText(rawText))
208
- return {
209
- truncated: true,
210
- totalBytes,
211
- hint: `${results.write ? WRITE_ALREADY_RAN + " " : ""}Paging is unavailable: text contains unpaired surrogates and can't be paged as text; request the value as JSON (value mode or a JSON-returning call) to recover it.`,
212
- };
213
- const id = crypto.randomUUID();
214
- try {
215
- // Base64 permits byte-range decoding, and splitting the envelope across
216
- // keys keeps a page's storage read proportional to the page instead of to
217
- // the whole result (issue #540). Chunk 0 carries the header; the connecta.result
218
- // reader below maps a byte offset back to chunk index and base64 quad.
219
- const chunkBytes = resultChunkBytes(totalBytes);
220
- const binding = base64Of(enc.encode(JSON.stringify(results.binding)));
221
- const chunks = [`${RESULT_ENVELOPE_V4}${binding}:${totalBytes}:${chunkBytes}:${results.cap}:`];
222
- for (let offset = 0; offset < bytes.length; offset += chunkBytes) {
223
- const chunk = base64Of(bytes.subarray(offset, offset + chunkBytes));
224
- if (offset === 0)
225
- chunks[0] += chunk;
226
- else
227
- chunks.push(chunk);
228
- }
229
- if (!(await results.set(id, chunks, RESULT_TTL_SECONDS))) {
230
- throw new Error("Result stash capacity exhausted");
231
- }
232
- }
233
- catch {
234
- // Paging is advisory after a completed call, including an approved write.
235
- // Neither backend prose nor a retry hint belongs in this successful result.
216
+ const stashResult = function (bytes, results, preview, rawText) {
217
+ return authFlow(function* () {
218
+ const totalBytes = bytes.length;
219
+ // Direct pages promise offsets into the original UTF-8 text. JSON-escaping
220
+ // raw text would change that contract; never stash the encoder's replacement
221
+ // characters as if they were the completed call's output.
222
+ if (rawText !== undefined && !isWellFormedText(rawText))
223
+ return {
224
+ truncated: true,
225
+ totalBytes,
226
+ hint: `${results.write ? WRITE_ALREADY_RAN + " " : ""}Paging is unavailable: text contains unpaired surrogates and can't be paged as text; request the value as JSON (value mode or a JSON-returning call) to recover it.`,
227
+ };
228
+ const id = crypto.randomUUID();
236
229
  try {
237
- results.warn();
230
+ // Base64 permits byte-range decoding, and splitting the envelope across
231
+ // keys keeps a page's storage read proportional to the page instead of to
232
+ // the whole result (issue #540). Chunk 0 carries the header; the connecta.result
233
+ // reader below maps a byte offset back to chunk index and base64 quad.
234
+ const chunkBytes = resultChunkBytes(totalBytes);
235
+ const binding = base64Of(enc.encode(JSON.stringify(results.binding)));
236
+ const chunks = [`${RESULT_ENVELOPE_V4}${binding}:${totalBytes}:${chunkBytes}:${results.cap}:`];
237
+ for (let offset = 0; offset < bytes.length; offset += chunkBytes) {
238
+ const chunk = base64Of(bytes.subarray(offset, offset + chunkBytes));
239
+ if (offset === 0)
240
+ chunks[0] += chunk;
241
+ else
242
+ chunks.push(chunk);
243
+ }
244
+ if (!(yield* upwardStep(authInvoke(results, "set", [id, chunks, RESULT_TTL_SECONDS])))) {
245
+ throw new Error("Result stash capacity exhausted");
246
+ }
238
247
  }
239
248
  catch {
240
- // Logging cannot change the call either.
249
+ // Paging is advisory after a completed call, including an approved write.
250
+ // Neither backend prose nor a retry hint belongs in this successful result.
251
+ try {
252
+ results.warn();
253
+ }
254
+ catch {
255
+ // Logging cannot change the call either.
256
+ }
257
+ return {
258
+ truncated: true,
259
+ totalBytes,
260
+ hint: results.write
261
+ ? `Paging is unavailable. ${WRITE_ALREADY_RAN}`
262
+ : results.pagingTool
263
+ ? "Paging is unavailable."
264
+ : "Paging is unavailable. Use execute_code to reduce read-only results before returning them.",
265
+ };
241
266
  }
242
267
  return {
243
268
  truncated: true,
269
+ resultId: id,
244
270
  totalBytes,
245
- hint: results.write
246
- ? `Paging is unavailable. ${WRITE_ALREADY_RAN}`
247
- : "Paging is unavailable. Use execute_code to reduce read-only results before returning them.",
271
+ hint: pagingHint(results, totalBytes, preview),
272
+ nextOffset: preview.kind === "prefix" ? preview.bytes : 0,
273
+ nextAction: results.pagingTool === "read_result"
274
+ ? {
275
+ tool: "read_result",
276
+ arguments: { id, offset: preview.kind === "prefix" ? preview.bytes : 0 },
277
+ }
278
+ : {
279
+ tool: "execute_code",
280
+ // A byte may cost six JSON characters when escaped. Leave room for the
281
+ // result id, page counters, and continuation metadata in the returned page.
282
+ arguments: {
283
+ code: `async () => await connecta.result(${JSON.stringify(id)}, { offset: ${preview.kind === "prefix" ? preview.bytes : 0}, maxBytes: ${Math.floor((MAX_EXECUTE_RESULT_CHARS - 1024) / 6)} })`,
284
+ },
285
+ },
248
286
  };
249
- }
250
- return {
251
- truncated: true,
252
- resultId: id,
253
- totalBytes,
254
- hint: pagingHint(results, totalBytes, preview),
255
- nextOffset: preview.kind === "prefix" ? preview.bytes : 0,
256
- nextAction: {
257
- tool: "execute_code",
258
- // A byte may cost six JSON characters when escaped. Leave room for the
259
- // result id, page counters, and continuation metadata in the returned page.
260
- arguments: {
261
- code: `async () => await connecta.result(${JSON.stringify(id)}, { offset: ${preview.kind === "prefix" ? preview.bytes : 0}, maxBytes: ${Math.floor((MAX_EXECUTE_RESULT_CHARS - 1024) / 6)} })`,
262
- },
263
- },
264
- };
265
- }
287
+ });
288
+ };
266
289
  /** Program calls use the direct-call envelope, ledger, expiry and authority. */
267
- export async function pageProgramResult(registry, baseUrl, resolved, value, format, secrets, requestScope, wireBytes = PROGRAM_RESULT_WIRE_BYTES) {
268
- const data = secrets.redact(value);
269
- const wire = JSON.stringify({ data, format });
270
- if (enc.encode(wire).length <= wireBytes)
271
- return { data, format };
272
- const text = secrets.text(format === "text" ? String(data) : serializeResultText(data));
273
- const bytes = enc.encode(text);
274
- const write = resolved.definition.classification !== "read";
275
- const notice = await stashResult(bytes, {
276
- binding: {
277
- identity: registry.resultIdentity(),
278
- baseUrl,
279
- connector: resolved.connector.id,
280
- tool: resolved.definition.name,
281
- classification: write ? "write" : "read",
282
- },
283
- write,
284
- cap: resolveMaxResultBytes(resolved.connector.maxResultBytes, registry.maxResultBytes),
285
- set: (id, chunks, ttl) => registry.stashResult(id, chunks, ttl),
286
- warn: () => logFailure(registry.contextFor(resolved.connector.id, baseUrl, requestScope).logger, "result paging unavailable", failureRecord({ connector: resolved.connector.id, tool: resolved.definition })),
287
- }, { kind: "none" }, format === "text" ? text : undefined);
288
- return {
289
- format: "paged",
290
- valueFormat: format,
291
- ...(notice.resultId ? { resultId: notice.resultId } : {}),
292
- totalBytes: bytes.length,
293
- truncated: true,
294
- hint: notice.resultId
295
- ? `${write ? WRITE_ALREADY_RAN + " " : ""}Page this result with connecta.result(handle); do not repeat the call for output.`
296
- : notice.hint,
297
- };
290
+ export function pageProgramResult(registry, baseUrl, resolved, value, format, secrets, requestScope, wireBytes = PROGRAM_RESULT_WIRE_BYTES) {
291
+ return runEdge(pageProgramResultNative(registry, baseUrl, resolved, value, format, secrets, requestScope, wireBytes));
298
292
  }
293
+ const pageProgramResultNative = /* @__PURE__ */ registerAuth(pageProgramResult, function (registry, baseUrl, resolved, value, format, secrets, requestScope, wireBytes = PROGRAM_RESULT_WIRE_BYTES) {
294
+ return authFlow(function* () {
295
+ const data = secrets.redact(value);
296
+ const wire = JSON.stringify({ data, format });
297
+ if (enc.encode(wire).length <= wireBytes)
298
+ return { data, format };
299
+ const text = secrets.text(format === "text" ? String(data) : serializeResultText(data));
300
+ const bytes = enc.encode(text);
301
+ const write = resolved.definition.classification !== "read";
302
+ const notice = yield* upwardStep(stashResult(bytes, {
303
+ binding: {
304
+ identity: registry.resultIdentity(),
305
+ baseUrl,
306
+ connector: resolved.connector.id,
307
+ tool: resolved.definition.name,
308
+ classification: write ? "write" : "read",
309
+ },
310
+ write,
311
+ cap: resolveMaxResultBytes(resolved.connector.maxResultBytes, registry.maxResultBytes),
312
+ set: authMethod((id, chunks, ttl) => registryProgram(registry, "stashResult", id, chunks, ttl)),
313
+ warn: () => logFailure(registry.contextFor(resolved.connector.id, baseUrl, requestScope).logger, "result paging unavailable", failureRecord({ connector: resolved.connector.id, tool: resolved.definition })),
314
+ }, { kind: "none" }, format === "text" ? text : undefined));
315
+ return {
316
+ format: "paged",
317
+ valueFormat: format,
318
+ ...(notice.resultId ? { resultId: notice.resultId } : {}),
319
+ totalBytes: bytes.length,
320
+ truncated: true,
321
+ hint: notice.resultId
322
+ ? `${write ? WRITE_ALREADY_RAN + " " : ""}Page this result with connecta.result(handle); do not repeat the call for output.`
323
+ : notice.hint,
324
+ };
325
+ });
326
+ });
299
327
  /** Program returns share the direct stash, with all host-admitted call dependencies. */
300
- export async function guardProgramReturn(value, registry, baseUrl, dependencies, secrets, logger, maxChars, completedWrite) {
301
- const serialized = serializeResultText(value);
302
- if (serialized.length <= maxChars)
303
- return value;
304
- const text = secrets.text(serialized);
305
- const bytes = enc.encode(text);
306
- const write = dependencies.some((dependency) => dependency.classification === "write");
307
- const cap = Math.min(registry.maxResultBytes, ...dependencies.map((dependency) => resolveMaxResultBytes(registry.getConnector(dependency.connector)?.maxResultBytes, registry.maxResultBytes)));
308
- const results = {
309
- secrets,
310
- binding: {
311
- identity: registry.resultIdentity(),
312
- baseUrl,
313
- classification: write ? "write" : "read",
314
- program: dependencies,
315
- },
316
- write: completedWrite,
317
- cap,
318
- set: (id, chunks, ttl) => registry.stashResult(id, chunks, ttl),
319
- warn: () => logFailure(logger, "result paging unavailable", failureRecord({})),
320
- };
321
- const notice = await stashResult(bytes, results, { kind: "none" }, text);
322
- // Put the entire recovery route ahead of the preview in both response forms.
323
- // A client retaining only the first 2,000 characters still sees the action.
324
- if (notice.resultId) {
325
- const base = { ...notice, totalChars: text.length, preview: "" };
326
- const budget = Math.max(0, maxChars - JSON.stringify(base).length);
327
- return { ...base, preview: escapedHeadOf(bytes, Math.min(512, budget)) };
328
- }
329
- const fallback = guardExecuteResultValue(secrets.redact(value), maxChars, notice.hint);
330
- return { truncated: true, totalChars: fallback.totalChars, hint: fallback.hint, preview: fallback.preview };
328
+ export function guardProgramReturn(value, registry, baseUrl, dependencies, secrets, logger, maxChars, completedWrite) {
329
+ return runEdge(guardProgramReturnNative(value, registry, baseUrl, dependencies, secrets, logger, maxChars, completedWrite));
331
330
  }
331
+ const guardProgramReturnNative = /* @__PURE__ */ registerAuth(guardProgramReturn, function (value, registry, baseUrl, dependencies, secrets, logger, maxChars, completedWrite) {
332
+ return authFlow(function* () {
333
+ const serialized = serializeResultText(value);
334
+ if (serialized.length <= maxChars)
335
+ return value;
336
+ const text = secrets.text(serialized);
337
+ const bytes = enc.encode(text);
338
+ const write = dependencies.some((dependency) => dependency.classification === "write");
339
+ const cap = Math.min(registry.maxResultBytes, ...dependencies.map((dependency) => resolveMaxResultBytes(registry.getConnector(dependency.connector)?.maxResultBytes, registry.maxResultBytes)));
340
+ const results = {
341
+ secrets,
342
+ binding: {
343
+ identity: registry.resultIdentity(),
344
+ baseUrl,
345
+ classification: write ? "write" : "read",
346
+ program: dependencies,
347
+ },
348
+ write: completedWrite,
349
+ cap,
350
+ set: authMethod((id, chunks, ttl) => registryProgram(registry, "stashResult", id, chunks, ttl)),
351
+ warn: () => logFailure(logger, "result paging unavailable", failureRecord({})),
352
+ };
353
+ const notice = yield* upwardStep(stashResult(bytes, results, { kind: "none" }, text));
354
+ // Put the entire recovery route ahead of the preview in both response forms.
355
+ // A client retaining only the first 2,000 characters still sees the action.
356
+ if (notice.resultId) {
357
+ const base = { ...notice, totalChars: text.length, preview: "" };
358
+ const budget = Math.max(0, maxChars - JSON.stringify(base).length);
359
+ return { ...base, preview: escapedHeadOf(bytes, Math.min(512, budget)) };
360
+ }
361
+ const fallback = guardExecuteResultValue(secrets.redact(value), maxChars, notice.hint);
362
+ return { truncated: true, totalChars: fallback.totalChars, hint: fallback.hint, preview: fallback.preview };
363
+ });
364
+ });
332
365
  /** The first `cap` bytes of `bytes`, ending on a character boundary. */
333
366
  function headOf(bytes, cap) {
334
367
  return bytes.subarray(0, alignEndToCharBoundary(bytes, 0, cap, bytes.length));
@@ -347,53 +380,59 @@ function noticeFirst(notice, preview) {
347
380
  * `text` already encoded, so a caller that had to measure it to make this
348
381
  * decision doesn't encode it twice.
349
382
  */
350
- async function guardEncoded(text, bytes, results, cap) {
351
- if (bytes.length <= cap) {
352
- return {
353
- result: { content: [{ type: "text", text }] },
354
- truncated: false,
355
- };
356
- }
357
- if (!isWellFormedText(text)) {
358
- const notice = await stashResult(bytes, results, { kind: "none" }, text);
359
- return { result: noticeFirst(notice, ""), truncated: true };
360
- }
361
- const head = headOf(bytes, cap);
362
- const notice = await stashResult(bytes, results, {
363
- kind: "prefix",
364
- bytes: head.length,
383
+ const guardEncoded = function (text, bytes, results, cap) {
384
+ return authFlow(function* () {
385
+ if (bytes.length <= cap) {
386
+ return {
387
+ result: { content: [{ type: "text", text }] },
388
+ truncated: false,
389
+ };
390
+ }
391
+ if (!isWellFormedText(text)) {
392
+ const notice = yield* upwardStep(stashResult(bytes, results, { kind: "none" }, text));
393
+ return { result: noticeFirst(notice, ""), truncated: true };
394
+ }
395
+ const head = headOf(bytes, cap);
396
+ const notice = yield* upwardStep(stashResult(bytes, results, {
397
+ kind: "prefix",
398
+ bytes: head.length,
399
+ }));
400
+ return { result: noticeFirst(notice, dec.decode(head)), truncated: true };
365
401
  });
366
- return { result: noticeFirst(notice, dec.decode(head)), truncated: true };
367
- }
402
+ };
368
403
  /** {@link guardEncoded} over a string that has not been measured yet. */
369
- async function guardText(text, results, cap) {
370
- // `JSON.stringify`'s type says `string` where its behavior says `string |
371
- // undefined`, so TypeScript alone does not keep a non-string out of here.
372
- // Normalizing at the door means the size check below always measures exactly
373
- // the text that is emitted, and no future caller can launder a non-string
374
- // through it the way issue #42 describes.
375
- const serialized = typeof text === "string" ? text : serializeResultText(text);
376
- const body = results.secrets?.text(serialized) ?? serialized;
377
- return guardEncoded(body, enc.encode(body), results, cap);
378
- }
404
+ const guardText = function (text, results, cap) {
405
+ return authFlow(function* () {
406
+ // `JSON.stringify`'s type says `string` where its behavior says `string |
407
+ // undefined`, so TypeScript alone does not keep a non-string out of here.
408
+ // Normalizing at the door means the size check below always measures exactly
409
+ // the text that is emitted, and no future caller can launder a non-string
410
+ // through it the way issue #42 describes.
411
+ const serialized = typeof text === "string" ? text : serializeResultText(text);
412
+ const body = results.secrets?.text(serialized) ?? serialized;
413
+ return yield* upwardStep(guardEncoded(body, enc.encode(body), results, cap));
414
+ });
415
+ };
379
416
  /** Store an oversized JSON value and replace it with a page handle. */
380
- async function guardValue(value, results, cap) {
381
- const serialized = serializeResultText(value);
382
- const text = results.secrets?.text(serialized) ?? serialized;
383
- const bytes = enc.encode(text);
384
- if (bytes.length <= cap)
385
- return { result: results.secrets?.redact(value) ?? value, truncated: false };
386
- const notice = await stashResult(bytes, results, { kind: "none" });
387
- return {
388
- result: notice.resultId
389
- ? notice
390
- : {
391
- ...notice,
392
- preview: escapedHeadOf(bytes, cap),
393
- },
394
- truncated: true,
395
- };
396
- }
417
+ const guardValue = function (value, results, cap) {
418
+ return authFlow(function* () {
419
+ const serialized = serializeResultText(value);
420
+ const text = results.secrets?.text(serialized) ?? serialized;
421
+ const bytes = enc.encode(text);
422
+ if (bytes.length <= cap)
423
+ return { result: results.secrets?.redact(value) ?? value, truncated: false };
424
+ const notice = yield* upwardStep(stashResult(bytes, results, { kind: "none" }));
425
+ return {
426
+ result: notice.resultId
427
+ ? notice
428
+ : {
429
+ ...notice,
430
+ preview: escapedHeadOf(bytes, cap),
431
+ },
432
+ truncated: true,
433
+ };
434
+ });
435
+ };
397
436
  /**
398
437
  * The longest head of `bytes` whose JSON string form fits `cap` bytes. Value
399
438
  * mode carries an unpageable preview inside its JSON envelope, where escaping
@@ -432,48 +471,50 @@ function escapedHeadOf(bytes, cap) {
432
471
  * the head of a half-written base64 image is of no use to anyone. Either way
433
472
  * the full envelope is stashed and pages through `connecta.result`.
434
473
  */
435
- async function guardContent(content, results, cap) {
436
- content = results.secrets?.redact({ content }).content ?? content;
437
- const only = content.length === 1 ? content[0] : undefined;
438
- if (only?.type === "text" && typeof only.text === "string") {
439
- const bytes = enc.encode(only.text);
440
- // Under the cap the block passes through untouched, annotations included.
441
- if (bytes.length <= cap)
474
+ const guardContent = function (content, results, cap) {
475
+ return authFlow(function* () {
476
+ content = results.secrets?.redact({ content }).content ?? content;
477
+ const only = content.length === 1 ? content[0] : undefined;
478
+ if (only?.type === "text" && typeof only.text === "string") {
479
+ const bytes = enc.encode(only.text);
480
+ // Under the cap the block passes through untouched, annotations included.
481
+ if (bytes.length <= cap)
482
+ return { result: { content }, truncated: false };
483
+ return yield* upwardStep(guardEncoded(only.text, bytes, results, cap));
484
+ }
485
+ let text;
486
+ try {
487
+ const serialized = JSON.stringify(content);
488
+ text = results.secrets?.text(serialized) ?? serialized;
489
+ }
490
+ catch {
491
+ // A block carrying a BigInt or a cycle cannot be serialized, so it cannot
492
+ // be measured, stashed, or paged either — there is nothing this guard could
493
+ // do with it. Pass it through as the old text-only measure did, rather than
494
+ // turning a call that used to succeed into result_processing_failed.
442
495
  return { result: { content }, truncated: false };
443
- return guardEncoded(only.text, bytes, results, cap);
444
- }
445
- let text;
446
- try {
447
- const serialized = JSON.stringify(content);
448
- text = results.secrets?.text(serialized) ?? serialized;
449
- }
450
- catch {
451
- // A block carrying a BigInt or a cycle cannot be serialized, so it cannot
452
- // be measured, stashed, or paged either — there is nothing this guard could
453
- // do with it. Pass it through as the old text-only measure did, rather than
454
- // turning a call that used to succeed into result_processing_failed.
455
- return { result: { content }, truncated: false };
456
- }
457
- const bytes = enc.encode(text);
458
- // Under the cap the downstream blocks pass through untouched, non-text ones
459
- // included, in their original order.
460
- if (bytes.length <= cap) {
461
- return { result: { content }, truncated: false };
462
- }
463
- if (content.every((b) => b.type === "text")) {
464
- const joined = enc.encode(content.map((b) => b.text).join("\n"));
465
- const notice = await stashResult(bytes, results, { kind: "text-of-envelope" });
496
+ }
497
+ const bytes = enc.encode(text);
498
+ // Under the cap the downstream blocks pass through untouched, non-text ones
499
+ // included, in their original order.
500
+ if (bytes.length <= cap) {
501
+ return { result: { content }, truncated: false };
502
+ }
503
+ if (content.every((b) => b.type === "text")) {
504
+ const joined = enc.encode(content.map((b) => b.text).join("\n"));
505
+ const notice = yield* upwardStep(stashResult(bytes, results, { kind: "text-of-envelope" }));
506
+ return {
507
+ result: noticeFirst(notice, dec.decode(headOf(joined, cap))),
508
+ truncated: true,
509
+ };
510
+ }
511
+ const notice = yield* upwardStep(stashResult(bytes, results, { kind: "none" }));
466
512
  return {
467
- result: noticeFirst(notice, dec.decode(headOf(joined, cap))),
513
+ result: { content: [{ type: "text", text: JSON.stringify(notice) }] },
468
514
  truncated: true,
469
515
  };
470
- }
471
- const notice = await stashResult(bytes, results, { kind: "none" });
472
- return {
473
- result: { content: [{ type: "text", text: JSON.stringify(notice) }] },
474
- truncated: true,
475
- };
476
- }
516
+ });
517
+ };
477
518
  /**
478
519
  * Every meta-tool handler over a registry, one per registered tool. Exported for
479
520
  * direct testing; registerMetaTools() wires the six explicit tools onto an
@@ -503,7 +544,13 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
503
544
  // request without leaking request-bound I/O into the next one.
504
545
  const requestScope = opts.requestScope ?? {};
505
546
  if (opts.canManageAuth || opts.oauthConnectUrl || opts.credentialHandoffUrl || opts.oauthConnectUnavailable) {
506
- bindAuthorizationHandoffs(requestScope, (connector) => authorizationHandoff(registry, baseUrl, requestScope, opts, { connector }));
547
+ bindAuthorizationHandoffs(requestScope, authMethod((connector) => authCall(authorizationHandoff, [
548
+ registry,
549
+ baseUrl,
550
+ requestScope,
551
+ opts,
552
+ { connector },
553
+ ])));
507
554
  }
508
555
  const sentSecrets = sentSecretsForRequest(requestScope);
509
556
  const skills = new SkillsRegistry(registry, baseUrl, {
@@ -525,144 +572,162 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
525
572
  });
526
573
  const invocation = new InvocationService(registry, catalog, opts.activity);
527
574
  /** MCP adapter: shared invocation semantics plus MCP-only result shaping. */
528
- async function runCall(call, source) {
529
- const timeoutMs = normalizeTimeoutMs(call.timeoutMs) ?? defaultToolTimeoutMs;
530
- const outcome = await invocation.invoke(call.address, call.args ?? {}, {
531
- source,
532
- sentSecrets,
533
- readOnly: opts.readOnly,
534
- ...(timeoutMs !== undefined ? { timeoutMs } : {}),
535
- ...(opts.requestSignal !== undefined ? { requestSignal: opts.requestSignal } : {}),
536
- unwrapResult: call.resultMode === "value",
537
- ...(opts.downstreamInput
538
- ? {
539
- processInputRequired: async (result, resolved, secrets) => {
540
- await captureDownstreamInput(requestScope, resolved.connector.id, `${resolved.connector.id}.${resolved.toolName}`, result, secrets);
541
- return { toolResult: { content: [] } };
542
- },
543
- }
544
- : {}),
545
- processResult: async (result, resolved, secrets, format) => {
546
- // Result-size cap for THIS call: the connector's own override wins,
547
- // then the deployment-wide value, then the built-in default (already
548
- // folded into `globalCap`). Resolved per call so one request can
549
- // mix a tight-capped connector with siblings on the global cap. An
550
- // override the registry already warned about at startup is dropped
551
- // here, so the connector simply inherits `globalCap`.
552
- const cap = resolveMaxResultBytes(resolved.connector.maxResultBytes, globalCap);
553
- const results = {
554
- secrets,
555
- binding: {
556
- identity: registry.resultIdentity(),
557
- baseUrl,
558
- connector: resolved.connector.id,
559
- tool: resolved.definition.name,
560
- classification: resolved.definition.classification === "read" ? "read" : "write",
561
- },
562
- write: resolved.definition.classification !== "read",
563
- cap,
564
- set: (id, value, ttlSeconds) => registry.stashResult(id, value, ttlSeconds),
565
- // The catalog entry, never `toolName`: a record names a tool
566
- // only through the grammar check (src/operator-record.ts).
567
- warn: () => logFailure(registry.contextFor(resolved.connector.id, baseUrl, requestScope).logger, "result paging unavailable", failureRecord({ connector: resolved.connector.id, tool: resolved.definition })),
568
- };
569
- const processed = (toolResult, truncated, value) => ({
570
- toolResult,
571
- ...value,
572
- ...(truncated ? { friction: "result_too_large" } : {}),
573
- });
574
- if (call.resultMode === "value") {
575
- let value = result;
576
- const guarded = await guardValue(value, results, cap);
577
- value = guarded.result;
578
- return processed(jsonResult({ ok: true, data: value, format }), guarded.truncated, { value });
579
- }
580
- if (resolved.connector.kind === "mcp") {
581
- const mcpResult = result;
582
- let content = mcpResult?.content ?? [];
583
- if (!content.some((block) => block.type === "text") && mcpResult?.structuredContent !== undefined) {
584
- content = [
585
- ...content,
586
- {
587
- type: "text",
588
- text: JSON.stringify(mcpResult.structuredContent),
575
+ function runCall(call, source) {
576
+ return runEdge(runCallNative(call, source));
577
+ }
578
+ const runCallNative = /* @__PURE__ */ registerAuth(runCall, function (call, source) {
579
+ return authFlow(function* () {
580
+ const timeoutMs = normalizeTimeoutMs(call.timeoutMs) ?? defaultToolTimeoutMs;
581
+ const outcome = yield* upwardStep(authInvoke(invocation, "invoke", [
582
+ call.address,
583
+ call.args ?? {},
584
+ {
585
+ source,
586
+ activitySource: opts.activitySource,
587
+ sentSecrets,
588
+ readOnly: opts.readOnly,
589
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
590
+ ...(opts.requestSignal !== undefined ? { requestSignal: opts.requestSignal } : {}),
591
+ unwrapResult: call.resultMode === "value",
592
+ ...(opts.downstreamInput
593
+ ? {
594
+ processInputRequired: authMethod((result, resolved, secrets) => authFlow(function* () {
595
+ yield* upwardStep(authCall(captureDownstreamInput, [
596
+ requestScope,
597
+ resolved.connector.id,
598
+ `${resolved.connector.id}.${resolved.toolName}`,
599
+ result,
600
+ secrets,
601
+ resolved.definition.classification !== "read",
602
+ ]));
603
+ return { toolResult: { content: [] } };
604
+ })),
605
+ }
606
+ : {}),
607
+ processResult: authMethod((result, resolved, secrets, format) => authFlow(function* () {
608
+ // Result-size cap for THIS call: the connector's own override wins,
609
+ // then the deployment-wide value, then the built-in default (already
610
+ // folded into `globalCap`). Resolved per call so one request can
611
+ // mix a tight-capped connector with siblings on the global cap. An
612
+ // override the registry already warned about at startup is dropped
613
+ // here, so the connector simply inherits `globalCap`.
614
+ const cap = resolveMaxResultBytes(resolved.connector.maxResultBytes, globalCap);
615
+ const results = {
616
+ secrets,
617
+ binding: {
618
+ identity: registry.resultIdentity(),
619
+ baseUrl,
620
+ connector: resolved.connector.id,
621
+ tool: resolved.definition.name,
622
+ classification: resolved.definition.classification === "read" ? "read" : "write",
589
623
  },
590
- ];
624
+ write: resolved.definition.classification !== "read",
625
+ cap,
626
+ ...(opts.pagingTool ? { pagingTool: opts.pagingTool } : {}),
627
+ set: authMethod((id, value, ttlSeconds) => registryProgram(registry, "stashResult", id, value, ttlSeconds)),
628
+ // The catalog entry, never `toolName`: a record names a tool
629
+ // only through the grammar check (src/operator-record.ts).
630
+ warn: () => logFailure(registry.contextFor(resolved.connector.id, baseUrl, requestScope).logger, "result paging unavailable", failureRecord({ connector: resolved.connector.id, tool: resolved.definition })),
631
+ };
632
+ const processed = (toolResult, truncated, value) => ({
633
+ toolResult,
634
+ ...value,
635
+ ...(truncated ? { friction: "result_too_large" } : {}),
636
+ });
637
+ if (call.resultMode === "value") {
638
+ let value = result;
639
+ const guarded = yield* upwardStep(guardValue(value, results, cap));
640
+ value = guarded.result;
641
+ return processed(jsonResult({ ok: true, data: value, format }), guarded.truncated, { value });
642
+ }
643
+ if (resolved.connector.kind === "mcp") {
644
+ const mcpResult = result;
645
+ let content = mcpResult?.content ?? [];
646
+ if (!content.some((block) => block.type === "text") && mcpResult?.structuredContent !== undefined) {
647
+ content = [
648
+ ...content,
649
+ {
650
+ type: "text",
651
+ text: JSON.stringify(mcpResult.structuredContent),
652
+ },
653
+ ];
654
+ }
655
+ const guarded = yield* upwardStep(guardContent(content, results, cap));
656
+ // Native blocks and bounded previews are the result. A format-only
657
+ // structuredContent hides them in clients that prefer that field.
658
+ guarded.result._meta = { "dev.connecta/format": format };
659
+ return processed(guarded.result, guarded.truncated);
660
+ }
661
+ const value = result;
662
+ const guarded = yield* upwardStep(guardText(serializeResultText(value), results, cap));
663
+ guarded.result._meta = { "dev.connecta/format": format };
664
+ return processed(guarded.result, guarded.truncated, { value });
665
+ })),
666
+ activityFriction: (processed) => processed.friction,
667
+ },
668
+ ]));
669
+ if (!outcome.ok) {
670
+ const structuredRecovery = outcome.error.nextAction !== undefined;
671
+ const recoveryRequired = structuredRecovery ||
672
+ // Sanitized `unavailable` diagnostics ride the structured shape too;
673
+ // the plain-text path would drop them (#539).
674
+ outcome.error.details !== undefined ||
675
+ // So does a conflict's `current`: where things stand is the retry.
676
+ outcome.error.current !== undefined ||
677
+ ["auth_required", "invalid_args", "input_required_unsupported"].includes(outcome.error.code);
678
+ // Every mode carries the complete repair envelope in both MCP forms.
679
+ const makeFailedResult = () => jsonResult({
680
+ ok: false,
681
+ error: outcome.error,
682
+ durationMs: outcome.durationMs,
683
+ attempts: outcome.attempts,
684
+ ...(call.diagnostics ? { timing: outcome.timing } : {}),
685
+ });
686
+ let failedResult = makeFailedResult();
687
+ // Errors repeat in text and structuredContent. Account for
688
+ // both copies and JSON escaping when the bounded provider reason is large.
689
+ if (!recoveryRequired) {
690
+ const cap = resolveMaxResultBytes(outcome.resolved?.connector.maxResultBytes, globalCap);
691
+ let budget = 512;
692
+ while (enc.encode(JSON.stringify(failedResult)).length > cap && budget > 0) {
693
+ budget = Math.floor(budget / 2);
694
+ outcome.error.message = boundedEchoText(outcome.error.message, budget);
695
+ failedResult = makeFailedResult();
591
696
  }
592
- const guarded = await guardContent(content, results, cap);
593
- // Native blocks and bounded previews are the result. A format-only
594
- // structuredContent hides them in clients that prefer that field.
595
- guarded.result._meta = { "dev.connecta/format": format };
596
- return processed(guarded.result, guarded.truncated);
597
- }
598
- const value = result;
599
- const guarded = await guardText(serializeResultText(value), results, cap);
600
- guarded.result._meta = { "dev.connecta/format": format };
601
- return processed(guarded.result, guarded.truncated, { value });
602
- },
603
- activityFriction: (processed) => processed.friction,
604
- });
605
- if (!outcome.ok) {
606
- const structuredRecovery = outcome.error.nextAction !== undefined;
607
- const recoveryRequired = structuredRecovery ||
608
- // Sanitized `unavailable` diagnostics ride the structured shape too;
609
- // the plain-text path would drop them (#539).
610
- outcome.error.details !== undefined ||
611
- // So does a conflict's `current`: where things stand is the retry.
612
- outcome.error.current !== undefined ||
613
- ["auth_required", "invalid_args", "input_required_unsupported"].includes(outcome.error.code);
614
- // Every mode carries the complete repair envelope in both MCP forms.
615
- const makeFailedResult = () => jsonResult({
616
- ok: false,
617
- error: outcome.error,
618
- durationMs: outcome.durationMs,
619
- attempts: outcome.attempts,
620
- ...(call.diagnostics ? { timing: outcome.timing } : {}),
621
- });
622
- let failedResult = makeFailedResult();
623
- // Errors repeat in text and structuredContent. Account for
624
- // both copies and JSON escaping when the bounded provider reason is large.
625
- if (!recoveryRequired) {
626
- const cap = resolveMaxResultBytes(outcome.resolved?.connector.maxResultBytes, globalCap);
627
- let budget = 512;
628
- while (enc.encode(JSON.stringify(failedResult)).length > cap && budget > 0) {
629
- budget = Math.floor(budget / 2);
630
- outcome.error.message = boundedEchoText(outcome.error.message, budget);
631
- failedResult = makeFailedResult();
632
697
  }
698
+ failedResult.isError = true;
699
+ return {
700
+ toolResult: failedResult,
701
+ durationMs: outcome.durationMs,
702
+ attempts: outcome.attempts,
703
+ timing: outcome.timing,
704
+ error: outcome.error,
705
+ };
633
706
  }
634
- failedResult.isError = true;
707
+ const valueModeResult = call.resultMode === "value"
708
+ ? jsonResult({
709
+ ok: true,
710
+ data: outcome.value.value,
711
+ format: outcome.format,
712
+ durationMs: outcome.durationMs,
713
+ attempts: outcome.attempts,
714
+ ...(call.diagnostics ? { timing: outcome.timing } : {}),
715
+ })
716
+ : outcome.value.toolResult;
635
717
  return {
636
- toolResult: failedResult,
718
+ toolResult: valueModeResult,
637
719
  durationMs: outcome.durationMs,
638
720
  attempts: outcome.attempts,
639
721
  timing: outcome.timing,
640
- error: outcome.error,
722
+ ...(Object.prototype.hasOwnProperty.call(outcome.value, "value") ? { value: outcome.value.value } : {}),
641
723
  };
642
- }
643
- const valueModeResult = call.resultMode === "value"
644
- ? jsonResult({
645
- ok: true,
646
- data: outcome.value.value,
647
- format: outcome.format,
648
- durationMs: outcome.durationMs,
649
- attempts: outcome.attempts,
650
- ...(call.diagnostics ? { timing: outcome.timing } : {}),
651
- })
652
- : outcome.value.toolResult;
653
- return {
654
- toolResult: valueModeResult,
655
- durationMs: outcome.durationMs,
656
- attempts: outcome.attempts,
657
- timing: outcome.timing,
658
- ...(Object.prototype.hasOwnProperty.call(outcome.value, "value") ? { value: outcome.value.value } : {}),
659
- };
660
- }
724
+ });
725
+ });
661
726
  return {
662
- async skills(args = {}) {
727
+ skills: authMethod((args = {}) => authFlow(function* () {
663
728
  try {
664
729
  if (!args.name) {
665
- const listing = await skills.summaries();
730
+ const listing = yield* upwardStep(authInvoke(skills, "summaries", []));
666
731
  return {
667
732
  structuredContent: { skills: listing },
668
733
  content: [
@@ -674,31 +739,37 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
674
739
  ],
675
740
  };
676
741
  }
677
- const text = await skills.text(args.name);
742
+ const text = yield* upwardStep(authInvoke(skills, "text", [args.name]));
678
743
  return { content: [{ type: "text", text }], structuredContent: { name: args.name, format: "text", text } };
679
744
  }
680
745
  catch (error) {
681
746
  return errorResult(error instanceof ConnectorCallError ? error.message : "Skills are unavailable.");
682
747
  }
683
- },
684
- async searchTools(args) {
748
+ })),
749
+ searchTools: authMethod((args) => authFlow(function* () {
685
750
  if (args.connector !== undefined && enc.encode(args.connector).length > 512) {
686
751
  return discoveryErrorResult(new DiscoveryPolicyError("invalid_args", "connector must be at most 512 UTF-8 bytes."));
687
752
  }
688
- return discoveryResult(async () => flatSearchResult(await catalog.search({
689
- ...args,
690
- includeSchemaKeys: args.includeSchemas !== undefined,
691
- })), "Request a smaller limit, omit fullDescriptions, or use compact schemas.");
692
- },
693
- async callTool(args) {
694
- return (await runCall(args, "call_tool")).toolResult;
695
- },
696
- async callDestructiveTool(args) {
753
+ return yield* upwardStep(discoveryResult(authMethod(() => authFlow(function* () {
754
+ return yield* upwardStep(authCall(flatSearchResult, [
755
+ yield* upwardStep(authInvoke(catalog, "search", [
756
+ {
757
+ ...args,
758
+ includeSchemaKeys: args.includeSchemas !== undefined,
759
+ },
760
+ ])),
761
+ ]));
762
+ })), "Request a smaller limit, omit fullDescriptions, or use compact schemas."));
763
+ })),
764
+ callTool: authMethod((args) => authFlow(function* () {
765
+ return (yield* upwardStep(authCall(runCall, [args, "call_tool"]))).toolResult;
766
+ })),
767
+ callDestructiveTool: authMethod((args) => authFlow(function* () {
697
768
  // `reason` is read by the host's approval view and stops there — runCall
698
769
  // forwards only the call arguments, so it never reaches the connector.
699
- return (await runCall(args, "call_destructive_tool")).toolResult;
700
- },
701
- async readResult(args, options = {}) {
770
+ return (yield* upwardStep(authCall(runCall, [args, "call_destructive_tool"]))).toolResult;
771
+ })),
772
+ readResult: authMethod((args, options = {}) => authFlow(function* () {
702
773
  // Defense for in-process callers too: guest paging validates these domains
703
774
  // before calling here, while internal consumers may call readResult directly.
704
775
  if (args.maxBytes !== undefined && !isValidMaxResultBytes(args.maxBytes)) {
@@ -709,7 +780,8 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
709
780
  return errorResult(`Invalid offset ${args.offset}: must be a whole number of bytes ` +
710
781
  `>= ${MIN_RESULT_OFFSET}. Omit it to start at the beginning.`);
711
782
  }
712
- if (args.page !== undefined && (!Number.isSafeInteger(args.page) || args.page < 0 || args.offset !== undefined)) {
783
+ if (args.page !== undefined &&
784
+ (!Number.isSafeInteger(args.page) || args.page < 0 || args.offset !== undefined)) {
713
785
  return errorResult("Use a whole page >= 0, without offset.");
714
786
  }
715
787
  const results = registry.resultsStorage();
@@ -726,11 +798,12 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
726
798
  // `false` is a storage failure — retryable, and distinct from an id that
727
799
  // is simply gone. Every key a page touches answers the same way, so a
728
800
  // backend that dies halfway through a multi-chunk page says so.
729
- const read = async (key) => {
801
+ const read = authMethod((key) => authFlow(function* () {
802
+ options.onStage?.("result storage");
730
803
  options.signal?.throwIfAborted();
731
804
  let value;
732
805
  try {
733
- value = (await results.get(key)) ?? null;
806
+ value = (yield* upwardStep(authStorage(results, "get", [key]))) ?? null;
734
807
  }
735
808
  catch {
736
809
  value = false;
@@ -739,8 +812,8 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
739
812
  // subsequent chunk read or decode may start when that happens (INV-7).
740
813
  options.signal?.throwIfAborted();
741
814
  return value;
742
- };
743
- const stored = await read(resultKeys.chunk(args.id, 0));
815
+ }));
816
+ const stored = yield* upwardStep(authCall(read, [resultKeys.chunk(args.id, 0)]));
744
817
  if (stored === false)
745
818
  return unavailableResult();
746
819
  if (stored === null) {
@@ -776,19 +849,19 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
776
849
  !["read", "write"].includes(dependency.classification))) ||
777
850
  !["read", "write"].includes(binding.classification))
778
851
  return denied();
779
- const allowed = async () => {
852
+ const allowed = authMethod(() => authFlow(function* () {
853
+ options.onStage?.("access validation");
780
854
  options.signal?.throwIfAborted();
781
855
  try {
782
- if (binding.program &&
783
- !(await registry.recheckResultAccess(undefined, binding.classification, options.signal)))
856
+ const dependencies = binding.program ?? [binding];
857
+ if (!(yield* upwardStep(registryProgram(registry, "recheckResultAccess", dependencies, binding.classification, options.signal))))
784
858
  return false;
859
+ options.signal?.throwIfAborted();
785
860
  if (binding.classification === "write" && opts.readOnly === true)
786
861
  return false;
787
- const dependencies = binding.program ?? [binding];
862
+ const catalogs = new Map();
788
863
  for (const dependency of dependencies) {
789
- const address = dependency.tool === undefined ? dependency.connector : `${dependency.connector}.${dependency.tool}`;
790
- if (!(await registry.recheckResultAccess(address, dependency.classification, options.signal, dependency.access)))
791
- return false;
864
+ options.signal?.throwIfAborted();
792
865
  if (dependency.tool === undefined) {
793
866
  if (dependency.access === "catalog"
794
867
  ? !registry.getConnector(dependency.connector)
@@ -799,7 +872,12 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
799
872
  const connector = registry.getConnector(dependency.connector);
800
873
  if (!connector)
801
874
  return false;
802
- const tools = await registry.getTools(dependency.connector, baseUrl, requestScope, options.signal ? { signal: options.signal } : {});
875
+ let tools = catalogs.get(dependency.connector);
876
+ if (!tools) {
877
+ tools = yield* upwardStep(registryProgram(registry, "getTools", dependency.connector, baseUrl, requestScope, options.signal ? { signal: options.signal } : {}));
878
+ catalogs.set(dependency.connector, tools);
879
+ }
880
+ options.signal?.throwIfAborted();
803
881
  const tool = tools.find((tool) => tool.name === dependency.tool);
804
882
  if (!tool ||
805
883
  !(dependency.access === "catalog" ||
@@ -813,9 +891,10 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
813
891
  options.signal?.throwIfAborted();
814
892
  return false;
815
893
  }
816
- };
817
- if (!(await allowed()))
894
+ }));
895
+ if (!(yield* upwardStep(authCall(allowed, []))))
818
896
  return denied();
897
+ options.onStage?.("result storage");
819
898
  opts.onResultRead?.(binding.program ?? [binding]);
820
899
  let requestedOffset = args.offset ?? 0;
821
900
  const cap = Number(header[4]);
@@ -845,7 +924,9 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
845
924
  bytes = new Uint8Array(Math.max(0, end - start));
846
925
  const lastChunk = Math.floor(Math.max(end - 1, start) / chunkBytes);
847
926
  for (let index = Math.floor(start / chunkBytes); index <= lastChunk; index++) {
848
- const encoded = index === 0 ? stored.slice(header[0].length) : await read(resultKeys.chunk(args.id, index));
927
+ const encoded = index === 0
928
+ ? stored.slice(header[0].length)
929
+ : yield* upwardStep(authCall(read, [resultKeys.chunk(args.id, index)]));
849
930
  if (encoded === false)
850
931
  return unavailableResult();
851
932
  if (encoded === null) {
@@ -894,28 +975,28 @@ function metaToolsForRequest(registry, baseUrl, opts = {}) {
894
975
  hasMore,
895
976
  ...(hasMore ? { nextOffset: end } : {}),
896
977
  };
897
- if (!(await allowed()))
978
+ if (!(yield* upwardStep(authCall(allowed, []))))
898
979
  return denied();
899
980
  const page = noticeFirst(pageHeader, slice);
900
981
  page.structuredContent = { ...pageHeader, text: slice, format: "text" };
901
982
  return page;
902
- },
903
- async authorizeConnector(args) {
983
+ })),
984
+ authorizeConnector: authMethod((args) => authFlow(function* () {
904
985
  try {
905
- return jsonResult(await authorizationHandoff(registry, baseUrl, requestScope, opts, args));
986
+ return jsonResult(yield* upwardStep(authCall(authorizeConnector, [registry, baseUrl, requestScope, opts, args])));
906
987
  }
907
988
  catch (err) {
908
989
  return errorResult(msg(err));
909
990
  }
910
- },
991
+ })),
911
992
  };
912
993
  }
913
- const SEARCH_DESC = (readOnly) => `${readOnly ? "Use top-level search for catalog inspection; this read-only endpoint omits write tools." : "Use top-level search for catalog inspection or approval-required work before call_destructive_tool."} Unknown-address read-only work belongs in one execute_code program that searches, calls, and returns the answer. Use 2–4 action/object terms and includeSchemas="compact"; the default limit is ${DEFAULT_SEARCH_LIMIT}. Set connector when known. safety="readOnly" finds tools that run unasked; "approvalRequired" finds the fail-closed complement. These filters grant no authority. Empty query browses. Returns { catalogErrors, tools, total, offset, limit, hasMore }; read catalogErrors and absence before selecting a tool. Compact schemas have schemaFormat="text".`;
994
+ const SEARCH_DESC = (readOnly) => `${readOnly ? "Use top-level search for catalog inspection; this read-only endpoint omits write tools." : "Use top-level search for catalog inspection or write work before call_destructive_tool."} Unknown-address read-only work belongs in one execute_code program that searches, calls, and returns the answer. Use 2–4 action/object terms and includeSchemas="compact"; the default limit is ${DEFAULT_SEARCH_LIMIT}. Set connector when known. safety="readOnly" selects reads; "approvalRequired" selects the fail-closed write complement. The host owns confirmation; these filters grant no authority. Empty query browses. Returns { catalogErrors, tools, total, offset, limit, hasMore }; read catalogErrors and absence before selecting a tool. Compact schemas have schemaFormat="text".`;
914
995
  const CALL_DESC = (readOnly) => `Call one known-address tool explicitly annotated readOnlyHint: true. Use execute_code for unknown-address, multiple, dependent, or reduced read-only work. ${readOnly ? "This endpoint is read-only and refuses every write." : "Unannotated or write-capable tools fail closed to call_destructive_tool."} A truncated result carries a connecta.result action.`;
915
996
  const CALL_DESTRUCTIVE_DESC = "Call any tool not explicitly annotated readOnlyHint: true. Include a short reason for the human reviewer after checking the schema and consequences. The reason grants no authority and is not sent downstream.";
916
997
  /** A read-only endpoint keeps the six-tool surface, so this tool says why it never writes here. */
917
998
  const CALL_DESTRUCTIVE_READ_ONLY_DESC = "This endpoint is read-only: search and describe omit write tools, and every write call here is refused. Reads still run; prefer call_tool or execute_code for them.";
918
- const AUTHORIZE_DESC = "Use after auth_required. Returns an OAuth or operator-credential handoff, or reports required deployment configuration. force=true requests an OAuth restart when the verified user opens the /connect URL; this tool never accepts credentials.";
999
+ const AUTHORIZE_DESC = "Use after auth_required. Returns an OAuth or operator-credential handoff, or required deployment configuration. A configured credential reports presence only: if a call just failed with an authentication or operator-configuration error, give the retained handoff to the operator; otherwise proceed with the call. force=true requests an OAuth restart when the verified user opens the /connect URL; this tool never accepts credentials.";
919
1000
  const SKILLS_DESC = "List or fetch on-demand guidance. Fetch usage only when the always-loaded instructions are insufficient or a program needs repair.";
920
1001
  /**
921
1002
  * Sentences appended to a meta-tool description only when this connection
@@ -982,6 +1063,13 @@ const CALL_DESTRUCTIVE_INPUT = advertisedSchema(z.strictObject({
982
1063
  // trade. It is normalized to absent in the handler instead.
983
1064
  reason: z.string().max(500).optional(),
984
1065
  }));
1066
+ const READ_RESULT_DESC = "Page by resultId and nextAction from the truncation notice. UTF-8 byte ranges: continue from nextOffset while hasMore. Never repeat a write for output.";
1067
+ const READ_RESULT_INPUT = advertisedSchema(z.strictObject({
1068
+ id: z.string(),
1069
+ offset: z.number().int().nonnegative().optional(),
1070
+ page: z.number().int().nonnegative().optional(),
1071
+ maxBytes: z.number().int().positive().optional(),
1072
+ }));
985
1073
  const AUTHORIZE_INPUT = advertisedSchema(z.object({
986
1074
  connector: z.string(),
987
1075
  force: z.boolean().optional(),
@@ -996,6 +1084,8 @@ const AUTHORIZE_INPUT = advertisedSchema(z.object({
996
1084
  */
997
1085
  export function registerMetaTools(server, registry, ctx) {
998
1086
  const mt = createMetaTools(registry, ctx.baseUrl, {
1087
+ ...(ctx.native && !ctx.native.executeCode ? { pagingTool: "read_result" } : {}),
1088
+ ...(ctx.native ? { activitySource: "native" } : {}),
999
1089
  downstreamInput: Boolean(ctx.authElicitation),
1000
1090
  requestScope: ctx.requestScope,
1001
1091
  readOnly: ctx.readOnly,
@@ -1017,22 +1107,40 @@ export function registerMetaTools(server, registry, ctx) {
1017
1107
  };
1018
1108
  server.registerTool("skills", {
1019
1109
  description: describedFor(registry, SKILLS_DESC, "skills"),
1110
+ ...(ctx.native ? { _meta: { "dev.connecta/surface": { surface: "native", ...ctx.native } } } : {}),
1020
1111
  inputSchema: SKILLS_INPUT,
1021
1112
  outputSchema: SKILLS_OUTPUT,
1022
1113
  annotations: READ_ONLY_LOCAL,
1023
- }, async (args, request) => {
1114
+ }, authMethod((args, request) => authFlow(function* () {
1024
1115
  bindRequest(request);
1025
- return mt.skills(args);
1026
- });
1116
+ return yield* upwardStep(authInvoke(mt, "skills", [args]));
1117
+ })));
1118
+ if (ctx.native) {
1119
+ if (!ctx.native.executeCode) {
1120
+ server.registerTool("read_result", { description: READ_RESULT_DESC, inputSchema: READ_RESULT_INPUT, annotations: READ_ONLY_LOCAL }, authMethod((args, request) => authFlow(function* () {
1121
+ bindRequest(request);
1122
+ return yield* upwardStep(authInvoke(mt, "readResult", [args, { signal: request.mcpReq.signal }]));
1123
+ })));
1124
+ }
1125
+ // A native call is the call_tool or call_destructive_tool call it names,
1126
+ // run exactly as those two tools run it below.
1127
+ return authMethod((route, call, request) => {
1128
+ bindRequest(request);
1129
+ const run = authMethod(() => authInvoke(mt, route === "call_tool" ? "callTool" : "callDestructiveTool", [call]));
1130
+ return ctx.authElicitation
1131
+ ? authInvoke(ctx.authElicitation, "run", [route, call, request, run])
1132
+ : authCall(run, []);
1133
+ });
1134
+ }
1027
1135
  server.registerTool("search_tools", {
1028
1136
  description: describedFor(registry, SEARCH_DESC(ctx.readOnly === true), "search"),
1029
1137
  inputSchema: SEARCH_INPUT,
1030
1138
  outputSchema: SEARCH_OUTPUT,
1031
1139
  annotations: READ_ONLY_REMOTE,
1032
- }, async (args, request) => {
1140
+ }, authMethod((args, request) => authFlow(function* () {
1033
1141
  bindRequest(request);
1034
- return mt.searchTools(args);
1035
- });
1142
+ return yield* upwardStep(authInvoke(mt, "searchTools", [args]));
1143
+ })));
1036
1144
  server.registerTool("call_tool", {
1037
1145
  description: CALL_DESC(ctx.readOnly === true),
1038
1146
  inputSchema: CALL_INPUT,
@@ -1040,12 +1148,17 @@ export function registerMetaTools(server, registry, ctx) {
1040
1148
  // call_tool admits only tools that are themselves explicitly read-only;
1041
1149
  // anything else is refused and routed to call_destructive_tool.
1042
1150
  annotations: READ_ONLY_REMOTE,
1043
- }, async (args, request) => {
1151
+ }, authMethod((args, request) => authFlow(function* () {
1044
1152
  bindRequest(request);
1045
- return ctx.authElicitation
1046
- ? ctx.authElicitation.run("call_tool", args, request, () => mt.callTool(args))
1047
- : mt.callTool(args);
1048
- });
1153
+ return yield* upwardStep(ctx.authElicitation
1154
+ ? authInvoke(ctx.authElicitation, "run", [
1155
+ "call_tool",
1156
+ args,
1157
+ request,
1158
+ authMethod(() => authInvoke(mt, "callTool", [args])),
1159
+ ])
1160
+ : authInvoke(mt, "callTool", [args]));
1161
+ })));
1049
1162
  server.registerTool("call_destructive_tool", {
1050
1163
  description: ctx.readOnly === true
1051
1164
  ? CALL_DESTRUCTIVE_READ_ONLY_DESC
@@ -1056,7 +1169,7 @@ export function registerMetaTools(server, registry, ctx) {
1056
1169
  readOnlyHint: false,
1057
1170
  openWorldHint: true,
1058
1171
  },
1059
- }, async (args, request) => {
1172
+ }, authMethod((args, request) => authFlow(function* () {
1060
1173
  bindRequest(request);
1061
1174
  // `reason` is the host's to display and connecta's to keep out of the
1062
1175
  // downstream call, so this destructuring is the whole of its handling:
@@ -1064,10 +1177,15 @@ export function registerMetaTools(server, registry, ctx) {
1064
1177
  // whitespace-only one "absent" rather than a validation failure — there
1065
1178
  // is no field left for it to be absent from.
1066
1179
  const { reason: _hostContext, ...call } = args;
1067
- return ctx.authElicitation
1068
- ? ctx.authElicitation.run("call_destructive_tool", call, request, () => mt.callDestructiveTool(call))
1069
- : mt.callDestructiveTool(call);
1070
- });
1180
+ return yield* upwardStep(ctx.authElicitation
1181
+ ? authInvoke(ctx.authElicitation, "run", [
1182
+ "call_destructive_tool",
1183
+ call,
1184
+ request,
1185
+ authMethod(() => authInvoke(mt, "callDestructiveTool", [call])),
1186
+ ])
1187
+ : authInvoke(mt, "callDestructiveTool", [call]));
1188
+ })));
1071
1189
  server.registerTool("authorize_connector", {
1072
1190
  description: AUTHORIZE_DESC,
1073
1191
  inputSchema: AUTHORIZE_INPUT,
@@ -1079,10 +1197,17 @@ export function registerMetaTools(server, registry, ctx) {
1079
1197
  destructiveHint: false,
1080
1198
  openWorldHint: true,
1081
1199
  },
1082
- }, async (args, request) => {
1200
+ }, authMethod((args, request) => authFlow(function* () {
1083
1201
  bindRequest(request);
1084
- return ctx.authElicitation
1085
- ? ctx.authElicitation.run("authorize_connector", args, request, () => mt.authorizeConnector(args))
1086
- : mt.authorizeConnector(args);
1087
- });
1202
+ const run = authMethod(() => authInvoke(mt, "authorizeConnector", [args]));
1203
+ return yield* upwardStep(ctx.authElicitation
1204
+ ? authInvoke(ctx.authElicitation, "run", [
1205
+ "authorize_connector",
1206
+ args,
1207
+ request,
1208
+ run,
1209
+ ])
1210
+ : authCall(run, []));
1211
+ })));
1212
+ return undefined;
1088
1213
  }