@zackbart/connecta 0.34.0 → 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 (428) hide show
  1. package/CHANGELOG.md +393 -0
  2. package/README.md +62 -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 +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 +15 -7
  53. package/dist/connectors/api-connector.js +147 -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/option-shapes.js +1 -1
  58. package/dist/connectors/remote-mcp.d.ts +12 -30
  59. package/dist/connectors/remote-mcp.js +960 -564
  60. package/dist/d1.js +10 -243
  61. package/dist/derived-context.js +5 -0
  62. package/dist/describe-config.d.ts +5 -1
  63. package/dist/describe-config.js +3 -0
  64. package/dist/downstream-input.js +347 -260
  65. package/dist/errors.d.ts +87 -6
  66. package/dist/errors.js +114 -4
  67. package/dist/execute.js +87 -50
  68. package/dist/executor-admission.js +3 -0
  69. package/dist/executors/quickjs.js +163 -112
  70. package/dist/guarded-transport.js +50 -0
  71. package/dist/guest-runtime.js +1 -1
  72. package/dist/http/client.js +439 -0
  73. package/dist/http/errors.js +558 -0
  74. package/dist/http/fields.js +49 -0
  75. package/dist/http/hatch-paging.js +318 -0
  76. package/dist/http/hatch.js +1279 -0
  77. package/dist/http/json-schema.js +267 -0
  78. package/dist/http/node-transport.js +241 -0
  79. package/dist/http/oauth.js +162 -0
  80. package/dist/http/paging.js +228 -0
  81. package/dist/http/provider.js +221 -0
  82. package/dist/http/safety.js +82 -0
  83. package/dist/http/services.js +79 -0
  84. package/dist/http/tools.js +383 -0
  85. package/dist/http/vendor-index.js +138 -0
  86. package/dist/http/vendor.js +448 -0
  87. package/dist/http/write-request.js +66 -0
  88. package/dist/http-policy.d.ts +11 -0
  89. package/dist/http-policy.js +45 -0
  90. package/dist/inbound-credential.js +1 -1
  91. package/dist/index.d.ts +2 -2
  92. package/dist/index.js +4 -0
  93. package/dist/invocation-auth.js +30 -19
  94. package/dist/invocation.js +221 -87
  95. package/dist/mcp-result.js +0 -4
  96. package/dist/membership.d.ts +4 -0
  97. package/dist/meta-output.js +1 -1
  98. package/dist/meta-tools.js +534 -408
  99. package/dist/native-recovery.js +52 -0
  100. package/dist/native-tools.js +382 -0
  101. package/dist/node.d.ts +4 -3
  102. package/dist/node.js +45 -8
  103. package/dist/oauth-handoff.js +177 -104
  104. package/dist/oauth-partition.js +6 -20
  105. package/dist/oauth-sealing.js +6 -7
  106. package/dist/operation-vocabulary.js +101 -0
  107. package/dist/operator-record.d.ts +2 -2
  108. package/dist/operator-record.js +17 -4
  109. package/dist/operator-ui/agents.d.ts +5 -4
  110. package/dist/operator-ui/agents.js +17 -7
  111. package/dist/operator-ui/config-summary.js +8 -4
  112. package/dist/operator-ui/connection-map.js +9 -6
  113. package/dist/operator-ui/contract.d.ts +7 -3
  114. package/dist/operator-ui/endpoints.js +5 -14
  115. package/dist/operator-ui/fix-prompts.d.ts +1 -1
  116. package/dist/operator-ui/fix-prompts.js +0 -8
  117. package/dist/operator-ui/generated.js +3 -3
  118. package/dist/operator-ui/health.js +52 -0
  119. package/dist/operator-ui/model.d.ts +2 -3
  120. package/dist/operator-ui/overview.js +11 -19
  121. package/dist/operator-ui/setup-commands.js +0 -4
  122. package/dist/operator-ui/view.d.ts +5 -29
  123. package/dist/operator-ui/view.js +3 -59
  124. package/dist/program-writes.js +47 -26
  125. package/dist/provider-call-facts.d.ts +115 -0
  126. package/dist/provider-call-facts.js +182 -0
  127. package/dist/provider-call.d.ts +112 -0
  128. package/dist/provider-call.js +271 -0
  129. package/dist/provider.d.ts +71 -32
  130. package/dist/provider.js +134 -40
  131. package/dist/providers/_shared/google/hatch.js +91 -0
  132. package/dist/providers/_shared/google/kit.js +537 -0
  133. package/dist/providers/_shared/google/result-size.js +15 -2
  134. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  135. package/dist/providers/_shared/google/service-account.js +8 -4
  136. package/dist/providers/_shared/google/workspace.d.ts +8 -209
  137. package/dist/providers/_shared/google/workspace.js +7 -426
  138. package/dist/providers/_shared/rest/compact-index.js +86 -0
  139. package/dist/providers/_shared/rest/dispatch.js +3 -23
  140. package/dist/providers/_shared/rest/kit.js +546 -0
  141. package/dist/providers/_shared/rest/operation-index.js +230 -14
  142. package/dist/providers/_shared/rest/response-media.js +11 -0
  143. package/dist/providers/_shared/rest/tools.js +101 -78
  144. package/dist/providers/_shared/rest/value-safety.js +353 -195
  145. package/dist/providers/_shared/sensitive-fields.js +27 -0
  146. package/dist/providers/breeze/api.js +584 -0
  147. package/dist/providers/breeze/index.d.ts +3 -3
  148. package/dist/providers/breeze/index.js +67 -1420
  149. package/dist/providers/breeze/openapi.generated.js +60 -0
  150. package/dist/providers/breeze/skill.generated.js +2 -6
  151. package/dist/providers/breeze/tools.js +696 -0
  152. package/dist/providers/ccb/api.js +496 -0
  153. package/dist/providers/ccb/index.d.ts +17 -37
  154. package/dist/providers/ccb/index.js +77 -1702
  155. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  156. package/dist/providers/ccb/lib/constants.js +74 -0
  157. package/dist/providers/ccb/lib/guide.js +9 -0
  158. package/dist/providers/ccb/lib/verdicts.js +387 -0
  159. package/dist/providers/ccb/openapi.generated.js +397 -0
  160. package/dist/providers/ccb/safety.js +295 -0
  161. package/dist/providers/ccb/skill.generated.js +2 -6
  162. package/dist/providers/ccb/tools.js +853 -0
  163. package/dist/providers/cloudflare/api.js +1066 -0
  164. package/dist/providers/cloudflare/index.d.ts +7 -22
  165. package/dist/providers/cloudflare/index.js +80 -194
  166. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  167. package/dist/providers/cloudflare/lib/config.js +2 -0
  168. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  169. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  170. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  171. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  172. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  173. package/dist/providers/cloudflare/openapi.generated.js +3299 -3208
  174. package/dist/providers/cloudflare/{value-safety.js → safety.js} +538 -18
  175. package/dist/providers/cloudflare/skill.generated.js +2 -10
  176. package/dist/providers/cloudflare/tools.js +360 -0
  177. package/dist/providers/docs/api.js +51 -0
  178. package/dist/providers/docs/index.d.ts +1 -12
  179. package/dist/providers/docs/index.js +53 -1165
  180. package/dist/providers/docs/lib/constants.d.ts +18 -0
  181. package/dist/providers/docs/lib/constants.js +18 -0
  182. package/dist/providers/docs/lib/discovery.js +26 -0
  183. package/dist/providers/docs/lib/requests.js +48 -0
  184. package/dist/providers/docs/lib/schemas.js +168 -0
  185. package/dist/providers/docs/openapi.generated.js +16 -0
  186. package/dist/providers/docs/safety.js +54 -0
  187. package/dist/providers/docs/skill.generated.js +2 -6
  188. package/dist/providers/docs/tools.js +802 -0
  189. package/dist/providers/drive/api.js +177 -0
  190. package/dist/providers/drive/index.d.ts +1 -12
  191. package/dist/providers/drive/index.js +38 -1448
  192. package/dist/providers/drive/lib/constants.d.ts +6 -0
  193. package/dist/providers/drive/lib/constants.js +6 -0
  194. package/dist/providers/drive/lib/contracts.js +532 -0
  195. package/dist/providers/drive/lib/hatch.js +197 -0
  196. package/dist/providers/drive/openapi.generated.js +77 -0
  197. package/dist/providers/drive/safety.js +90 -0
  198. package/dist/providers/drive/skill.generated.js +2 -6
  199. package/dist/providers/drive/tools.js +1290 -0
  200. package/dist/providers/forms/api.js +71 -0
  201. package/dist/providers/forms/index.d.ts +1 -12
  202. package/dist/providers/forms/index.js +49 -1145
  203. package/dist/providers/forms/lib/constants.d.ts +14 -0
  204. package/dist/providers/forms/lib/constants.js +19 -0
  205. package/dist/providers/forms/lib/hatch.js +72 -0
  206. package/dist/providers/forms/openapi.generated.js +23 -0
  207. package/dist/providers/forms/safety.js +34 -0
  208. package/dist/providers/forms/skill.generated.js +2 -8
  209. package/dist/providers/forms/tools.js +1024 -0
  210. package/dist/providers/github/api.js +150 -0
  211. package/dist/providers/github/index.d.ts +4 -5
  212. package/dist/providers/github/index.js +30 -87
  213. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  214. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  215. package/dist/providers/github/lib/discovery.js +132 -0
  216. package/dist/providers/github/lib/operations.js +58 -0
  217. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  218. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  219. package/dist/providers/github/openapi.generated.js +1216 -0
  220. package/dist/providers/github/safety.js +216 -0
  221. package/dist/providers/github/skill.generated.js +2 -5
  222. package/dist/providers/github/tools.js +506 -0
  223. package/dist/providers/gmail/api.js +114 -0
  224. package/dist/providers/gmail/index.d.ts +1 -12
  225. package/dist/providers/gmail/index.js +73 -1621
  226. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  227. package/dist/providers/gmail/lib/constants.js +16 -0
  228. package/dist/providers/gmail/lib/index.js +173 -0
  229. package/dist/providers/gmail/lib/schemas.js +176 -0
  230. package/dist/providers/gmail/lib/threading.js +120 -0
  231. package/dist/providers/gmail/openapi.generated.js +92 -0
  232. package/dist/providers/gmail/safety.js +21 -0
  233. package/dist/providers/gmail/skill.generated.js +2 -6
  234. package/dist/providers/gmail/tools.js +1435 -0
  235. package/dist/providers/overflow/api.js +1008 -0
  236. package/dist/providers/overflow/index.d.ts +3 -17
  237. package/dist/providers/overflow/index.js +60 -1488
  238. package/dist/providers/overflow/skill.generated.js +2 -5
  239. package/dist/providers/overflow/tools.js +873 -0
  240. package/dist/providers/planning-center/api.js +467 -0
  241. package/dist/providers/planning-center/index.d.ts +6 -35
  242. package/dist/providers/planning-center/index.js +69 -2445
  243. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  244. package/dist/providers/planning-center/lib/constants.js +83 -0
  245. package/dist/providers/planning-center/lib/discovery.js +40 -0
  246. package/dist/providers/planning-center/lib/guide.js +39 -0
  247. package/dist/providers/planning-center/lib/json-api.js +83 -0
  248. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  249. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  250. package/dist/providers/planning-center/safety.js +188 -0
  251. package/dist/providers/planning-center/skill.generated.js +2 -27
  252. package/dist/providers/planning-center/tools.js +1010 -0
  253. package/dist/providers/sheets/api.js +133 -0
  254. package/dist/providers/sheets/index.d.ts +4 -16
  255. package/dist/providers/sheets/index.js +61 -1172
  256. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  257. package/dist/providers/sheets/lib/constants.js +14 -0
  258. package/dist/providers/sheets/lib/hatch.js +163 -0
  259. package/dist/providers/sheets/lib/requests.js +105 -0
  260. package/dist/providers/sheets/lib/schemas.js +138 -0
  261. package/dist/providers/sheets/openapi.generated.js +30 -0
  262. package/dist/providers/sheets/safety.js +27 -0
  263. package/dist/providers/sheets/skill.generated.js +2 -7
  264. package/dist/providers/sheets/tools.js +734 -0
  265. package/dist/providers/slides/api.js +147 -0
  266. package/dist/providers/slides/index.d.ts +2 -10
  267. package/dist/providers/slides/index.js +48 -2555
  268. package/dist/providers/slides/lib/constants.d.ts +11 -0
  269. package/dist/providers/slides/lib/constants.js +11 -0
  270. package/dist/providers/slides/lib/requests.js +60 -0
  271. package/dist/providers/slides/lib/schemas.js +565 -0
  272. package/dist/providers/slides/openapi.generated.js +18 -0
  273. package/dist/providers/slides/skill.generated.js +2 -6
  274. package/dist/providers/slides/tools.js +1968 -0
  275. package/dist/providers/stripe/api.js +401 -0
  276. package/dist/providers/stripe/index.d.ts +7 -27
  277. package/dist/providers/stripe/index.js +75 -191
  278. package/dist/providers/stripe/lib/config.d.ts +4 -0
  279. package/dist/providers/stripe/lib/config.js +2 -0
  280. package/dist/providers/stripe/openapi.generated.js +4 -3
  281. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  282. package/dist/providers/stripe/skill.generated.js +2 -10
  283. package/dist/providers/stripe/tools.js +77 -0
  284. package/dist/providers/tithely/api.js +531 -0
  285. package/dist/providers/tithely/index.d.ts +19 -0
  286. package/dist/providers/tithely/index.js +86 -965
  287. package/dist/providers/tithely/skill.generated.js +2 -5
  288. package/dist/providers/tithely/tools.js +426 -0
  289. package/dist/providers/vercel/api.js +551 -0
  290. package/dist/providers/vercel/index.d.ts +11 -25
  291. package/dist/providers/vercel/index.js +67 -261
  292. package/dist/providers/vercel/lib/config.d.ts +2 -0
  293. package/dist/providers/vercel/lib/config.js +2 -0
  294. package/dist/providers/vercel/lib/index.js +117 -0
  295. package/dist/providers/vercel/lib/logs.js +100 -0
  296. package/dist/providers/vercel/lib/pin.js +270 -0
  297. package/dist/providers/vercel/openapi.generated.js +398 -376
  298. package/dist/providers/vercel/{value-safety.js → safety.js} +261 -42
  299. package/dist/providers/vercel/skill.generated.js +2 -7
  300. package/dist/providers/vercel/tools.js +642 -0
  301. package/dist/redaction-identity.js +47 -0
  302. package/dist/redaction-marker.js +125 -0
  303. package/dist/registry.d.ts +30 -28
  304. package/dist/registry.js +446 -413
  305. package/dist/routes/access-tokens.js +1 -1
  306. package/dist/routes/actor-labels.js +1 -1
  307. package/dist/routes/connect.js +1 -1
  308. package/dist/routes/dispatch.js +103 -0
  309. package/dist/routes/mcp.js +170 -79
  310. package/dist/routes/oauth.js +3 -1
  311. package/dist/routes/operator.js +2 -2
  312. package/dist/routes/shared.d.ts +5 -4
  313. package/dist/routes/shared.js +244 -177
  314. package/dist/routes/skills.js +10 -8
  315. package/dist/routes/ui-config.js +27 -3
  316. package/dist/routes/ui-data.js +29 -22
  317. package/dist/routes/ui.js +1 -1
  318. package/dist/runtime/call-admission.js +0 -26
  319. package/dist/runtime/call-entry.js +30 -0
  320. package/dist/runtime/call.js +556 -0
  321. package/dist/runtime/connector-scope.js +7 -0
  322. package/dist/runtime/credential-probe.js +34 -0
  323. package/dist/runtime/executor.js +40 -0
  324. package/dist/runtime/native-storage.js +63 -0
  325. package/dist/runtime/registry.js +27 -0
  326. package/dist/runtime/request-scope.js +72 -0
  327. package/dist/runtime/run.js +15 -7
  328. package/dist/runtime/shared-read.js +34 -5
  329. package/dist/runtime/storage.js +34 -21
  330. package/dist/sent-secrets.d.ts +194 -37
  331. package/dist/sent-secrets.js +1743 -340
  332. package/dist/server.js +5 -56
  333. package/dist/skills.js +330 -224
  334. package/dist/sqlite.js +15 -14
  335. package/dist/storage/d1-copy.js +255 -0
  336. package/dist/storage/d1-driver.js +15 -0
  337. package/dist/storage/keys.js +28 -0
  338. package/dist/storage/sql-driver.js +88 -0
  339. package/dist/storage/sql-native.js +544 -0
  340. package/dist/storage/sql-schema.d.ts +6 -0
  341. package/dist/storage/sql-schema.js +15 -0
  342. package/dist/storage/sql.d.ts +14 -20
  343. package/dist/storage/sql.js +29 -535
  344. package/dist/timeout.js +0 -26
  345. package/dist/tool-safety.js +20 -10
  346. package/dist/types.d.ts +11 -99
  347. package/dist/upward.js +3 -0
  348. package/dist/usage-guide.js +3 -1
  349. package/dist/validate.d.ts +4 -2
  350. package/dist/validate.js +86 -9
  351. package/dist/version.d.ts +1 -1
  352. package/dist/version.js +1 -1
  353. package/dist/worker.js +312 -292
  354. package/dist/workspace-subject.js +5 -1
  355. package/dist/write-outcome.js +454 -34
  356. package/documentation/README.md +27 -7
  357. package/documentation/architecture.md +2550 -186
  358. package/documentation/auth.md +821 -98
  359. package/documentation/code-mode.md +56 -21
  360. package/documentation/deploying.md +164 -20
  361. package/documentation/hosted-mcp-migration.md +460 -0
  362. package/documentation/integrating.md +144 -105
  363. package/documentation/meta-tools.md +411 -34
  364. package/documentation/operating.md +28 -4
  365. package/documentation/operator-tests.md +11 -2
  366. package/documentation/operator-ui.md +103 -33
  367. package/documentation/provider-auth-migration.md +38 -63
  368. package/documentation/provider-migration-0.29.md +10 -5
  369. package/documentation/provider-migration-0.35.md +975 -0
  370. package/documentation/providers.md +965 -0
  371. package/examples/worker/AGENTS.md +23 -9
  372. package/examples/worker/README.md +254 -135
  373. package/examples/worker/alchemy.run.ts +116 -0
  374. package/examples/worker/gitignore +10 -0
  375. package/examples/worker/src/connecta.config.ts +23 -21
  376. package/examples/worker/wrangler.jsonc +14 -5
  377. package/guides/connecting-agents.md +157 -0
  378. package/guides/how-it-works.md +156 -0
  379. package/guides/safety-and-trust.md +153 -0
  380. package/package.json +27 -42
  381. package/templates/node/.env.example +3 -0
  382. package/templates/node/README.md +22 -2
  383. package/templates/node/docker-compose.yml +1 -0
  384. package/templates/node/package.json +3 -2
  385. package/templates/node/src/connecta.config.ts +8 -0
  386. package/dist/catalog-drift.d.ts +0 -134
  387. package/dist/catalog-drift.js +0 -493
  388. package/dist/providers/_shared/request-token.js +0 -83
  389. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  390. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  391. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  392. package/dist/providers/basecamp/index.d.ts +0 -43
  393. package/dist/providers/basecamp/index.js +0 -997
  394. package/dist/providers/basecamp/skill.generated.js +0 -9
  395. package/dist/providers/cloudflare/rest.d.ts +0 -37
  396. package/dist/providers/cloudflare/rest.js +0 -1078
  397. package/dist/providers/github/hosted.js +0 -218
  398. package/dist/providers/github/rest.js +0 -321
  399. package/dist/providers/infisical/index.d.ts +0 -7
  400. package/dist/providers/infisical/index.js +0 -713
  401. package/dist/providers/infisical/skill.generated.js +0 -8
  402. package/dist/providers/linear/index.d.ts +0 -55
  403. package/dist/providers/linear/index.js +0 -352
  404. package/dist/providers/linear/skill.generated.js +0 -8
  405. package/dist/providers/mixpanel/index.d.ts +0 -57
  406. package/dist/providers/mixpanel/index.js +0 -468
  407. package/dist/providers/mixpanel/operations.js +0 -20
  408. package/dist/providers/mixpanel/rest.js +0 -603
  409. package/dist/providers/mixpanel/skill.generated.js +0 -10
  410. package/dist/providers/mixpanel/value-safety.js +0 -16
  411. package/dist/providers/notion/index.d.ts +0 -65
  412. package/dist/providers/notion/index.js +0 -368
  413. package/dist/providers/notion/openapi.generated.js +0 -75
  414. package/dist/providers/notion/rest.d.ts +0 -24
  415. package/dist/providers/notion/rest.js +0 -1226
  416. package/dist/providers/notion/skill.generated.js +0 -11
  417. package/dist/providers/notion/value-safety.js +0 -56
  418. package/dist/providers/revenuecat/index.d.ts +0 -49
  419. package/dist/providers/revenuecat/index.js +0 -557
  420. package/dist/providers/revenuecat/skill.generated.js +0 -11
  421. package/dist/providers/stripe/rest.d.ts +0 -14
  422. package/dist/providers/stripe/rest.js +0 -402
  423. package/dist/providers/vercel/rest.d.ts +0 -20
  424. package/dist/providers/vercel/rest.js +0 -1020
  425. package/dist/providers/wispr-flow/index.d.ts +0 -14
  426. package/dist/providers/wispr-flow/index.js +0 -117
  427. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  428. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -0,0 +1,506 @@
