@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,734 @@
1
+ // Google Sheets named projections, bounded cell reads and guarded writes.
2
+ import { Effect, Option, Schema } from "effect";
3
+ import { ConnectorCallError } from "../../errors.js";
4
+ import { Refusal } from "../../http/errors.js";
5
+ import { googleClient, googleFailureMessage, googleOutcomeOf } from "../_shared/google/kit.js";
6
+ import { clampText, jsonBytes, RESULT_BUDGET_BYTES } from "../_shared/google/result-size.js";
7
+ import { encodeForAgent, redactBeforeCut } from "../../sent-secrets.js";
8
+ import { SHEETS } from "./api.js";
9
+ import { read, write } from "../../http/tools.js";
10
+ import { boundCursor } from "../../http/paging.js";
11
+ import { fieldsInput, fieldsProblem, selectFields } from "../../http/fields.js";
12
+ import { Operation } from "../../runtime/call.js";
13
+ import { sheetsIndex } from "./lib/hatch.js";
14
+ import { SHEETS_ADMITTED_KINDS, SHEETS_REFUSED_KINDS, sheetsExternalDataProblem } from "./lib/requests.js";
15
+ import { inputs, outputs } from "./lib/schemas.js";
16
+ /** Cells per page of get_values: connecta's cap, since Sheets has no paging. */
17
+ const DEFAULT_PAGE_CELLS = 2_000;
18
+ const MAX_PAGE_CELLS = 10_000;
19
+ /**
20
+ * Bytes per page of get_values as serialized, beside the cell count: a page
21
+ * has to be deliverable, and cells say nothing about size — 60 cells of 5,000
22
+ * characters already outgrow the QuickJS executor's host bridge. The default
23
+ * is the Workspace providers' shared result budget, so an execute_code
24
+ * program always receives a default page; unlike that budget's envelope
25
+ * allowance, maxBytes bounds the whole page, fields and cursor included. A
26
+ * direct call_tool caller may ask for more, up to 4 MiB: base64 in the result
27
+ * stash makes that about 5.6 MiB, under the stash's default 8 MiB, so an
28
+ * oversized page still pages through connecta.result rather than being dropped.
29
+ */
30
+ const DEFAULT_PAGE_BYTES = RESULT_BUDGET_BYTES;
31
+ const MAX_PAGE_BYTES = 4 * 1024 * 1024;
32
+ /**
33
+ * Characters a read keeps per cell by default. A cell holds at most 50,000
34
+ * (Sheets' own limit), so `maxCellChars` at that ceiling always reads one whole.
35
+ */
36
+ const DEFAULT_CELL_CHARS = 5_000;
37
+ const MAX_CELL_CHARS = 50_000;
38
+ /** Rows and cells one write may carry: connecta's cap, under Google's 10 MB request. */
39
+ const MAX_WRITE_CELLS = 50_000;
40
+ /** Named ranges a metadata read lists before reporting a size cut. */
41
+ const MAX_NAMED_RANGES = 200;
42
+ function asRecord(value) {
43
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
44
+ }
45
+ function asArray(value) {
46
+ return Array.isArray(value) ? value : [];
47
+ }
48
+ function compact(value) {
49
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
50
+ }
51
+ function text(value) {
52
+ return typeof value === "string" && value !== "" ? value : undefined;
53
+ }
54
+ function integer(value) {
55
+ return typeof value === "number" && Number.isInteger(value) ? value : undefined;
56
+ }
57
+ /** A 0-based column index as its A1 letters: 0 → A, 26 → AA. */
58
+ function columnLetters(index) {
59
+ let letters = "";
60
+ for (let rest = index + 1; rest > 0; rest = Math.floor((rest - 1) / 26)) {
61
+ letters = String.fromCharCode(65 + ((rest - 1) % 26)) + letters;
62
+ }
63
+ return letters;
64
+ }
65
+ /**
66
+ * A sheet title as A1, always single-quoted. Bare, a whole-sheet range is
67
+ * indistinguishable from a named range of the same name — and Google resolves
68
+ * the named range first, so `Sheet1` handed to clear_values could clear
69
+ * something else. No named range can contain a quote, so quoted cannot.
70
+ */
71
+ function sheetPrefix(title) {
72
+ return `'${title.replace(/'/g, "''")}'`;
73
+ }
74
+ /**
75
+ * A GridRange as A1, or `undefined` for a shape A1 cannot say. Google leaves
76
+ * a zero index out of the JSON, and a missing start index means "from the
77
+ * first", so both read as 0; a missing end means "to the edge".
78
+ */
79
+ function gridRangeA1(range, title) {
80
+ if (title === undefined)
81
+ return undefined;
82
+ const startRow = integer(range["startRowIndex"]) ?? 0;
83
+ const startColumn = integer(range["startColumnIndex"]) ?? 0;
84
+ const endRow = integer(range["endRowIndex"]);
85
+ const endColumn = integer(range["endColumnIndex"]);
86
+ const prefix = sheetPrefix(title);
87
+ if (endRow === undefined && endColumn === undefined) {
88
+ return startRow === 0 && startColumn === 0 ? prefix : undefined;
89
+ }
90
+ if (endColumn === undefined) {
91
+ return startColumn === 0 ? `${prefix}!${startRow + 1}:${endRow}` : undefined;
92
+ }
93
+ const start = `${columnLetters(startColumn)}${startRow + 1}`;
94
+ return `${prefix}!${start}:${columnLetters(endColumn - 1)}${endRow ?? ""}`;
95
+ }
96
+ function valuesCursorArgs(args) {
97
+ return {
98
+ spreadsheetId: args["spreadsheetId"],
99
+ ranges: args["ranges"],
100
+ valueRenderOption: args["valueRenderOption"] ?? null,
101
+ dateTimeRenderOption: args["dateTimeRenderOption"] ?? null,
102
+ };
103
+ }
104
+ function encodeCursor(ctx, args, cursor) {
105
+ return boundCursor.encode("get_values", valuesCursorArgs(args), JSON.stringify(cursor), (json) => encodeForAgent(ctx, json, { url: true, claims: true }));
106
+ }
107
+ function decodeCursor(value, ranges) {
108
+ let parsed;
109
+ try {
110
+ parsed = JSON.parse(value);
111
+ }
112
+ catch {
113
+ parsed = undefined;
114
+ }
115
+ const cursor = asRecord(parsed), index = integer(cursor["i"]), skip = integer(cursor["s"]);
116
+ if (Object.keys(cursor).length !== 2 ||
117
+ index === undefined ||
118
+ index < 0 ||
119
+ index >= ranges.length ||
120
+ skip === undefined ||
121
+ skip < 0 ||
122
+ !Number.isSafeInteger(skip)) {
123
+ throw new ConnectorCallError("invalid_args", "Pass the get_values page.nextCursor back unchanged with the same spreadsheet, ranges and render options.");
124
+ }
125
+ return { i: index, s: skip };
126
+ }
127
+ /**
128
+ * The caller's own range past its first `rows` rows, when the range says
129
+ * where it starts: `Sheet1!C5:F100` → `Sheet1!C8:F100`, `B:D` → `B4:D`. A
130
+ * sheet title, a named range, or R1C1 does not, and is resent whole with the
131
+ * rows skipped instead — correct, but it re-reads what earlier pages did.
132
+ */
133
+ function rangeAfter(range, rows) {
134
+ if (rows === 0)
135
+ return range;
136
+ const match = /^(.*!)?([A-Z]+)(\d+)?:([A-Z]+)(\d+)?$/.exec(range);
137
+ if (!match)
138
+ return undefined;
139
+ const [, prefix = "", startColumn, startRow, endColumn, endRow] = match;
140
+ if (startRow === undefined && endRow !== undefined)
141
+ return undefined;
142
+ const first = Number(startRow ?? 1);
143
+ if (endRow !== undefined && first > Number(endRow))
144
+ return undefined;
145
+ const start = first + rows;
146
+ if (endRow !== undefined && start > Number(endRow))
147
+ return undefined;
148
+ return `${prefix}${startColumn}${start}:${endColumn}${endRow ?? ""}`;
149
+ }
150
+ /** A cell as read, cut at `max` characters with a marker saying how to read the rest. Every
151
+ * secret the request sent is replaced first: a cut through an echo leaves a fragment. */
152
+ function cappedCell(ctx, whole, max, cut) {
153
+ if (typeof whole !== "string" || whole.length <= max)
154
+ return whole;
155
+ const value = redactBeforeCut(ctx, whole);
156
+ if (value.length <= max)
157
+ return value;
158
+ cut.cells += 1;
159
+ return `${value.slice(0, max)}[… ${value.length - max} more characters truncated; read this cell alone with maxCellChars up to ${MAX_CELL_CHARS} for the rest]`;
160
+ }
161
+ /** Refuse a row no page of `budget` bytes can hold, rather than overrun the cap. */
162
+ function rowTooLarge(range, row, needed, budget) {
163
+ return new ConnectorCallError("invalid_args", `Row ${row} of ${range} needs a page of ${needed} bytes with the page's own fields, more than this page's maxBytes of ${budget}; narrow the range's columns or lower maxCellChars${raiseBytes(needed)}.`);
164
+ }
165
+ /** Refuse a page too small for even one range's own fields. */
166
+ function pageTooSmall(range, needed, budget) {
167
+ return new ConnectorCallError("invalid_args", `A page reporting ${range} needs ${needed} bytes before any of its rows, more than this page's maxBytes of ${budget}; shorten the range${raiseBytes(needed)}.`);
168
+ }
169
+ function raiseBytes(needed) {
170
+ if (needed > MAX_PAGE_BYTES)
171
+ return "";
172
+ return needed <= DEFAULT_PAGE_BYTES
173
+ ? `, or raise maxBytes to at least ${needed}`
174
+ : `, or raise maxBytes to at least ${needed} (above ${DEFAULT_PAGE_BYTES}, only for a direct call_tool read: execute_code cannot receive it)`;
175
+ }
176
+ /** Refuse a row no page of `budget` cells can hold, rather than overrun the cap. */
177
+ function rowTooWide(range, row, width, budget) {
178
+ const fix = width <= MAX_PAGE_CELLS
179
+ ? `raise maxCells to at least ${width}, or narrow the range's columns`
180
+ : `narrow the range's columns; a page holds at most ${MAX_PAGE_CELLS} cells`;
181
+ return new ConnectorCallError("invalid_args", `Row ${row} of ${range} has ${width} cells, more than this page's maxCells of ${budget}; ${fix}.`);
182
+ }
183
+ // --- Writing values ---------------------------------------------------------------
184
+ /** Refuse a write past connecta's per-call cell cap before anything is sent. */
185
+ function checkWriteSize(blocks) {
186
+ let cells = 0;
187
+ for (const block of blocks)
188
+ for (const row of block)
189
+ cells += row.length;
190
+ if (cells > MAX_WRITE_CELLS) {
191
+ throw new ConnectorCallError("invalid_args", `This write carries ${cells} cells; connecta sends at most ${MAX_WRITE_CELLS} per call (Google's own request limit is larger). Split it into several calls. Nothing was written.`);
192
+ }
193
+ }
194
+ function projectUpdate(value) {
195
+ const update = asRecord(value);
196
+ return compact({
197
+ updatedRange: text(update["updatedRange"]),
198
+ updatedRows: integer(update["updatedRows"]) ?? 0,
199
+ updatedColumns: integer(update["updatedColumns"]) ?? 0,
200
+ updatedCells: integer(update["updatedCells"]) ?? 0,
201
+ });
202
+ }
203
+ /** Refuse a raw request that is not exactly one known kind, unsent. */
204
+ function checkRequestKinds(requests) {
205
+ for (const request of requests)
206
+ for (const kind of Object.keys(asRecord(request)))
207
+ if (Object.hasOwn(SHEETS_REFUSED_KINDS, kind))
208
+ throw new ConnectorCallError("invalid_args", SHEETS_REFUSED_KINDS[kind]);
209
+ const problem = sheetsExternalDataProblem(requests);
210
+ if (problem)
211
+ throw new ConnectorCallError("invalid_args", problem);
212
+ requests.forEach((request, index) => {
213
+ const kinds = Object.keys(asRecord(request));
214
+ if (kinds.length !== 1 || !SHEETS_ADMITTED_KINDS.includes(kinds[0])) {
215
+ throw new ConnectorCallError("invalid_args", `requests[${index}] must set exactly one Sheets Request kind (addSheet, repeatCell, deleteDimension, …); it sets ${kinds.length === 0 ? "none" : kinds.slice(0, 3).join(", ")}. Nothing was sent.`);
216
+ }
217
+ });
218
+ }
219
+ /**
220
+ * A write whose outcome is unknown — sent, and then anything but an answer
221
+ * Google refused it with: a reply that broke off, no reply, a redirect, or a
222
+ * 5xx after receiving it — told what to read before trying again, for the
223
+ * writes where the shared "re-read before repeating it" has nothing to re-read
224
+ * or where repeating does harm. The advice claims nothing either way; only
225
+ * the client's own message says "probably applied", and only for a 2xx whose
226
+ * body broke off. A refusal, and anything never sent, passes through as the
227
+ * client mapped it.
228
+ */
229
+ function uncertainWrite(connection, advice, write) {
230
+ return write.pipe(Effect.catch((failure) => {
231
+ const outcome = googleOutcomeOf(failure);
232
+ return outcome?.dispatched && outcome.phase !== "refused"
233
+ ? googleFailureMessage(connection, failure, (message) => `${message} ${advice}`)
234
+ : Effect.fail(failure);
235
+ }));
236
+ }
237
+ // --- Raw batchUpdate replies -----------------------------------------------------
238
+ /** A string kept in a reply summary, cut so a long one cannot crowd out ids. */
239
+ const SUMMARY_TEXT_BYTES = 256;
240
+ /** The scalar fields of an object — ids, titles, counts — and nothing nested. */
241
+ function scalarsOf(ctx, value) {
242
+ const scalars = {};
243
+ for (const [key, field] of Object.entries(asRecord(value))) {
244
+ if (typeof field === "string") {
245
+ scalars[key] = clampText(ctx, field, SUMMARY_TEXT_BYTES, (dropped) => `[… ${dropped} more characters]`);
246
+ }
247
+ else if (typeof field === "number" || typeof field === "boolean") {
248
+ scalars[key] = field;
249
+ }
250
+ }
251
+ return Object.keys(scalars).length > 0 ? scalars : undefined;
252
+ }
253
+ /**
254
+ * One reply reduced to its kind and the scalars an agent acts on next: a new
255
+ * sheet's id and title, a chart's id, findReplace's counts. Google's replies
256
+ * put those at the top of the kind's body or one object down (`properties`,
257
+ * `chart`, `namedRange`); arrays and anything deeper are dropped.
258
+ */
259
+ function summarizeReply(ctx, reply) {
260
+ const kind = Object.keys(reply)[0];
261
+ if (kind === undefined)
262
+ return {};
263
+ const body = asRecord(reply[kind]);
264
+ const nested = {};
265
+ for (const [key, field] of Object.entries(body)) {
266
+ if (field && typeof field === "object" && !Array.isArray(field))
267
+ nested[key] = scalarsOf(ctx, field);
268
+ }
269
+ return compact({ kind, ...scalarsOf(ctx, body), ...compact(nested) });
270
+ }
271
+ /**
272
+ * A batchUpdate result that can be delivered. The write has already applied,
273
+ * so whatever does not fit is summarized, never dropped silently and never
274
+ * reported as a failure: replies are kept whole, in order, while the ones
275
+ * after them still fit as summaries; the rest become summaries; and a reply
276
+ * set too large even for that leaves the counts and where to re-read.
277
+ */
278
+ function deliverableReplies(ctx, spreadsheetId, replies, requestCount) {
279
+ const whole = { spreadsheetId, replies };
280
+ if (jsonBytes(whole) <= RESULT_BUDGET_BYTES)
281
+ return whole;
282
+ const note = `All ${requestCount} requests applied. Replies too large to return here are cut to their kind, ids, and counts; read the spreadsheet with get_spreadsheet or get_values for the rest. Do not send the requests again.`;
283
+ const summaries = replies.map((reply) => summarizeReply(ctx, reply));
284
+ // What every reply from index i on costs as a summary, comma included.
285
+ const rest = Array.from({ length: replies.length + 1 }, () => 0);
286
+ for (let index = replies.length - 1; index >= 0; index -= 1) {
287
+ rest[index] = rest[index + 1] + jsonBytes(summaries[index]) + 1;
288
+ }
289
+ let bytes = jsonBytes({ spreadsheetId, replies: [], truncated: ["replies[]"], note });
290
+ if (bytes + rest[0] <= RESULT_BUDGET_BYTES) {
291
+ const kept = [];
292
+ let summarized = 0;
293
+ replies.forEach((reply, index) => {
294
+ const size = jsonBytes(reply) + 1;
295
+ if (summarized === 0 && bytes + size + rest[index + 1] <= RESULT_BUDGET_BYTES) {
296
+ bytes += size;
297
+ kept.push(reply);
298
+ }
299
+ else {
300
+ summarized += 1;
301
+ kept.push(summaries[index]);
302
+ }
303
+ });
304
+ return { spreadsheetId, replies: kept, truncated: ["replies[]"], note };
305
+ }
306
+ return {
307
+ spreadsheetId,
308
+ replies: [],
309
+ truncated: ["replies[]"],
310
+ note: `All ${requestCount} requests applied, but their ${replies.length} replies are too large to return even summarized. Read the spreadsheet with get_spreadsheet or get_values to see the result. Do not send the requests again.`,
311
+ };
312
+ }
313
+ function projectSheet(value) {
314
+ const properties = asRecord(asRecord(value)["properties"]);
315
+ const grid = asRecord(properties["gridProperties"]);
316
+ return compact({
317
+ // The first sheet's id is 0, which Google leaves out of the JSON.
318
+ sheetId: integer(properties["sheetId"]) ?? 0,
319
+ title: text(properties["title"]),
320
+ index: integer(properties["index"]) ?? 0,
321
+ type: text(properties["sheetType"]),
322
+ hidden: properties["hidden"] === true ? true : undefined,
323
+ rowCount: integer(grid["rowCount"]),
324
+ columnCount: integer(grid["columnCount"]),
325
+ frozenRowCount: integer(grid["frozenRowCount"]),
326
+ frozenColumnCount: integer(grid["frozenColumnCount"]),
327
+ });
328
+ }
329
+ /** The metadata get_spreadsheet reads: no grid data, no formatting. */
330
+ const SPREADSHEET_FIELDS = [
331
+ "spreadsheetId",
332
+ "spreadsheetUrl",
333
+ "properties(title,locale,timeZone)",
334
+ "sheets.properties(sheetId,title,index,sheetType,hidden,gridProperties(rowCount,columnCount,frozenRowCount,frozenColumnCount))",
335
+ "namedRanges(namedRangeId,name,range)",
336
+ ].join(",");
337
+ // --- Tools ------------------------------------------------------------------------
338
+ export function sheetsTools({ connection }) {
339
+ const client = googleClient(connection, SHEETS.provider, { standard: true, index: sheetsIndex });
340
+ const readOnly = { readOnlyHint: true };
341
+ // Additive: nothing that existed is lost. Writes require a writable
342
+ // endpoint: call_destructive_tool or a program. The host controls approval.
343
+ const additive = { readOnlyHint: false, destructiveHint: false };
344
+ const destructive = { readOnlyHint: false, destructiveHint: true };
345
+ const spreadsheetPath = (args) => `/spreadsheets/${encodeURIComponent(String(args["spreadsheetId"]))}`;
346
+ const declarations = [
347
+ {
348
+ name: "get_spreadsheet",
349
+ description: "Get a spreadsheet's title, sheets (ids, titles, grid sizes), and named ranges as A1, never cell values. To find one by name, search Drive; Sheets cannot list.",
350
+ annotations: readOnly,
351
+ input: inputs.get_spreadsheet,
352
+ output: outputs.get_spreadsheet,
353
+ run: (args) => Effect.gen(function* () {
354
+ const spreadsheet = asRecord(yield* client.json({
355
+ method: "GET",
356
+ path: spreadsheetPath(args),
357
+ template: "/spreadsheets/{spreadsheetId}",
358
+ query: { fields: SPREADSHEET_FIELDS },
359
+ }));
360
+ const properties = asRecord(spreadsheet["properties"]);
361
+ const sheets = asArray(spreadsheet["sheets"]).map(projectSheet);
362
+ const titles = new Map(sheets.map((sheet) => [sheet["sheetId"], sheet["title"]]));
363
+ const named = asArray(spreadsheet["namedRanges"]);
364
+ return compact({
365
+ spreadsheetId: text(spreadsheet["spreadsheetId"]) ?? args["spreadsheetId"],
366
+ title: text(properties["title"]),
367
+ url: text(spreadsheet["spreadsheetUrl"]),
368
+ locale: text(properties["locale"]),
369
+ timeZone: text(properties["timeZone"]),
370
+ sheets,
371
+ namedRanges: named.length > 0
372
+ ? named.slice(0, MAX_NAMED_RANGES).map((value) => {
373
+ const entry = asRecord(value);
374
+ const range = asRecord(entry["range"]);
375
+ const sheetId = integer(range["sheetId"]) ?? 0;
376
+ return compact({
377
+ namedRangeId: text(entry["namedRangeId"]),
378
+ name: text(entry["name"]),
379
+ sheetId,
380
+ range: gridRangeA1(range, titles.get(sheetId)),
381
+ });
382
+ })
383
+ : undefined,
384
+ truncated: named.length > MAX_NAMED_RANGES ? ["namedRanges[]"] : undefined,
385
+ });
386
+ }),
387
+ },
388
+ {
389
+ name: "get_values",
390
+ description: "Read cell values from one or more A1 ranges as rows, paged by cell count. Long cells are cut with a marker; no formatting, notes, or formulas unless asked.",
391
+ annotations: readOnly,
392
+ input: inputs.get_values,
393
+ output: outputs.get_values,
394
+ run: (args, ctx) => Effect.gen(function* () {
395
+ const ranges = args["ranges"];
396
+ const budget = args["maxCells"] ?? DEFAULT_PAGE_CELLS;
397
+ const byteBudget = args["maxBytes"] ?? DEFAULT_PAGE_BYTES;
398
+ const cellChars = args["maxCellChars"] ?? DEFAULT_CELL_CHARS;
399
+ const resume = typeof args["cursor"] === "string"
400
+ ? decodeCursor(yield* boundCursor.decode("get_values", valuesCursorArgs(args), args["cursor"]), ranges)
401
+ : { i: 0, s: 0 };
402
+ // Every range sent is the caller's own, or the caller's own rewritten
403
+ // to start further down; nothing in the cursor names a range.
404
+ const rewritten = rangeAfter(ranges[resume.i], resume.s);
405
+ const sent = [rewritten ?? ranges[resume.i], ...ranges.slice(resume.i + 1)];
406
+ const skip = rewritten === undefined ? resume.s : 0;
407
+ const response = asRecord(yield* client.json({
408
+ method: "GET",
409
+ path: `${spreadsheetPath(args)}/values:batchGet`,
410
+ template: "/spreadsheets/{spreadsheetId}/values:batchGet",
411
+ query: {
412
+ ranges: sent,
413
+ majorDimension: "ROWS",
414
+ valueRenderOption: args["valueRenderOption"],
415
+ dateTimeRenderOption: args["dateTimeRenderOption"],
416
+ },
417
+ }));
418
+ const spreadsheetId = text(response["spreadsheetId"]) ?? args["spreadsheetId"];
419
+ const valueRanges = [];
420
+ const cut = { cells: 0 };
421
+ let cells = 0;
422
+ // maxBytes bounds the whole page as serialized, not just its cells. The
423
+ // envelope is reserved first at its largest: both counts at their
424
+ // ceiling and the longest cursor this call could end with.
425
+ let bytes = jsonBytes({
426
+ spreadsheetId,
427
+ valueRanges: [],
428
+ cellCount: MAX_PAGE_CELLS,
429
+ truncated: ["valueRanges[].values"],
430
+ page: {
431
+ hasMore: false,
432
+ nextCursor: yield* encodeCursor(ctx, args, { i: ranges.length - 1, s: Number.MAX_SAFE_INTEGER }),
433
+ },
434
+ });
435
+ let next;
436
+ const returned = asArray(response["valueRanges"]);
437
+ for (let index = 0; index < returned.length && next === undefined; index += 1) {
438
+ const valueRange = asRecord(returned[index]);
439
+ // Rows of the caller's range earlier pages returned, and of those,
440
+ // how many Google sent again and this page drops.
441
+ const before = index === 0 ? resume.s : 0;
442
+ const dropped = index === 0 ? skip : 0;
443
+ const rows = asArray(valueRange["values"]).slice(dropped);
444
+ const echoed = text(valueRange["range"]) ?? sent[index];
445
+ const entry = (kept) => compact({
446
+ range: echoed,
447
+ rowOffset: dropped > 0 ? dropped : undefined,
448
+ values: kept,
449
+ rowCount: kept.length,
450
+ });
451
+ // This range's own fields at their largest, and the comma before it;
452
+ // an empty range costs them too.
453
+ const fields = jsonBytes(compact({
454
+ range: echoed,
455
+ rowOffset: dropped > 0 ? dropped : undefined,
456
+ values: [],
457
+ rowCount: rows.length,
458
+ })) + 1;
459
+ if (bytes + fields > byteBudget) {
460
+ if (valueRanges.length === 0)
461
+ throw pageTooSmall(echoed, bytes + fields, byteBudget);
462
+ next = { i: resume.i + index, s: before };
463
+ break;
464
+ }
465
+ bytes += fields;
466
+ const kept = [];
467
+ for (const row of rows) {
468
+ const empty = valueRanges.length === 0 && kept.length === 0;
469
+ // An empty row still costs one cell, so every page makes progress.
470
+ const width = Math.max(1, asArray(row).length);
471
+ if (cells + width > budget) {
472
+ if (cells === 0)
473
+ throw rowTooWide(echoed, dropped + kept.length + 1, width, budget);
474
+ break;
475
+ }
476
+ const rowCut = { cells: 0 };
477
+ const capped = asArray(row).map((cell) => cappedCell(ctx, cell, cellChars, rowCut));
478
+ // The row as serialized, plus the comma before it.
479
+ const size = jsonBytes(capped) + 1;
480
+ if (bytes + size > byteBudget) {
481
+ if (empty)
482
+ throw rowTooLarge(echoed, dropped + 1, bytes + size, byteBudget);
483
+ break;
484
+ }
485
+ cells += width;
486
+ bytes += size;
487
+ cut.cells += rowCut.cells;
488
+ kept.push(capped);
489
+ }
490
+ if (kept.length < rows.length) {
491
+ next = { i: resume.i + index, s: before + kept.length };
492
+ // A range this page never reached is not reported half-read; the
493
+ // next page starts it.
494
+ if (kept.length > 0)
495
+ valueRanges.push(entry(kept));
496
+ break;
497
+ }
498
+ valueRanges.push(entry(kept));
499
+ if (cells >= budget && index + 1 < returned.length) {
500
+ next = { i: resume.i + index + 1, s: 0 };
501
+ }
502
+ }
503
+ const nextCursor = next ? yield* encodeCursor(ctx, args, next) : null;
504
+ return compact({
505
+ spreadsheetId,
506
+ valueRanges,
507
+ cellCount: cells,
508
+ truncated: cut.cells > 0 || next ? ["valueRanges[].values"] : undefined,
509
+ page: compact({ hasMore: nextCursor !== null, nextCursor: nextCursor ?? undefined }),
510
+ });
511
+ }),
512
+ },
513
+ {
514
+ name: "update_values",
515
+ description: "Write rows of values into one A1 range, overwriting what is there. Cells past the given rows are untouched; to add rows below a table use append_values.",
516
+ annotations: destructive,
517
+ input: inputs.update_values,
518
+ output: outputs.update_values,
519
+ run: (args) => Effect.gen(function* () {
520
+ checkWriteSize([args["values"]]);
521
+ const response = asRecord(yield* client.json({
522
+ method: "POST",
523
+ path: `${spreadsheetPath(args)}/values:batchUpdate`,
524
+ template: "/spreadsheets/{spreadsheetId}/values:batchUpdate",
525
+ body: {
526
+ valueInputOption: args["valueInputOption"],
527
+ data: [{ range: args["range"], majorDimension: "ROWS", values: args["values"] }],
528
+ includeValuesInResponse: false,
529
+ },
530
+ }));
531
+ return {
532
+ spreadsheetId: text(response["spreadsheetId"]) ?? args["spreadsheetId"],
533
+ ...projectUpdate(asArray(response["responses"])[0]),
534
+ };
535
+ }),
536
+ },
537
+ {
538
+ name: "batch_update_values",
539
+ description: "Write values into several A1 ranges in one call, overwriting each. All succeed or none do. For formatting or structure use batch_update_spreadsheet.",
540
+ annotations: destructive,
541
+ input: inputs.batch_update_values,
542
+ output: outputs.batch_update_values,
543
+ run: (args) => Effect.gen(function* () {
544
+ const data = asArray(args["data"]).map(asRecord);
545
+ checkWriteSize(data.map((entry) => entry["values"]));
546
+ const response = asRecord(yield* client.json({
547
+ method: "POST",
548
+ path: `${spreadsheetPath(args)}/values:batchUpdate`,
549
+ template: "/spreadsheets/{spreadsheetId}/values:batchUpdate",
550
+ body: {
551
+ valueInputOption: args["valueInputOption"],
552
+ data: data.map((entry) => ({ range: entry["range"], majorDimension: "ROWS", values: entry["values"] })),
553
+ includeValuesInResponse: false,
554
+ },
555
+ }));
556
+ return {
557
+ spreadsheetId: text(response["spreadsheetId"]) ?? args["spreadsheetId"],
558
+ totalUpdatedRows: integer(response["totalUpdatedRows"]) ?? 0,
559
+ totalUpdatedColumns: integer(response["totalUpdatedColumns"]) ?? 0,
560
+ totalUpdatedCells: integer(response["totalUpdatedCells"]) ?? 0,
561
+ totalUpdatedSheets: integer(response["totalUpdatedSheets"]) ?? 0,
562
+ updates: asArray(response["responses"]).map(projectUpdate),
563
+ };
564
+ }),
565
+ },
566
+ {
567
+ name: "append_values",
568
+ description: "Append rows after the last row of the table in an A1 range, inserting new rows so nothing below is overwritten. Returns where they landed.",
569
+ annotations: additive,
570
+ input: inputs.append_values,
571
+ output: outputs.append_values,
572
+ run: (args) => Effect.gen(function* () {
573
+ checkWriteSize([args["values"]]);
574
+ const response = asRecord(
575
+ // A second call after one that landed adds the rows twice.
576
+ yield* uncertainWrite(connection, "Appending again would add the rows a second time if the first append landed: read the table with get_values and append only what is missing.", client.json({
577
+ method: "POST",
578
+ path: `${spreadsheetPath(args)}/values/${encodeURIComponent(String(args["range"]))}:append`,
579
+ template: "/spreadsheets/{spreadsheetId}/values/{range}:append",
580
+ query: {
581
+ valueInputOption: args["valueInputOption"],
582
+ // Pinned: OVERWRITE writes over whatever sits below the table.
583
+ insertDataOption: "INSERT_ROWS",
584
+ includeValuesInResponse: false,
585
+ },
586
+ body: { majorDimension: "ROWS", values: args["values"] },
587
+ })));
588
+ return compact({
589
+ spreadsheetId: text(response["spreadsheetId"]) ?? args["spreadsheetId"],
590
+ tableRange: text(response["tableRange"]),
591
+ ...projectUpdate(response["updates"]),
592
+ });
593
+ }),
594
+ },
595
+ {
596
+ name: "clear_values",
597
+ description: "Clear the values in one or more A1 ranges, keeping formatting, notes, and validation. The values are gone; nothing is deleted or shifted.",
598
+ annotations: destructive,
599
+ input: inputs.clear_values,
600
+ output: outputs.clear_values,
601
+ run: (args) => Effect.gen(function* () {
602
+ const response = asRecord(yield* client.json({
603
+ method: "POST",
604
+ path: `${spreadsheetPath(args)}/values:batchClear`,
605
+ template: "/spreadsheets/{spreadsheetId}/values:batchClear",
606
+ body: { ranges: args["ranges"] },
607
+ }));
608
+ return {
609
+ spreadsheetId: text(response["spreadsheetId"]) ?? args["spreadsheetId"],
610
+ clearedRanges: asArray(response["clearedRanges"]).filter((range) => typeof range === "string"),
611
+ };
612
+ }),
613
+ },
614
+ {
615
+ name: "create_spreadsheet",
616
+ description: "Create a new spreadsheet in the user's My Drive root, optionally with named sheets. It is not shared or moved; that is Drive's job.",
617
+ annotations: additive,
618
+ input: inputs.create_spreadsheet,
619
+ output: outputs.create_spreadsheet,
620
+ run: (args) => Effect.gen(function* () {
621
+ const titles = args["sheetTitles"];
622
+ const spreadsheet = asRecord(
623
+ // A spreadsheet that may exist has no id to re-read; only its title
624
+ // can find it, and Sheets cannot search.
625
+ yield* uncertainWrite(connection, "Search Drive for this title before creating it again, or a second spreadsheet results.", client.json({
626
+ method: "POST",
627
+ path: "/spreadsheets",
628
+ template: "/spreadsheets",
629
+ query: { fields: "spreadsheetId,spreadsheetUrl,properties.title,sheets.properties(sheetId,title)" },
630
+ body: compact({
631
+ properties: { title: args["title"] },
632
+ sheets: titles?.map((title) => ({ properties: { title } })),
633
+ }),
634
+ })));
635
+ return compact({
636
+ spreadsheetId: text(spreadsheet["spreadsheetId"]),
637
+ title: text(asRecord(spreadsheet["properties"])["title"]),
638
+ url: text(spreadsheet["spreadsheetUrl"]),
639
+ sheets: asArray(spreadsheet["sheets"]).map((sheet) => {
640
+ const { sheetId, title } = projectSheet(sheet);
641
+ return compact({ sheetId, title });
642
+ }),
643
+ });
644
+ }),
645
+ },
646
+ {
647
+ name: "add_sheet",
648
+ description: "Add a new, empty sheet tab to a spreadsheet. Fails if the title is taken; it never renames, replaces, or copies an existing sheet.",
649
+ annotations: additive,
650
+ input: inputs.add_sheet,
651
+ output: outputs.add_sheet,
652
+ run: (args) => Effect.gen(function* () {
653
+ const grid = compact({ rowCount: args["rowCount"], columnCount: args["columnCount"] });
654
+ const response = asRecord(yield* client.json({
655
+ method: "POST",
656
+ path: `${spreadsheetPath(args)}:batchUpdate`,
657
+ template: "/spreadsheets/{spreadsheetId}:batchUpdate",
658
+ body: {
659
+ requests: [
660
+ {
661
+ addSheet: {
662
+ properties: compact({
663
+ title: args["title"],
664
+ index: args["index"],
665
+ gridProperties: Object.keys(grid).length > 0 ? grid : undefined,
666
+ }),
667
+ },
668
+ },
669
+ ],
670
+ },
671
+ }));
672
+ const added = asRecord(asRecord(asArray(response["replies"])[0])["addSheet"]);
673
+ return {
674
+ spreadsheetId: text(response["spreadsheetId"]) ?? args["spreadsheetId"],
675
+ ...projectSheet(added),
676
+ };
677
+ }),
678
+ },
679
+ {
680
+ name: "batch_update_spreadsheet",
681
+ description: "Apply Sheets batchUpdate requests — formatting, merges, sorts, deleting sheets or rows, protections — atomically. This named structural workflow can destroy data.",
682
+ // Destructive, whatever the requests are: they can delete sheets, rows,
683
+ // ranges, and protections, and connecta does not read them to guess.
684
+ annotations: destructive,
685
+ input: inputs.batch_update_spreadsheet,
686
+ output: outputs.batch_update_spreadsheet,
687
+ run: (args, ctx) => Effect.gen(function* () {
688
+ checkRequestKinds(args["requests"]);
689
+ const response = asRecord(yield* client.json({
690
+ method: "POST",
691
+ path: `${spreadsheetPath(args)}:batchUpdate`,
692
+ template: "/spreadsheets/{spreadsheetId}:batchUpdate",
693
+ body: { requests: args["requests"], includeSpreadsheetInResponse: false },
694
+ }));
695
+ return deliverableReplies(ctx, text(response["spreadsheetId"]) ?? args["spreadsheetId"], asArray(response["replies"]).map(asRecord), asArray(args["requests"]).length);
696
+ }),
697
+ },
698
+ ];
699
+ return declarations.map((declaration) => {
700
+ const spec = {
701
+ name: declaration.name,
702
+ description: declaration.description,
703
+ input: declaration.input,
704
+ output: declaration.output,
705
+ run: (args) => Effect.gen(function* () {
706
+ const current = yield* Effect.serviceOption(Operation);
707
+ if (Option.isNone(current) || !current.value)
708
+ return yield* Effect.die(new Error("Sheets tools require a call context."));
709
+ return yield* declaration.run(args, current.value);
710
+ }).pipe(Effect.catchDefect((defect) => defect instanceof ConnectorCallError ? Effect.fail(new Refusal({ error: defect })) : Effect.die(defect))),
711
+ };
712
+ if (!declaration.annotations.readOnlyHint)
713
+ return write(connection, { ...spec, destructive: declaration.annotations.destructiveHint });
714
+ return read(connection, {
715
+ ...spec,
716
+ input: Schema.Struct({
717
+ ...declaration.input.fields,
718
+ fields: Schema.optionalKey(fieldsInput("Dot paths to keep; truncation and content continuation metadata remain available.")),
719
+ }),
720
+ run: (args) => Effect.gen(function* () {
721
+ const { fields, ...rest } = args;
722
+ const problem = fieldsProblem(fields, new Set(Object.keys(declaration.output.fields)), "the Sheets result");
723
+ if (problem)
724
+ return yield* Effect.fail(new Refusal({ error: problem }));
725
+ const result = yield* spec.run(rest);
726
+ return {
727
+ ...selectFields(result, fields),
728
+ ...(result["truncated"] ? { truncated: result["truncated"] } : {}),
729
+ ...(declaration.name === "get_values" ? { page: result["page"] } : {}),
730
+ };
731
+ }),
732
+ });
733
+ });
734
+ }