1
+ import { Effect, Option, Schema } from "effect";
2
+ import { api } from "../../connectors/api.js";
3
+ import { deriveContext } from "../../derived-context.js";
4
+ import { ConnectorCallError } from "../../errors.js";
5
+ import { Refusal } from "../../http/errors.js";
6
+ import { fieldsInput, fieldsProblem, selectFields } from "../../http/fields.js";
7
+ import { hatch } from "../../http/hatch.js";
8
+ import { githubPage } from "../../http/hatch-paging.js";
9
+ import { Integer, isMinChars } from "../../http/json-schema.js";
10
+ import { boundCursor, StandardPage } from "../../http/paging.js";
11
+ import { Dispatches } from "../../http/services.js";
12
+ import { read, write } from "../../http/tools.js";
13
+ import { Operation } from "../../runtime/call.js";
14
+ import { encodeForAgent, redactBeforeCut } from "../../sent-secrets.js";
15
+ import { tracksEveryRequest } from "../../provider-call.js";
16
+ import { checked } from "../_shared/rest/kit.js";
17
+ import { withholdEmbeddedUrlsIn } from "../_shared/rest/value-safety.js";
18
+ import { githubConnection, rejectedToken } from "./api.js";
19
+ import { contentsPath, githubIndex, GITHUB_HATCH } from "./lib/discovery.js";
20
+ import { READ_PERMISSIONS, RELEASE_PERMISSIONS, hatchRefusal } from "./lib/operations.js";
21
+ import { target, workflowPath, isPullHead } from "./lib/scope.js";
22
+ const text = (description) => Schema.String.check(isMinChars(1)).annotate({ description });
23
+ const row = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
24
+ const repoPath = (t) => `/repos/${encodeURIComponent(t.owner)}/${encodeURIComponent(t.repo)}`;
25
+ const ownerRepo = { owner: text("Configured repository owner."), repo: text("Repository name under that owner.") };
26
+ const optional = (fields) => Schema.Struct(Object.fromEntries(Object.entries(fields).map(([k, v]) => [k, Schema.optionalKey(v)])));
27
+ const ScopeRow = optional({
28
+ org: Schema.String,
29
+ repo: Schema.String,
30
+ owner: Schema.String,
31
+ id: Integer,
32
+ full_name: Schema.String,
33
+ access: Schema.Literals(["read", "read-write"]),
34
+ workflows: Schema.Literal("write"),
35
+ });
36
+ const SearchRow = optional({
37
+ scope: Schema.String,
38
+ repository: Schema.String,
39
+ item: Schema.Record(Schema.String, Schema.Unknown),
40
+ });
41
+ const common = {
42
+ limit: Schema.optionalKey(Integer.check(Schema.isBetween({ minimum: 1, maximum: 100 })).annotate({
43
+ description: "Maximum returned items, default 30; at most 100. Keep the same limit with cursor; omission uses 30.",
44
+ default: 30,
45
+ })),
46
+ cursor: Schema.optionalKey(text("Opaque page.nextCursor from this same search or scope listing.")),
47
+ fields: Schema.optionalKey(fieldsInput("Dot paths into each item; only narrows already-safe data.")),
48
+ };
49
+ const scopeNames = new Set(["org", "repo", "owner", "id", "full_name", "access", "workflows"]);
50
+ const searchNames = new Set(["scope", "repository", "item"]);
51
+ const contextOf = Effect.gen(function* () {
52
+ const op = yield* Effect.serviceOption(Operation);
53
+ if (Option.isNone(op) || !op.value)
54
+ return yield* Effect.die(new Error("Missing GitHub call context."));
55
+ return op.value;
56
+ });
57
+ function selectors(input, policy) {
58
+ if (!Array.isArray(input) || input.length === 0 || input.length > 100)
59
+ throw new ConnectorCallError("invalid_args", "Select between 1 and 100 configured scopes.");
60
+ return input.map((item) => {
61
+ const value = row(item);
62
+ if ((value.org === undefined) === (value.repo === undefined))
63
+ throw new ConnectorCallError("invalid_args", "Each selector names either org or repo.");
64
+ if (value.org !== undefined) {
65
+ const owner = policy.owner(value.org);
66
+ const grant = policy.scopes.find((scope) => scope.org === owner);
67
+ if (!grant)
68
+ throw new ConnectorCallError("invalid_args", "This org is not configured for org-wide access.");
69
+ return grant;
70
+ }
71
+ const parts = typeof value.repo === "string" ? value.repo.split("/") : [];
72
+ const t = target(parts[0], parts.length === 2 ? parts[1] : undefined);
73
+ const grant = policy.scope(t);
74
+ return { repo: `${t.owner}/${t.repo}`, access: grant.access };
75
+ });
76
+ }
77
+ function searchPartitions(scopes) {
78
+ const orgs = new Set(scopes.flatMap((scope) => (scope.org ? [scope.org] : [])));
79
+ const repos = new Map();
80
+ for (const scope of scopes) {
81
+ if (!scope.repo)
82
+ continue;
83
+ const [owner, repo] = scope.repo.split("/");
84
+ if (orgs.has(owner))
85
+ continue;
86
+ if (!repos.has(owner))
87
+ repos.set(owner, new Set());
88
+ repos.get(owner).add(repo);
89
+ }
90
+ const partitions = [...orgs].sort().map((owner) => ({ id: `org:${owner}`, owner, qualifier: `org:${owner}` }));
91
+ const result = [...partitions];
92
+ for (const [owner, names] of [...repos].sort(([a], [b]) => a.localeCompare(b))) {
93
+ const sorted = [...names].sort();
94
+ for (let offset = 0; offset < sorted.length; offset += 5) {
95
+ const chunk = sorted.slice(offset, offset + 5);
96
+ result.push({
97
+ id: chunk.map((repo) => `${owner}/${repo}`).join(","),
98
+ owner,
99
+ repos: chunk,
100
+ qualifier: chunk.map((repo) => `repo:${owner}/${repo}`).join(" "),
101
+ });
102
+ }
103
+ }
104
+ if (result.length > 20)
105
+ throw new ConnectorCallError("invalid_args", "Search fan-out exceeds 20 partitions. Select fewer scopes.");
106
+ return result;
107
+ }
108
+ function resultTarget(item, kind) {
109
+ if (kind === "repositories" || kind === "code") {
110
+ const name = kind === "code" ? row(item.repository).full_name : item.full_name;
111
+ const parts = typeof name === "string" ? name.split("/") : [];
112
+ return target(parts[0], parts.length === 2 ? parts[1] : undefined);
113
+ }
114
+ let url;
115
+ try {
116
+ url = new URL(item.repository_url);
117
+ }
118
+ catch {
119
+ throw new ConnectorCallError("connector_call_failed", "GitHub search omitted the repository binding.");
120
+ }
121
+ const match = /^\/repos\/([^/]+)\/([^/]+)$/.exec(url.pathname);
122
+ if (url.origin !== "https://api.github.com" || !match || url.search || url.hash)
123
+ throw new ConnectorCallError("connector_call_failed", "GitHub search returned an invalid repository binding.");
124
+ return target(match[1], match[2]);
125
+ }
126
+ function authCall(ctx, run) {
127
+ return Effect.tryPromise({
128
+ try: (interrupted) => run(deriveContext(ctx, { signal: ctx.signal ? AbortSignal.any([ctx.signal, interrupted]) : interrupted })),
129
+ catch: (error) => error,
130
+ }).pipe(Effect.catch((error) => error instanceof ConnectorCallError ? Effect.fail(new Refusal({ error })) : Effect.die(error)));
131
+ }
132
+ /** Redact before decoding/cutting; keep only bounded inline UTF-8 content and patches. */
133
+ function boundedData(data, ctx) {
134
+ const cutPaths = new Set();
135
+ const cut = (value, path) => {
136
+ let end = 32768;
137
+ if (value.length <= end)
138
+ return value;
139
+ if (/[\uD800-\uDBFF]/.test(value[end - 1]))
140
+ end--;
141
+ cutPaths.add(path);
142
+ return value.slice(0, end);
143
+ };
144
+ const visit = (value, path) => {
145
+ if (Array.isArray(value))
146
+ return value.map((v) => visit(v, `${path}[]`));
147
+ if (value === null || typeof value !== "object")
148
+ return value;
149
+ const source = value;
150
+ const out = Object.create(null);
151
+ for (const [key, entry] of Object.entries(source)) {
152
+ const at = `${path}.${key}`;
153
+ if (key === "patch" && typeof entry === "string")
154
+ out[key] = cut(withholdEmbeddedUrlsIn(redactBeforeCut(ctx, entry)), at);
155
+ else if (key === "content" && source.encoding === "base64" && typeof entry === "string") {
156
+ if (entry.includes("[redacted]")) {
157
+ out[key] = "[redacted]";
158
+ continue;
159
+ }
160
+ const normalized = entry.replace(/\s/g, "");
161
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(normalized))
162
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned invalid base64 file content.");
163
+ let decoded;
164
+ try {
165
+ decoded = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Uint8Array.from(atob(normalized), (c) => c.charCodeAt(0)));
166
+ }
167
+ catch {
168
+ throw new ConnectorCallError("invalid_args", "This GitHub connection returns inline UTF-8 file content only; binary and raw downloads are unavailable.");
169
+ }
170
+ const safe = withholdEmbeddedUrlsIn(redactBeforeCut(ctx, decoded));
171
+ out[key] = encodeForAgent(ctx, cut(safe, at));
172
+ }
173
+ else
174
+ out[key] = visit(entry, at);
175
+ }
176
+ return out;
177
+ };
178
+ const safe = visit(data, "data");
179
+ return { data: safe, ...(cutPaths.size ? { truncated: [...cutPaths] } : {}) };
180
+ }
181
+ export function restConnector(id, auth, policy) {
182
+ const connection = githubConnection(), adapter = githubPage("https://api.github.com");
183
+ const send = (request, bearer, options = {}) => Effect.gen(function* () {
184
+ const dispatches = yield* Dispatches, start = dispatches.length;
185
+ return yield* connection
186
+ .sendPath({
187
+ method: request.method,
188
+ path: request.path,
189
+ template: request.path,
190
+ ...(request.query
191
+ ? {
192
+ query: Object.entries(request.query).flatMap(([k, v]) => v === undefined ? [] : (Array.isArray(v) ? v : [v]).map((entry) => [k, String(entry)])),
193
+ }
194
+ : {}),
195
+ ...(request.body !== undefined ? { json: JSON.stringify(request.body) } : {}),
196
+ headers: { Authorization: `Bearer ${bearer}` },
197
+ }, options)
198
+ .pipe(Effect.ensuring(Effect.sync(() => {
199
+ if (dispatches
200
+ .slice(start)
201
+ .some((d) => d.status !== undefined && rejectedToken(d.status, d.headers ?? new Headers())))
202
+ void auth.rejectToken(bearer);
203
+ })));
204
+ });
205
+ const cursorArgs = (args) => {
206
+ const { fields: _fields, cursor: _cursor, ...rest } = args;
207
+ return { ...rest, grants: policy.scopes };
208
+ };
209
+ const decode = (name, args) => args.cursor === undefined
210
+ ? Effect.succeed(undefined)
211
+ : boundCursor.decode(name, cursorArgs(args), String(args.cursor));
212
+ const finish = (name, args, items, next, extra = {}) => Effect.gen(function* () {
213
+ const page = next === undefined
214
+ ? { hasMore: false }
215
+ : {
216
+ hasMore: true,
217
+ nextCursor: yield* boundCursor.encode(name, cursorArgs(args), JSON.stringify({ ...next, limit: args.limit })),
218
+ };
219
+ return {
220
+ items: selectFields(items, args.fields),
221
+ page,
222
+ ...extra,
223
+ };
224
+ });
225
+ const parse = (cursor, limit) => {
226
+ if (cursor === undefined)
227
+ return {};
228
+ const position = JSON.parse(cursor);
229
+ if (!Number.isSafeInteger(position.limit) || position.limit < 1 || position.limit > 100)
230
+ throw new ConnectorCallError("invalid_args", "This listing cursor is out of date; start a new listing.");
231
+ if (position.limit !== limit)
232
+ throw new ConnectorCallError("invalid_args", `This cursor was issued with limit ${position.limit}; repeat limit ${position.limit} or start a new listing.`);
233
+ return position;
234
+ };
235
+ const namesCheck = (fields, names) => checked(() => {
236
+ const bad = fieldsProblem(fields, names, "an item");
237
+ if (bad)
238
+ throw bad;
239
+ });
240
+ const scopesTool = read(connection, {
241
+ name: "list_scopes",
242
+ description: "List configured GitHub grants locally, or verified reachable repositories for one configured owner. Without owner this reads configuration without network access.",
243
+ input: Schema.Struct({ owner: Schema.optionalKey(text("Configured owner; omit for local grants.")), ...common }),
244
+ output: Schema.Struct({ items: Schema.Array(ScopeRow), page: StandardPage }),
245
+ run: (args) => Effect.gen(function* () {
246
+ yield* namesCheck(args.fields, scopeNames);
247
+ const limit = args.limit ?? 30, normalized = { ...args, limit };
248
+ const raw = yield* decode("list_scopes", normalized);
249
+ const position = yield* checked(() => parse(raw, limit));
250
+ const page = position.page ?? 1;
251
+ if (!Number.isSafeInteger(page) || page < 1 || page > 1000)
252
+ return yield* checked(() => {
253
+ throw new ConnectorCallError("invalid_args", "This repository cursor is outside its page bound.");
254
+ });
255
+ if (args.owner === undefined) {
256
+ const items = policy.scopes.slice((page - 1) * limit, page * limit);
257
+ return yield* finish("list_scopes", normalized, items, page * limit < policy.scopes.length ? { page: page + 1 } : undefined);
258
+ }
259
+ const owner = yield* checked(() => policy.owner(args.owner)), ctx = yield* contextOf;
260
+ const token = yield* authCall(ctx, (view) => auth.token(owner, undefined, { metadata: "read" }, view));
261
+ const answer = yield* send({ method: "GET", path: "/installation/repositories", query: { page, per_page: limit } }, token, { responseHeaders: ["link"] });
262
+ const body = row(answer.data);
263
+ if (!Array.isArray(body.repositories))
264
+ return yield* checked(() => {
265
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned an invalid repository list.");
266
+ });
267
+ const items = [];
268
+ for (const entry of body.repositories) {
269
+ const repository = row(entry), t = yield* checked(() => resultTarget(repository, "repositories"));
270
+ if (t.owner !== owner)
271
+ return yield* checked(() => {
272
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned a repository from another installation owner.");
273
+ });
274
+ yield* authCall(ctx, (view) => auth.checkResult(t, repository.id, token, view));
275
+ const grant = yield* checked(() => policy.scope(t));
276
+ items.push({
277
+ id: repository.id,
278
+ full_name: `${t.owner}/${t.repo}`,
279
+ owner: t.owner,
280
+ repo: t.repo,
281
+ access: grant.access,
282
+ ...(grant.workflows ? { workflows: grant.workflows } : {}),
283
+ });
284
+ }
285
+ const next = yield* checked(() => adapter.next(answer.data, {
286
+ path: "/installation/repositories",
287
+ query: { page, per_page: limit },
288
+ headers: answer.headers ?? {},
289
+ }));
290
+ if (next !== null && page === 1000)
291
+ return yield* checked(() => {
292
+ throw new ConnectorCallError("connector_call_failed", "The GitHub repository list exceeded its page bound; narrow the configured scope.");
293
+ });
294
+ return yield* finish("list_scopes", normalized, items, next === null ? undefined : { page: adapter.decode(next) });
295
+ }),
296
+ });
297
+ const searchTool = read(connection, {
298
+ name: "search_scoped",
299
+ description: "Search GitHub repositories, issues, pull requests or code within configured scopes. Results carry verified repository and partition bindings; continuation visits one bounded partition page at a time.",
300
+ input: Schema.Struct({
301
+ terms: text("Literal words only; qualifiers and Boolean operators are refused."),
302
+ kind: Schema.Literals(["repositories", "issues", "pull-requests", "code"]).annotate({
303
+ description: "Resource to search.",
304
+ }),
305
+ scopes: Schema.optionalKey(Schema.Array(Schema.Struct({
306
+ org: Schema.optionalKey(text("Configured organization grant.")),
307
+ repo: Schema.optionalKey(text("Exact configured owner/repository.")),
308
+ }))
309
+ .check(Schema.isMinLength(1), Schema.isMaxLength(100))
310
+ .annotate({ description: "Optional subset of the configured scopes." })),
311
+ ...common,
312
+ }),
313
+ output: Schema.Struct({ items: Schema.Array(SearchRow), page: StandardPage, incomplete_results: Schema.Boolean }),
314
+ run: (args) => Effect.gen(function* () {
315
+ yield* namesCheck(args.fields, searchNames);
316
+ const selected = yield* checked(() => {
317
+ if (!/^[a-z0-9_. -]{1,128}$/i.test(args.terms) || /\b(?:OR|NOT|AND)\b/i.test(args.terms))
318
+ throw new ConnectorCallError("invalid_args", "Search terms must be literal words, without qualifiers or Boolean operators.");
319
+ return args.scopes === undefined ? policy.scopes : selectors(args.scopes, policy);
320
+ });
321
+ const partitions = yield* checked(() => searchPartitions(selected)), limit = args.limit ?? 30, normalized = { ...args, limit };
322
+ const raw = yield* decode("search_scoped", normalized), at = yield* checked(() => parse(raw, limit));
323
+ const partitionIndex = at.partition ?? 0, page = at.page ?? 1;
324
+ if (!Number.isSafeInteger(partitionIndex) ||
325
+ partitionIndex < 0 ||
326
+ partitionIndex >= partitions.length ||
327
+ !Number.isSafeInteger(page) ||
328
+ page < 1 ||
329
+ page > 10)
330
+ return yield* checked(() => {
331
+ throw new ConnectorCallError("invalid_args", "This search cursor is outside the selected partitions.");
332
+ });
333
+ const partition = partitions[partitionIndex], kind = args.kind === "pull-requests" ? "issues" : args.kind;
334
+ const type = args.kind === "issues" ? " is:issue" : args.kind === "pull-requests" ? " is:pr" : "";
335
+ const query = `${partition.qualifier} ${args.terms.trim()}${type}`;
336
+ if (query.length > 256)
337
+ return yield* checked(() => {
338
+ throw new ConnectorCallError("invalid_args", "The scoped search query exceeds GitHub's 256-character bound. Select fewer repositories.");
339
+ });
340
+ const permissions = args.kind === "repositories"
341
+ ? { metadata: "read" }
342
+ : args.kind === "code"
343
+ ? { contents: "read" }
344
+ : args.kind === "issues"
345
+ ? { issues: "read" }
346
+ : { pull_requests: "read" };
347
+ const ctx = yield* contextOf, token = yield* authCall(ctx, (view) => auth.token(partition.owner, partition.repos, permissions, view));
348
+ const answer = yield* send({ method: "GET", path: `/search/${kind}`, query: { q: query, page, per_page: limit } }, token, { responseHeaders: ["link"] });
349
+ const body = row(answer.data);
350
+ if (!Array.isArray(body.items))
351
+ return yield* checked(() => {
352
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned an invalid search result.");
353
+ });
354
+ const items = [], seen = new Set();
355
+ for (const value of body.items) {
356
+ const item = row(value), t = yield* checked(() => resultTarget(item, args.kind));
357
+ yield* checked(() => policy.owner(t.owner));
358
+ if (t.owner !== partition.owner)
359
+ return yield* checked(() => {
360
+ throw new ConnectorCallError("connector_call_failed", "GitHub search escaped its selected partition; results were withheld.");
361
+ });
362
+ yield* authCall(ctx, (view) => auth.checkResult(t, args.kind === "repositories" ? item.id : args.kind === "code" ? row(item.repository).id : undefined, token, view));
363
+ const key = `${args.kind}:${t.owner}/${t.repo}:${args.kind === "code" ? item.path : item.id}`;
364
+ if (!seen.has(key)) {
365
+ seen.add(key);
366
+ items.push({ scope: partition.id, repository: `${t.owner}/${t.repo}`, item: value });
367
+ }
368
+ }
369
+ const next = yield* checked(() => adapter.next(body, {
370
+ path: `/search/${kind}`,
371
+ query: { q: query, page, per_page: limit },
372
+ headers: answer.headers ?? {},
373
+ }));
374
+ const capped = next !== null && page === 10;
375
+ const position = next !== null && !capped
376
+ ? { partition: partitionIndex, page: adapter.decode(next) }
377
+ : partitionIndex + 1 < partitions.length
378
+ ? { partition: partitionIndex + 1, page: 1 }
379
+ : undefined;
380
+ const incomplete = at.incomplete === true || body.incomplete_results === true || capped;
381
+ return {
382
+ ...(yield* finish("search_scoped", normalized, items, position === undefined ? undefined : { ...position, incomplete })),
383
+ incomplete_results: incomplete,
384
+ };
385
+ }),
386
+ });
387
+ const mergeTool = write(connection, {
388
+ name: "merge_pull_request",
389
+ description: "Merge a same-repository GitHub pull request once, guarded by expected head SHA and explicit workflow-write scope. A failed preflight sends no merge.",
390
+ destructive: true,
391
+ writeSteps: { pull: "once", write: "once" },
392
+ input: Schema.Struct({
393
+ ...ownerRepo,
394
+ pull_number: Integer.check(Schema.isGreaterThanOrEqualTo(1)).annotate({ description: "Pull request number." }),
395
+ sha: text("Expected head commit SHA; reread after a conflict."),
396
+ merge_method: Schema.optionalKey(Schema.Literals(["merge", "squash", "rebase"]).annotate({ description: "Merge strategy, default merge." })),
397
+ }),
398
+ output: Schema.Record(Schema.String, Schema.Unknown),
399
+ run: (args) => Effect.gen(function* () {
400
+ const t = yield* checked(() => target(args.owner, args.repo));
401
+ yield* checked(() => policy.scope(t, true, true));
402
+ const ctx = yield* contextOf, reader = yield* authCall(ctx, (view) => auth.repo(t, { pull_requests: "read" }, view));
403
+ const pr = row((yield* send({ method: "GET", path: `${repoPath(t)}/pulls/${args.pull_number}` }, reader, {
404
+ step: "pull",
405
+ effect: "read",
406
+ })).data);
407
+ const head = row(pr.head), base = row(pr.base);
408
+ if (head.sha !== args.sha ||
409
+ row(head.repo).full_name?.toLowerCase() !== `${t.owner}/${t.repo}` ||
410
+ row(base.repo).full_name?.toLowerCase() !== `${t.owner}/${t.repo}`)
411
+ return yield* checked(() => {
412
+ throw new ConnectorCallError("conflict", "Merge requires the expected head SHA and a same-repository head and base. Reread the pull request.");
413
+ });
414
+ const token = yield* authCall(ctx, (view) => auth.repo(t, { contents: "write", pull_requests: "write", workflows: "write" }, view));
415
+ return row((yield* send({
416
+ method: "PUT",
417
+ path: `${repoPath(t)}/pulls/${args.pull_number}/merge`,
418
+ body: { sha: args.sha, merge_method: args.merge_method ?? "merge" },
419
+ }, token)).data);
420
+ }),
421
+ });
422
+ const binding = {
423
+ review: "Repository-ID-bound App tokens and per-operation documented permissions; all unreviewed operations, raw search and merge refuse before authentication.",
424
+ truncated: true,
425
+ path: contentsPath,
426
+ refusal: (op) => hatchRefusal(`${op.method} ${op.path}`),
427
+ refuse: (call) => {
428
+ const key = `${call.method} ${call.op.path}`, refusal = hatchRefusal(key);
429
+ if (refusal)
430
+ return refusal;
431
+ const { params } = githubIndex.resolve(call.method, call.path), t = target(params.owner, params.repo);
432
+ policy.scope(t, Boolean(RELEASE_PERMISSIONS[key]));
433
+ if (params.path !== undefined && params.path !== "")
434
+ workflowPath(decodeURIComponent(params.path));
435
+ const pullList = key === "GET /repos/{owner}/{repo}/pulls";
436
+ if (pullList && call.query.head !== undefined && !isPullHead(call.query.head))
437
+ return "GitHub needs head in owner:branch form; a bare branch is silently ignored. Pass a valid login and branch.";
438
+ // A PR head label only filters this repository's PRs. Owner-prefixed
439
+ // object refs can select fork-network objects and remain unavailable.
440
+ const refs = [
441
+ params.ref,
442
+ params.basehead,
443
+ params.tag,
444
+ ...["ref", "sha", "base", "branch", "head_sha"].map((name) => call.query[name]),
445
+ ...(pullList ? [] : [call.query.head]),
446
+ ...["target_commitish", "tag_name"].map((name) => row(call.body)[name]),
447
+ ];
448
+ for (const ref of refs) {
449
+ if (typeof ref !== "string")
450
+ continue;
451
+ let decoded;
452
+ try {
453
+ decoded = decodeURIComponent(ref);
454
+ }
455
+ catch {
456
+ return "Use a literal repository-local ref, without malformed percent-encoding.";
457
+ }
458
+ if (/[:%]/.test(decoded))
459
+ return params.basehead !== undefined
460
+ ? "Compare refs within this repository; cross-repository compare is unavailable."
461
+ : "Use refs within this repository; owner-prefixed and encoded repository aliases are unavailable.";
462
+ }
463
+ const perPage = call.query.per_page;
464
+ if (perPage !== undefined &&
465
+ (!/^\d+$/.test(String(perPage)) ||
466
+ !Number.isSafeInteger(Number(perPage)) ||
467
+ Number(perPage) < 1 ||
468
+ Number(perPage) > 100))
469
+ return "GitHub per_page must be a whole number from 1 to 100.";
470
+ return undefined;
471
+ },
472
+ send: (call, options) => Effect.gen(function* () {
473
+ const key = `${call.method} ${call.op.path}`, { params } = githubIndex.resolve(call.method, call.path), t = target(params.owner, params.repo), ctx = yield* contextOf;
474
+ const permissions = READ_PERMISSIONS[key] ?? RELEASE_PERMISSIONS[key];
475
+ if (!permissions)
476
+ return yield* checked(() => {
477
+ throw new ConnectorCallError("invalid_args", "This GitHub operation has no reviewed permission declaration.");
478
+ });
479
+ const token = yield* authCall(ctx, (view) => auth.repo(t, permissions, view));
480
+ const answer = yield* send({
481
+ method: call.method,
482
+ path: call.path,
483
+ query: call.query,
484
+ ...(call.body === undefined ? {} : { body: call.body }),
485
+ }, token, options);
486
+ const bounded = yield* checked(() => boundedData(answer.data, ctx));
487
+ return { ...answer, ...bounded };
488
+ }),
489
+ };
490
+ return tracksEveryRequest(api(id, {
491
+ tools: [
492
+ ...hatch({
493
+ ...GITHUB_HATCH,
494
+ connection,
495
+ binding,
496
+ notes: {
497
+ read: "Only the reviewed repository reads are available. Nested contents paths such as /repos/alice/widget/contents/src/index.ts and the root /repos/alice/widget/contents are accepted. In commits/{ref} and compare/{basehead}, encode branch-name slashes as %2F. Owner-prefixed object refs are refused; PR-list head filters require login:branch. Pass page.nextCursor as cursor; raw page numbers, diffs and log downloads are refused.",
498
+ write: "Only release create/update/delete are available here. Use merge_pull_request for merges.",
499
+ },
500
+ }),
501
+ scopesTool,
502
+ searchTool,
503
+ mergeTool,
504
+ ],
505
+ }));
506
+ }
@@ -0,0 +1,114 @@
1
+ // Gmail's declared operations are confined to the delegated user's own mailbox.
2
+ import { Effect, Schema } from "effect";
3
+ import { encodedFieldProvenance, get, post, put, vendor } from "../../http/vendor.js";
4
+ import { googleErrors } from "../_shared/google/kit.js";
5
+ import { gmailSafety } from "./safety.js";
6
+ import { ConnectorCallError } from "../../errors.js";
7
+ import { GMAIL_SCOPES } from "./lib/constants.js";
8
+ export const GMAIL = {
9
+ provider: "Gmail",
10
+ api: "Gmail API",
11
+ scopes: GMAIL_SCOPES,
12
+ // users/me confines every request to the token's own mailbox.
13
+ notFound: "absent",
14
+ };
15
+ const Id = { id: Schema.String };
16
+ const Format = {
17
+ format: Schema.optionalKey(Schema.String),
18
+ metadataHeaders: Schema.optionalKey(Schema.Array(Schema.String)),
19
+ };
20
+ const Page = {
21
+ q: Schema.optionalKey(Schema.String),
22
+ maxResults: Schema.optionalKey(Schema.String),
23
+ pageToken: Schema.optionalKey(Schema.String),
24
+ };
25
+ export const listLabels = get("listLabels", "/labels", { success: Schema.Unknown });
26
+ const OPERATIONS = [
27
+ get("listThreads", "/threads", {
28
+ query: { ...Page, includeSpamTrash: Schema.optionalKey(Schema.String) },
29
+ success: Schema.Unknown,
30
+ }),
31
+ get("getThread", "/threads/:id", { params: Id, query: Format, success: Schema.Unknown }),
32
+ get("getMessage", "/messages/:id", { params: Id, query: Format, success: Schema.Unknown }),
33
+ get("getAttachment", "/messages/:messageId/attachments/:id", {
34
+ params: { messageId: Schema.String, ...Id },
35
+ success: Schema.Unknown,
36
+ }),
37
+ listLabels,
38
+ get("listMessages", "/messages", { query: Page, success: Schema.Unknown }),
39
+ get("getLabel", "/labels/:id", { params: Id, success: Schema.Unknown }),
40
+ get("getProfile", "/profile", { success: Schema.Unknown }),
41
+ get("listHistory", "/history", { query: Page, success: Schema.Unknown }),
42
+ get("listDrafts", "/drafts", { query: Page, success: Schema.Unknown }),
43
+ get("getDraft", "/drafts/:id", { params: Id, query: Format, success: Schema.Unknown }),
44
+ post("createDraft", "/drafts", { payload: Schema.Unknown, success: Schema.Unknown }),
45
+ put("updateDraft", "/drafts/:id", { params: Id, payload: Schema.Unknown, success: Schema.Unknown }),
46
+ ];
47
+ // The manifest uses Discovery's path; transport stays fixed beneath users/me.
48
+ const bodyPaths = [
49
+ "payload",
50
+ "message.payload",
51
+ "messages[*].payload",
52
+ "drafts[*].message.payload",
53
+ "history[*].messages[*].payload",
54
+ "history[*].messagesAdded[*].message.payload",
55
+ "history[*].messagesDeleted[*].message.payload",
56
+ "history[*].labelsAdded[*].message.payload",
57
+ "history[*].labelsRemoved[*].message.payload",
58
+ ].flatMap((prefix) =>
59
+ // The normal MIME walk and a parsed forwarded-message walk each admit 20 levels.
60
+ Array.from({ length: 41 }, (_, depth) => `${prefix}${".parts[*]".repeat(depth)}.body.data`));
61
+ export const GmailApi = {
62
+ ...vendor({ name: "Gmail", manifestBase: "/gmail/v1/users/{userId}" }, ...OPERATIONS),
63
+ // Legacy body reads accept optional sizes. Attachment projection instead checks
64
+ // originalLength against the fresh part's required size and the download's optional size.
65
+ encodedFields: [
66
+ ...bodyPaths,
67
+ "data",
68
+ "raw",
69
+ "message.raw",
70
+ "messages[*].raw",
71
+ "drafts[*].message.raw",
72
+ "history[*].messages[*].raw",
73
+ "history[*].messagesAdded[*].message.raw",
74
+ "history[*].messagesDeleted[*].message.raw",
75
+ "history[*].labelsAdded[*].message.raw",
76
+ "history[*].labelsRemoved[*].message.raw",
77
+ ].map((path) => ({
78
+ path,
79
+ encoding: "base64url",
80
+ review: "Gmail's bounded MIME projection decodes this body or attachment data before rendering or byte encoding.",
81
+ })),
82
+ };
83
+ const bodyProvenance = new WeakMap();
84
+ /** Product-private metadata copied only by the result hook, before the projection validates attachment sizes. */
85
+ export const gmailBodyProvenance = (body) => bodyProvenance.get(body);
86
+ export const result = (body) => Effect.sync(() => {
87
+ const visit = (node, path) => {
88
+ if (node === null || typeof node !== "object")
89
+ return;
90
+ const provenance = encodedFieldProvenance(body, [...path, "data"]);
91
+ if (provenance)
92
+ bodyProvenance.set(node, provenance);
93
+ // A generic MIME read cannot expose an encoded lane beyond the reviewed
94
+ // decoder paths. Named walks are bounded too, but hatch JSON keeps the whole graph.
95
+ if (!provenance && typeof node["data"] === "string" && path.at(-1) === "body")
96
+ throw new ConnectorCallError("connector_call_failed", "Gmail returned MIME data beyond the reviewed nesting boundary.", { retryable: false });
97
+ for (const [key, child] of Object.entries(node))
98
+ if (child !== null && typeof child === "object")
99
+ visit(child, [...path, Array.isArray(node) ? Number(key) : key]);
100
+ };
101
+ visit(body, []);
102
+ return body;
103
+ });
104
+ const standardErrors = googleErrors(GMAIL, { standard: true });
105
+ export const errors = {
106
+ ...standardErrors,
107
+ meanings: { ...standardErrors.meanings, conflicts: ["A thread changed before its content cursor"] },
108
+ };
109
+ export const safety = gmailSafety(OPERATIONS);
110
+ export const probe = {
111
+ operation: listLabels,
112
+ request: {},
113
+ describe: () => "Gmail delegation has no credential to test.",
114
+ };
@@ -1,17 +1,6 @@
1
1
  import { type GoogleWorkspaceOptions } from "../_shared/google/workspace.js";
2
+ export { GMAIL_API_BASE_URL, GMAIL_SCOPES } from "./lib/constants.js";
2
3
  export type { GoogleServiceAccount, GoogleSubjectContext, GoogleWorkspaceOptions, GoogleWorkspaceSubject, } from "../_shared/google/workspace.js";
3
- /**
4
- * Gmail's API root, confined to the delegated user's own mailbox: `me` is the
5
- * token's subject, and no path can climb out of it.
6
- */
7
- export declare const GMAIL_API_BASE_URL = "https://gmail.googleapis.com/gmail/v1/users/me";
8
- /**
9
- * Exactly the scopes this connection requests, and exactly what the Admin
10
- * console's domain-wide delegation entry must list (comma-separated there).
11
- * `gmail.compose` technically permits sending; the tool surface does not.
12
- */
13
- export declare const GMAIL_SCOPES: readonly ["https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.compose"];
14
- /** Options for {@link gmail}: the shared Workspace delegation options. */
15
4
  export type GmailOptions = GoogleWorkspaceOptions;
16
5
  /**
17
6
  * A maintained, draft-only Gmail connection acting as each signed-in