@zackbart/connecta 0.15.1 → 0.16.1

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 (335) hide show
  1. package/AGENTS.md +40 -20
  2. package/CHANGELOG.md +653 -0
  3. package/README.md +25 -5
  4. package/bin/connecta.mjs +44 -3
  5. package/dist/access-tokens.d.ts +0 -1
  6. package/dist/access-tokens.js +0 -1
  7. package/dist/activity.d.ts +50 -2
  8. package/dist/activity.js +34 -1
  9. package/dist/apps-shell.d.ts +0 -1
  10. package/dist/apps-shell.js +0 -1
  11. package/dist/auth/bearer.d.ts +0 -1
  12. package/dist/auth/bearer.js +0 -1
  13. package/dist/auth/clerk.d.ts +0 -1
  14. package/dist/auth/clerk.js +45 -9
  15. package/dist/auth/downstream-oauth.d.ts +0 -1
  16. package/dist/auth/downstream-oauth.js +0 -1
  17. package/dist/call-admission.d.ts +0 -1
  18. package/dist/call-admission.js +0 -1
  19. package/dist/catalog-drift.d.ts +99 -0
  20. package/dist/catalog-drift.js +251 -0
  21. package/dist/catalog-fingerprint.d.ts +0 -1
  22. package/dist/catalog-fingerprint.js +0 -1
  23. package/dist/catalog-limits.d.ts +0 -1
  24. package/dist/catalog-limits.js +0 -1
  25. package/dist/catalog-service.d.ts +0 -1
  26. package/dist/catalog-service.js +41 -5
  27. package/dist/catalog.d.ts +11 -1
  28. package/dist/catalog.js +20 -1
  29. package/dist/concurrency.d.ts +0 -1
  30. package/dist/concurrency.js +0 -1
  31. package/dist/connector-scope.d.ts +0 -1
  32. package/dist/connector-scope.js +0 -1
  33. package/dist/connectors/api.d.ts +29 -21
  34. package/dist/connectors/api.js +45 -19
  35. package/dist/connectors/guarded-fetch.d.ts +106 -0
  36. package/dist/connectors/guarded-fetch.js +289 -0
  37. package/dist/connectors/remote-mcp.d.ts +0 -1
  38. package/dist/connectors/remote-mcp.js +0 -1
  39. package/dist/credentials.d.ts +0 -1
  40. package/dist/credentials.js +0 -1
  41. package/dist/errors.d.ts +30 -3
  42. package/dist/errors.js +1 -1
  43. package/dist/execute.d.ts +0 -1
  44. package/dist/execute.js +0 -1
  45. package/dist/executor-admission.d.ts +11 -1
  46. package/dist/executor-admission.js +28 -1
  47. package/dist/executor-result.d.ts +0 -1
  48. package/dist/executor-result.js +0 -1
  49. package/dist/executors/quickjs-child.d.ts +0 -1
  50. package/dist/executors/quickjs-child.js +0 -1
  51. package/dist/executors/quickjs-protocol.d.ts +0 -1
  52. package/dist/executors/quickjs-protocol.js +0 -1
  53. package/dist/executors/quickjs-runtime.d.ts +0 -1
  54. package/dist/executors/quickjs-runtime.js +0 -1
  55. package/dist/executors/quickjs.d.ts +0 -1
  56. package/dist/executors/quickjs.js +2 -1
  57. package/dist/favicon.d.ts +0 -1
  58. package/dist/favicon.js +0 -1
  59. package/dist/index.d.ts +2 -3
  60. package/dist/index.js +24 -7
  61. package/dist/invocation.d.ts +0 -1
  62. package/dist/invocation.js +0 -1
  63. package/dist/json-schema.d.ts +0 -1
  64. package/dist/json-schema.js +0 -1
  65. package/dist/mcp-result.d.ts +0 -1
  66. package/dist/mcp-result.js +0 -1
  67. package/dist/meta-tools.d.ts +0 -1
  68. package/dist/meta-tools.js +0 -1
  69. package/dist/node.d.ts +0 -1
  70. package/dist/node.js +0 -1
  71. package/dist/operator-ui/generated.d.ts +2 -3
  72. package/dist/operator-ui/generated.js +3 -4
  73. package/dist/operator-ui/model.d.ts +10 -1
  74. package/dist/operator-ui/model.js +0 -1
  75. package/dist/operator-ui/view.d.ts +167 -0
  76. package/dist/operator-ui/view.js +270 -0
  77. package/dist/providers/cloudflare.d.ts +0 -1
  78. package/dist/providers/cloudflare.js +261 -299
  79. package/dist/providers/linear.d.ts +26 -5
  80. package/dist/providers/linear.js +32 -61
  81. package/dist/providers/mixpanel.d.ts +35 -4
  82. package/dist/providers/mixpanel.js +53 -79
  83. package/dist/providers/notion.d.ts +0 -1
  84. package/dist/providers/notion.js +87 -42
  85. package/dist/providers/stripe.d.ts +13 -1
  86. package/dist/providers/stripe.js +31 -60
  87. package/dist/registry.d.ts +34 -2
  88. package/dist/registry.js +85 -5
  89. package/dist/retired-toolkits.d.ts +0 -1
  90. package/dist/retired-toolkits.js +0 -1
  91. package/dist/routes/access-tokens.d.ts +0 -1
  92. package/dist/routes/access-tokens.js +0 -1
  93. package/dist/routes/activity.d.ts +0 -1
  94. package/dist/routes/activity.js +0 -1
  95. package/dist/routes/credentials.d.ts +0 -1
  96. package/dist/routes/credentials.js +0 -1
  97. package/dist/routes/mcp.d.ts +0 -1
  98. package/dist/routes/mcp.js +0 -1
  99. package/dist/routes/oauth.d.ts +0 -1
  100. package/dist/routes/oauth.js +0 -1
  101. package/dist/routes/shared.d.ts +2 -1
  102. package/dist/routes/shared.js +0 -1
  103. package/dist/routes/ui.d.ts +0 -1
  104. package/dist/routes/ui.js +0 -1
  105. package/dist/server.d.ts +0 -1
  106. package/dist/server.js +11 -1
  107. package/dist/skills.d.ts +0 -1
  108. package/dist/skills.js +0 -1
  109. package/dist/storage/file.d.ts +0 -1
  110. package/dist/storage/file.js +0 -1
  111. package/dist/storage/memory.d.ts +0 -1
  112. package/dist/storage/memory.js +0 -1
  113. package/dist/timeout.d.ts +0 -1
  114. package/dist/timeout.js +0 -1
  115. package/dist/tool-safety.d.ts +0 -1
  116. package/dist/tool-safety.js +0 -1
  117. package/dist/types.d.ts +45 -1
  118. package/dist/types.js +0 -1
  119. package/dist/ui.d.ts +0 -1
  120. package/dist/ui.js +13 -137
  121. package/dist/validate.d.ts +13 -15
  122. package/dist/validate.js +24 -11
  123. package/dist/version.d.ts +1 -2
  124. package/dist/version.js +1 -2
  125. package/documentation/architecture.md +186 -5
  126. package/documentation/auth.md +11 -0
  127. package/documentation/call-admission.md +142 -6
  128. package/documentation/cloudflare.md +92 -10
  129. package/documentation/code-first-exploration.md +2 -2
  130. package/documentation/code-mode.md +14 -14
  131. package/documentation/connector-guides.md +172 -5
  132. package/documentation/connectors.md +89 -0
  133. package/documentation/linear.md +20 -2
  134. package/documentation/mcp-ui-design.md +2 -2
  135. package/documentation/meta-tools.md +7 -1
  136. package/documentation/mixpanel.md +42 -9
  137. package/documentation/notion.md +23 -2
  138. package/documentation/operations.md +325 -5
  139. package/documentation/operator-ui.md +120 -5
  140. package/documentation/provider-audit.md +160 -0
  141. package/documentation/provider-conventions.md +667 -0
  142. package/documentation/request-admission.md +167 -5
  143. package/documentation/rich-output-design.md +1 -2
  144. package/documentation/stripe.md +15 -0
  145. package/documentation/upgrading.md +454 -0
  146. package/ethos.md +14 -9
  147. package/examples/worker/README.md +102 -12
  148. package/examples/worker/src/index.ts +46 -1
  149. package/examples/worker/wrangler.jsonc +14 -1
  150. package/package.json +10 -6
  151. package/templates/node/.dockerignore +10 -0
  152. package/templates/node/.env.example +40 -3
  153. package/templates/node/AGENTS.md +16 -1
  154. package/templates/node/Dockerfile +49 -0
  155. package/templates/node/README.md +117 -2
  156. package/templates/node/docker-compose.yml +39 -0
  157. package/templates/node/package.json +1 -1
  158. package/templates/node/src/file-activity.ts +182 -0
  159. package/templates/node/src/index.ts +80 -4
  160. package/assets/connecta-clay-hero.png +0 -0
  161. package/dist/access-tokens.d.ts.map +0 -1
  162. package/dist/access-tokens.js.map +0 -1
  163. package/dist/activity.d.ts.map +0 -1
  164. package/dist/activity.js.map +0 -1
  165. package/dist/apps-shell.d.ts.map +0 -1
  166. package/dist/apps-shell.js.map +0 -1
  167. package/dist/auth/bearer.d.ts.map +0 -1
  168. package/dist/auth/bearer.js.map +0 -1
  169. package/dist/auth/clerk.d.ts.map +0 -1
  170. package/dist/auth/clerk.js.map +0 -1
  171. package/dist/auth/downstream-oauth.d.ts.map +0 -1
  172. package/dist/auth/downstream-oauth.js.map +0 -1
  173. package/dist/call-admission.d.ts.map +0 -1
  174. package/dist/call-admission.js.map +0 -1
  175. package/dist/catalog-fingerprint.d.ts.map +0 -1
  176. package/dist/catalog-fingerprint.js.map +0 -1
  177. package/dist/catalog-limits.d.ts.map +0 -1
  178. package/dist/catalog-limits.js.map +0 -1
  179. package/dist/catalog-service.d.ts.map +0 -1
  180. package/dist/catalog-service.js.map +0 -1
  181. package/dist/catalog.d.ts.map +0 -1
  182. package/dist/catalog.js.map +0 -1
  183. package/dist/concurrency.d.ts.map +0 -1
  184. package/dist/concurrency.js.map +0 -1
  185. package/dist/connector-scope.d.ts.map +0 -1
  186. package/dist/connector-scope.js.map +0 -1
  187. package/dist/connectors/api.d.ts.map +0 -1
  188. package/dist/connectors/api.js.map +0 -1
  189. package/dist/connectors/remote-mcp.d.ts.map +0 -1
  190. package/dist/connectors/remote-mcp.js.map +0 -1
  191. package/dist/credentials.d.ts.map +0 -1
  192. package/dist/credentials.js.map +0 -1
  193. package/dist/errors.d.ts.map +0 -1
  194. package/dist/errors.js.map +0 -1
  195. package/dist/execute.d.ts.map +0 -1
  196. package/dist/execute.js.map +0 -1
  197. package/dist/executor-admission.d.ts.map +0 -1
  198. package/dist/executor-admission.js.map +0 -1
  199. package/dist/executor-result.d.ts.map +0 -1
  200. package/dist/executor-result.js.map +0 -1
  201. package/dist/executors/quickjs-child.d.ts.map +0 -1
  202. package/dist/executors/quickjs-child.js.map +0 -1
  203. package/dist/executors/quickjs-protocol.d.ts.map +0 -1
  204. package/dist/executors/quickjs-protocol.js.map +0 -1
  205. package/dist/executors/quickjs-runtime.d.ts.map +0 -1
  206. package/dist/executors/quickjs-runtime.js.map +0 -1
  207. package/dist/executors/quickjs.d.ts.map +0 -1
  208. package/dist/executors/quickjs.js.map +0 -1
  209. package/dist/favicon.d.ts.map +0 -1
  210. package/dist/favicon.js.map +0 -1
  211. package/dist/index.d.ts.map +0 -1
  212. package/dist/index.js.map +0 -1
  213. package/dist/invocation.d.ts.map +0 -1
  214. package/dist/invocation.js.map +0 -1
  215. package/dist/json-schema.d.ts.map +0 -1
  216. package/dist/json-schema.js.map +0 -1
  217. package/dist/mcp-result.d.ts.map +0 -1
  218. package/dist/mcp-result.js.map +0 -1
  219. package/dist/meta-tools.d.ts.map +0 -1
  220. package/dist/meta-tools.js.map +0 -1
  221. package/dist/node.d.ts.map +0 -1
  222. package/dist/node.js.map +0 -1
  223. package/dist/operator-ui/generated.d.ts.map +0 -1
  224. package/dist/operator-ui/generated.js.map +0 -1
  225. package/dist/operator-ui/model.d.ts.map +0 -1
  226. package/dist/operator-ui/model.js.map +0 -1
  227. package/dist/providers/cloudflare.d.ts.map +0 -1
  228. package/dist/providers/cloudflare.js.map +0 -1
  229. package/dist/providers/linear.d.ts.map +0 -1
  230. package/dist/providers/linear.js.map +0 -1
  231. package/dist/providers/mixpanel.d.ts.map +0 -1
  232. package/dist/providers/mixpanel.js.map +0 -1
  233. package/dist/providers/notion.d.ts.map +0 -1
  234. package/dist/providers/notion.js.map +0 -1
  235. package/dist/providers/stripe.d.ts.map +0 -1
  236. package/dist/providers/stripe.js.map +0 -1
  237. package/dist/registry.d.ts.map +0 -1
  238. package/dist/registry.js.map +0 -1
  239. package/dist/retired-toolkits.d.ts.map +0 -1
  240. package/dist/retired-toolkits.js.map +0 -1
  241. package/dist/routes/access-tokens.d.ts.map +0 -1
  242. package/dist/routes/access-tokens.js.map +0 -1
  243. package/dist/routes/activity.d.ts.map +0 -1
  244. package/dist/routes/activity.js.map +0 -1
  245. package/dist/routes/credentials.d.ts.map +0 -1
  246. package/dist/routes/credentials.js.map +0 -1
  247. package/dist/routes/mcp.d.ts.map +0 -1
  248. package/dist/routes/mcp.js.map +0 -1
  249. package/dist/routes/oauth.d.ts.map +0 -1
  250. package/dist/routes/oauth.js.map +0 -1
  251. package/dist/routes/shared.d.ts.map +0 -1
  252. package/dist/routes/shared.js.map +0 -1
  253. package/dist/routes/ui.d.ts.map +0 -1
  254. package/dist/routes/ui.js.map +0 -1
  255. package/dist/server.d.ts.map +0 -1
  256. package/dist/server.js.map +0 -1
  257. package/dist/skills.d.ts.map +0 -1
  258. package/dist/skills.js.map +0 -1
  259. package/dist/storage/file.d.ts.map +0 -1
  260. package/dist/storage/file.js.map +0 -1
  261. package/dist/storage/memory.d.ts.map +0 -1
  262. package/dist/storage/memory.js.map +0 -1
  263. package/dist/timeout.d.ts.map +0 -1
  264. package/dist/timeout.js.map +0 -1
  265. package/dist/tool-safety.d.ts.map +0 -1
  266. package/dist/tool-safety.js.map +0 -1
  267. package/dist/types.d.ts.map +0 -1
  268. package/dist/types.js.map +0 -1
  269. package/dist/ui.d.ts.map +0 -1
  270. package/dist/ui.js.map +0 -1
  271. package/dist/validate.d.ts.map +0 -1
  272. package/dist/validate.js.map +0 -1
  273. package/dist/version.d.ts.map +0 -1
  274. package/dist/version.js.map +0 -1
  275. package/examples/node/README.md +0 -52
  276. package/examples/node/src/index.ts +0 -71
  277. package/src/access-tokens.ts +0 -289
  278. package/src/activity.ts +0 -234
  279. package/src/apps-shell.ts +0 -370
  280. package/src/auth/bearer.ts +0 -61
  281. package/src/auth/clerk.ts +0 -553
  282. package/src/auth/downstream-oauth.ts +0 -575
  283. package/src/call-admission.ts +0 -521
  284. package/src/catalog-fingerprint.ts +0 -52
  285. package/src/catalog-limits.ts +0 -11
  286. package/src/catalog-service.ts +0 -1153
  287. package/src/catalog.ts +0 -790
  288. package/src/concurrency.ts +0 -39
  289. package/src/connector-scope.ts +0 -66
  290. package/src/connectors/api.ts +0 -159
  291. package/src/connectors/remote-mcp.ts +0 -1090
  292. package/src/credentials.ts +0 -428
  293. package/src/errors.ts +0 -364
  294. package/src/execute.ts +0 -1406
  295. package/src/executor-admission.ts +0 -328
  296. package/src/executor-result.ts +0 -107
  297. package/src/executors/quickjs-child.ts +0 -172
  298. package/src/executors/quickjs-protocol.ts +0 -99
  299. package/src/executors/quickjs-runtime.ts +0 -470
  300. package/src/executors/quickjs.ts +0 -776
  301. package/src/favicon.ts +0 -53
  302. package/src/index.ts +0 -666
  303. package/src/invocation.ts +0 -655
  304. package/src/json-schema.ts +0 -11
  305. package/src/mcp-result.ts +0 -43
  306. package/src/meta-tools.ts +0 -1607
  307. package/src/node.ts +0 -206
  308. package/src/operator-ui/browser.css +0 -423
  309. package/src/operator-ui/browser.ts +0 -1281
  310. package/src/operator-ui/generated.ts +0 -4
  311. package/src/operator-ui/model.ts +0 -111
  312. package/src/providers/cloudflare.ts +0 -3999
  313. package/src/providers/linear.ts +0 -302
  314. package/src/providers/mixpanel.ts +0 -229
  315. package/src/providers/notion.ts +0 -1879
  316. package/src/providers/stripe.ts +0 -307
  317. package/src/registry.ts +0 -1085
  318. package/src/retired-toolkits.ts +0 -14
  319. package/src/routes/access-tokens.ts +0 -115
  320. package/src/routes/activity.ts +0 -215
  321. package/src/routes/credentials.ts +0 -254
  322. package/src/routes/mcp.ts +0 -429
  323. package/src/routes/oauth.ts +0 -322
  324. package/src/routes/shared.ts +0 -247
  325. package/src/routes/ui.ts +0 -152
  326. package/src/server.ts +0 -195
  327. package/src/skills.ts +0 -271
  328. package/src/storage/file.ts +0 -123
  329. package/src/storage/memory.ts +0 -44
  330. package/src/timeout.ts +0 -84
  331. package/src/tool-safety.ts +0 -15
  332. package/src/types.ts +0 -523
  333. package/src/ui.ts +0 -744
  334. package/src/validate.ts +0 -387
  335. package/src/version.ts +0 -7
package/CHANGELOG.md CHANGED
@@ -2,6 +2,659 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## 0.16.1 — 2026-08-13
6
+
7
+ This is the cleanup that follows 0.16.0 out the door: the packaging housekeeping
8
+ the pre-release smoke gauntlet turned up, one provider tool Cloudflare
9
+ deprecated out from under us, a discovery answer that told a plain lie, and the
10
+ upgrade runbook an existing deployment never had. Two things break, both on
11
+ Cloudflare and both named here rather than left to the section below:
12
+ `list_zone_settings` is gone from the `cloudflare()` named surface, and
13
+ Cloudflare's 404 arrives as `not_found` instead of `connector_call_failed`.
14
+ Nothing else does — no wire shape changes, no construction contract moves, no
15
+ other code reclassified, and the per-setting operations `list_zone_settings`
16
+ sat beside are the supported ones and are untouched. A deployment that writes
17
+ no `api()` connectors, branches on no error code, and never asked an agent for
18
+ a whole zone's settings in one call upgrades without reading further.
19
+
20
+ Three things are additions rather than repairs, and they are the reason this
21
+ release is worth reading rather than just installing: a new
22
+ `ConnectorCallErrorCode` member, `not_found`, with a rule for when a connector
23
+ may mint it; a `"./package.json"` entry in the `exports` map, so the installed
24
+ manifest resolves; and `@cloudflare/codemode` declared as an optional peer at
25
+ `^0.4.4 || ^0.5.0`. Strict semver would read those three as a minor, and would
26
+ read the two Cloudflare changes above as more than that. This ships as a patch
27
+ deliberately: every addition is opt-in at the point a deployment chooses to
28
+ read it, and the tool removal and the 404 reclassification ride along in the
29
+ same patch on purpose — both are scoped to one provider, both have a stated
30
+ replacement, and both carry a version boundary in
31
+ [`documentation/upgrading.md`](./documentation/upgrading.md). Holding them for
32
+ a minor would mean shipping a release that keeps calling an endpoint its
33
+ provider deprecated. The one install-time consequence is spelled out next.
34
+
35
+ One thing to check before upgrading a Worker: `@cloudflare/codemode` is now a
36
+ declared peer, so if your `package.json` holds it at a version outside
37
+ `^0.4.4 || ^0.5.0` — a `0.3.x`, or a `0.4` below `0.4.4` — npm stops the
38
+ upgrade with an `ERESOLVE` conflict rather than installing. Move it into the
39
+ range this release is tested against, or pass `--legacy-peer-deps` if you have
40
+ a reason to run outside it. A version already inside the range, and a range
41
+ loose enough for npm to pick one that is, both resolve exactly as before.
42
+
43
+ Alongside it, the upgrade path an existing deployment takes gets written down.
44
+ `connecta init` was the golden path for a new deployment and the whole story
45
+ for an old one, which is a gap with a shape: `init` refuses to merge into an
46
+ existing path — the guard that keeps an initializer from eating a connector
47
+ set — so an agent pointed at a deployment two releases behind had to
48
+ reconstruct the procedure from release prose written for the maintainer. Both
49
+ interesting failures there were silent too. It overwrites the configuration the
50
+ deployment exists for, or it "fixes" a construction throw by weakening a
51
+ fail-closed default and ships something quieter and wrong.
52
+
53
+ ### Added
54
+
55
+ - **`not_found`, for a downstream that answered and had nothing to give.** A
56
+ hand-written connector meeting a 404 had exactly one honest code,
57
+ `connector_call_failed`, which also means "the call blew up" — so a program
58
+ inside `execute_code` could not tell a clean absence from a broken connector,
59
+ and a loop over ids had to abort where it should have skipped one. The new
60
+ code earns its place the way every code has to: it changes what the caller
61
+ does next. You do not wait, you do not go to `authorize_connector`, you do
62
+ not repair the arguments — you re-address. It is non-retryable, carries no
63
+ recovery envelope, derives no activity friction class, and is exported from
64
+ the root entry as part of `ConnectorCallErrorCode`.
65
+
66
+ The qualifier is the interesting half, and it is now written down in
67
+ [H11](./documentation/provider-conventions.md#h11--errors-are-mapped-to-what-the-caller-does-next):
68
+ map a status to `not_found` only where the provider tells absence apart from
69
+ a permission gap. Cloudflare does — a token that may not touch a resource is
70
+ refused with 401 or 403 — so its 404 is now `not_found` instead of
71
+ `connector_call_failed`. Notion does not: `object_not_found` means both "it
72
+ is gone" and "it was never shared with this integration", so it deliberately
73
+ stays generic with a message that says so. The hosted-MCP proxy path mints
74
+ the code never, because `P1` forbids re-shaping downstream framing and
75
+ provider prose is never parsed to invent a classification (#373).
76
+
77
+ - **An upgrade runbook for existing deployments.**
78
+ [`documentation/upgrading.md`](./documentation/upgrading.md) is written for
79
+ the agent sitting inside a generated deployment it did not create: read the
80
+ exact pin and the template generation it implies, regenerate that generation
81
+ with `npx @zackbart/connecta@<pin> init` to get a real merge base, three-way
82
+ reconcile the scaffolding against the current template while `src/index.ts`
83
+ stays the deployment's own, cross the version boundaries that break
84
+ construction, and finish where `init` finishes — typecheck, start,
85
+ `connecta doctor`, then a program that exercises the deployment's *own*
86
+ connectors, which doctor deliberately knows nothing about. The migration
87
+ notes are per boundary and derived from this file: the 0.16.0 `api()`
88
+ construction contract (with the one safe answer for an unannotated tool
89
+ written down — `readOnlyHint: false`, which is the routing it already had),
90
+ the `linear()`, `mixpanel()`, and Cloudflare provider changes, redirect
91
+ refusal and the response ceilings, and the fail-closed shipped defaults; then
92
+ 0.14's annotation-precedence change, 0.13's rewritten guide summaries, the
93
+ 0.11.0 executor requirement, 0.7.0's `verifyState` requirement and
94
+ core-owned routes for the pre-template deployments that still have to cross
95
+ them, and every removed option that throws with its migration. It closes with five refusals, because each is somebody's plausible
96
+ shortcut: no re-init over the top, no weakening a fail-closed default to get
97
+ green, no pinning back, no vendored internals, no second project shape.
98
+ Reachable from the README, from `operations.md`, and — absolutely, because
99
+ that reader has no copy of this repository — from the template's `AGENTS.md`
100
+ (#380).
101
+ - **A suite that keeps the guide honest.** `test/upgrade-guide.test.ts` pins
102
+ every claim its reader cannot check: the generated file inventory against
103
+ `templates/node/`, the seven tool names against the CLI's own list, each
104
+ named version boundary against a release that shipped, each removed option
105
+ against the release section that names its issue, the bump target against
106
+ this package's version, and the three places the guide is linked from. A template that gains a file now fails `npm run check` rather than
107
+ leaving an agent to guess which of the two is wrong (#380).
108
+ - **`@cloudflare/codemode` is a declared optional peer.** Every Workers
109
+ deployment installs the executor behind `execute_code` by hand, and until now
110
+ the only version range anywhere was a devDependency no consumer can read — a
111
+ fresh install resolved a minor ahead of what this repository tests, silently.
112
+ The manifest now publishes `^0.4.4 || ^0.5.0` for it, optional like
113
+ `@clerk/backend` and `quickjs-emscripten`, so a supported version installs in
114
+ silence and an unsupported one stops the install with something to act on
115
+ instead of becoming skew a Worker discovers in production. It still installs
116
+ with nothing: a default `npm install @zackbart/connecta` pulls no executor,
117
+ and the package smoke proves that, both halves of the range behavior, and
118
+ that the version this repository develops against stays inside the range it
119
+ publishes (#376).
120
+
121
+ ### Changed
122
+
123
+ - **Cloudflare's 404 is `not_found`.** A deployment branching on
124
+ `connector_call_failed` to detect an unknown zone or account id should read
125
+ `not_found` instead; retryability, the message, and its pointer to
126
+ `list_zones` / `list_accounts` are unchanged (#373).
127
+ - `cloudflare()` no longer names a bulk zone-settings read.
128
+ `GET /zones/{zoneId}/settings` and its `PATCH` sibling are published as
129
+ `deprecated: true`, Cloudflare offers no bulk replacement, and the tool that
130
+ wrapped the read projected nothing — it took a zone id and grew the payload
131
+ by wrapping an unpaginated array in a page object. Read one setting with
132
+ `get_zone_setting` and write one with `update_zone_setting`, both on the
133
+ supported `/zones/{zoneId}/settings/{settingId}` operations. An operator who
134
+ still wants the whole set can name the deprecated path explicitly through
135
+ `cloudflare_api_get`. The named surface is 47 tools plus the three escape
136
+ hatches ([#361](https://github.com/zackbart/connecta/issues/361)).
137
+ - The Cloudflare touched-endpoint manifest drops the deprecated row with the
138
+ tool, so `npm run drift:check -- --specs` is quiet about zone settings
139
+ because nothing calls the endpoint, not because a maintainer signed off on
140
+ calling it anyway.
141
+
142
+ ### Fixed
143
+
144
+ - **A search for a connector's own name stops claiming the deployment has no
145
+ such capability.** A connector's `id` — the address prefix an agent already
146
+ holds — and its `title` are displayed, never indexed, so `search_tools({
147
+ query: "inventory" })` against a connector called `inventory` matched no tool
148
+ and was answered with "No matching capability is configured in this
149
+ deployment", which was plainly false. Connector identity stays out of the
150
+ lexical index, because putting it in would move ranking for every query that
151
+ already matches tools; instead an unscoped miss whose terms name configured
152
+ connectors says so, names up to three of them by ID, and sends the caller to
153
+ a scoped browse. A term that matches nothing in the deployment still gets the
154
+ original sentence, unchanged. One `queryAnalysis.guidance` string differs; no
155
+ ranking, result, or field changed (#372).
156
+ - **Every relative link in the shipped Markdown resolves for the reader who
157
+ installed the package.** Ten of them pointed at `eval/`, `test/`, `scripts/`,
158
+ and the README hero — repository paths the tarball has never carried and, per
159
+ #346, should not start carrying. The link gate could not see any of them: it
160
+ read only `documentation/` targets, so the whole class was invisible and grew
161
+ with every trim. The policy is now stated once in the operations guide and
162
+ enforced over *every* relative link in packed Markdown: it either resolves
163
+ inside the tarball or it is cited as an absolute
164
+ `https://github.com/zackbart/connecta/blob/main/...` URL, which an outside
165
+ reader can follow and which `check:docs` resolves back to the checkout, so a
166
+ citation still fails when the file it names moves. The ten links were
167
+ rewritten that way, the README hero now loads from
168
+ `raw.githubusercontent.com` and still renders on npmjs.com, and the
169
+ repository reader loses no citation (#378).
170
+ - **`@zackbart/connecta/package.json` resolves.** The `exports` map listed
171
+ every code subpath and nothing else, so a bundler plugin, framework build
172
+ step, or version probe reaching for the installed manifest — a thing the
173
+ ecosystem broadly expects to work — got `ERR_PACKAGE_PATH_NOT_EXPORTED`
174
+ instead of the file. The manifest is now exported. It is a data file, so this
175
+ widens the published surface by exactly zero code paths: the root entry's
176
+ Workers purity boundary and the optional-peer subpaths are untouched. The
177
+ package-surface gate now asserts the whole subpath set, manifest included, so
178
+ neither this entry nor an unwanted one can arrive unnoticed (#374).
179
+ - **The published-surface rule says what it actually forbids.** `AGENTS.md`
180
+ claimed platform-specific storage adapters live in `examples/`, "not the
181
+ package", while the tarball has always carried `examples/worker` — Cloudflare
182
+ KV and D1 adapters included — because that example is the Workers starting
183
+ template a consumer copies. The invariant was never in danger: nothing under
184
+ `examples/` appears in the `exports` map, so those adapters are reference
185
+ source and not an importable subpath. The wording now draws the line where
186
+ the gates draw it — a platform-bound adapter must not reach `src/` or the
187
+ `exports` map — and says why the example ships, in `AGENTS.md`, the
188
+ operations guide, and the `scripts/check-package.mjs` comment. A new
189
+ assertion in `test/package-surface.test.ts` holds the instruction file and
190
+ the exports map to the same story (#377).
191
+
192
+ ## 0.16.0 — 2026-08-12
193
+
194
+ This is the agent-efficiency refocus. One release, sixteen merges, and a single
195
+ question asked of every tool description, schema, discovery result, and error
196
+ message in the package: what does this cost the model that has to read it?
197
+ Where operator convenience and agent cost disagreed, the agent-facing contract
198
+ won. The work lands on two pillars — excellent curated providers, and a
199
+ footgun-free path for everything else — plus an operator boundary that finally
200
+ describes the surface it guards, exactly two deployment shapes instead of four,
201
+ and an operator UI that is a component app rather than string-built HTML.
202
+
203
+ What breaks, breaks loudly, and mostly at construction rather than at 2 a.m.
204
+ `api()` now requires a `description` and an explicit
205
+ `annotations.readOnlyHint` on every tool and refuses an `inputSchema` it cannot
206
+ compile; `strictValidation` is gone because fail-closed is the only behavior
207
+ left for it to switch. `linear()` requires an explicit `access` mode.
208
+ `mixpanel()` no longer declares a call-admission budget. `cloudflare()` checks
209
+ an overridden `baseUrl` where it is written. The Cloudflare connection ships 52
210
+ named tools instead of 55, having been measured against its own escape hatches
211
+ rather than assumed to beat them. And Cloudflare and Notion now refuse a
212
+ redirect and cap the response they will read, which is visible only to a
213
+ deployment that was downloading something enormous through a tool call. Each
214
+ of those has a one-line migration, spelled out below.
215
+
216
+ A deployment that writes no `api()` connectors and runs none of the five
217
+ prebuilt connections can upgrade without editing anything. Nothing in the core
218
+ runtime surface moved: the seven meta-tools, the executor contract, the storage
219
+ interfaces, the route table, and the wire shapes are where they were. The
220
+ tarball is half the size, the guides are all written, and `connecta init` now
221
+ produces a project that runs under `docker compose up` without becoming a
222
+ second project shape.
223
+
224
+ `api()` stops being forgiving. A hand-written tool now declares what it does
225
+ and whether calling it needs a human, and any `inputSchema` it ships is one
226
+ Connecta can actually enforce — all three checked at construction, where a
227
+ deployment can still refuse to boot, rather than discovered by an agent at
228
+ 2 a.m. The warn-once-then-pass-raw-arguments-through behavior behind an
229
+ unenforceable schema is gone, and with it the `strictValidation` option that
230
+ existed only to turn it off.
231
+
232
+ That construction contract breaks `api()` authors and nobody else. Migration is
233
+ mechanical: give every tool a non-empty `description` and an explicit
234
+ `annotations.readOnlyHint` — `true` for a read, `false` for work that should
235
+ cross `call_destructive_tool` — then delete `strictValidation`, which is now
236
+ the only behavior. A tool that used to ship unannotated becomes
237
+ `readOnlyHint: false`, which is exactly the routing it already got.
238
+ Hosted-MCP proxies are untouched: `remoteMcp()` relays a downstream's names,
239
+ descriptions, schemas, and annotations as they arrive, and an unannotated or
240
+ contradictory downstream tool still fails closed onto `call_destructive_tool`.
241
+ Connecta infers read-only behavior from nothing, anywhere.
242
+
243
+ The maintained Cloudflare connection ships the second break. Its named surface
244
+ was measured against its own escape hatches instead of being assumed to beat
245
+ them, and three tools came out. Every named tool now carries a recorded `keep`,
246
+ `prune`, or `improve` verdict backed by per-tool numbers: catalog tokens, rank
247
+ in a real `search_tools` call for a representative operator request, whether
248
+ classes of argument mistake are refused before the round trip, and whether the
249
+ handler projects Cloudflare's object or hands it back whole. The evidence, the
250
+ tasks, and the reason for every removal — including the one removed for pair
251
+ symmetry rather than for a measured defect — are in
252
+ [`eval/current-version/results/issue-350-evidence.md`](./eval/current-version/results/issue-350-evidence.md).
253
+ **A deployment that calls `set_r2_cors`, `delete_r2_cors`, or `get_r2_metrics`
254
+ has to change.** No capability is lost: `get_r2_cors` still reads a bucket's
255
+ policy, and the usage guide now names the replacement routes —
256
+ `cloudflare_api_mutate` at
257
+ `PUT`/`DELETE /accounts/{accountId}/r2/buckets/{bucketName}/cors`, and
258
+ `cloudflare_api_get` at `/accounts/{accountId}/r2/metrics`. Every other
259
+ Cloudflare tool, argument, projection, and annotation is unchanged.
260
+
261
+ All five maintained prebuilt connections have been audited against the written
262
+ provider conventions, one report per provider, with a verdict for every
263
+ applicable convention. Nineteen misses were found and fixed. Sixteen of them
264
+ were a guide, a title, or a schema description failing to say something the
265
+ implementation already did correctly — the conventions were mostly not asking
266
+ for different behavior, they were asking for the behavior to reach the agent.
267
+
268
+ Three of them did change behavior, and two of those break a deployment:
269
+ `linear()` now requires an `access` mode, and `mixpanel()` no longer declares a
270
+ call-admission budget. Both failures are loud — one at construction, one as an
271
+ absent ceiling an operator can restore in one option. Everything else is a
272
+ smaller catalog, a better summary, and a guide that says what it always meant.
273
+
274
+ Underneath all of that, the two hand-written providers stopped each keeping
275
+ their own copy of the same transport safety machinery. Cloudflare and Notion
276
+ now send every request through one guarded transport that owns URL
277
+ confinement, redirect refusal, bounded response reads, and network-failure
278
+ normalization, and owns no opinion at all about what a status code means.
279
+
280
+ Consolidating it was not free, and three of the differences are visible from
281
+ outside. A 3xx from either provider is refused now rather than followed, which
282
+ is what both used to do by default — a redirect is an instruction to re-send
283
+ the connector's credential to whatever origin `Location` names, and neither
284
+ API has a legitimate one to send. Both providers now cap what they will read,
285
+ at 8 MiB for Cloudflare and 4 MiB for Notion, so a `cloudflare_api_get`
286
+ downloading an R2 object or a Worker script larger than 8 MiB fails instead of
287
+ returning it. And `cloudflare()`'s optional `baseUrl` is checked where it is
288
+ written: a non-loopback plain-http origin, URL-embedded credentials, or a
289
+ query or fragment throws at construction, so a deployment pointed at an http
290
+ proxy stops booting rather than sending it a token.
291
+
292
+ The repository now models exactly the two deployments it actually has: a Node
293
+ one and a Worker one. `connecta init` still copies the same template, but that
294
+ template now carries its own `Dockerfile` and `docker-compose.yml`, so the
295
+ generated project runs from `npm start` locally and from `docker compose up`
296
+ in production without becoming a second project shape. The two near-identical
297
+ Node scaffolds that sat beside it — `examples/node` and `examples/docker`, the
298
+ latter of which built the Connecta repository rather than a consumer project —
299
+ are gone. Existing deployments can ignore all of this; nothing in the package's
300
+ runtime surface moved.
301
+
302
+ The three maintained hosted-MCP connections now notice when the catalog they
303
+ were reviewed against moves. Each ships a vetted manifest — the tool names and
304
+ classifications a release read, plus schema digests once a release records
305
+ them — and compares it with the live listing *inside* a catalog refresh the
306
+ deployment already asked for. Nothing new is requested: no scheduled job, no
307
+ background poll, no credential probe, which is the boundary that keeps this
308
+ from being the proactive liveness checking connecta removed. What comes out is
309
+ four counts — unclassified additions, names no longer served, explicit
310
+ annotation conflicts, schema changes — on connector status, on `/health`, and
311
+ in `connecta doctor`, plus one payload-free activity event per change in those
312
+ counts for stores that implement the new optional `recordCatalogDrift`. The
313
+ observation is per runtime and is not persisted, so status, `/health`, and
314
+ doctor answer for the isolate or process that served the refresh — an empty
315
+ report means that runtime has observed nothing, and the activity event is the
316
+ durable half. A deployment can ignore all of it: an unclassified tool already
317
+ failed closed onto `call_destructive_tool` before anyone counted it.
318
+
319
+ The other half of that story is a command, not a surface. `npm run drift:check`
320
+ is maintainer tooling — it ships nowhere, runs on a laptop before a release, and
321
+ answers the question the runtime counts deliberately cannot: *which* tool moved.
322
+ It diffs each hosted-MCP catalog against the same vetted manifest the connector
323
+ classifies from, using the maintainer's own credential, and it compares the
324
+ handful of endpoints Cloudflare and Notion actually call against those
325
+ providers' published OpenAPI documents — reporting a gone path, a gone method, a
326
+ new deprecation, or a changed contract, and ignoring the two thousand operations
327
+ connecta never touches. No credential goes near CI, nothing is scheduled,
328
+ nothing files itself, and a published specification is drift evidence only: it
329
+ never generates a tool and never becomes a runtime input.
330
+
331
+ Both deployment shapes now carry the operator feature set the operator pages
332
+ were built for. A fresh `connecta init` used to produce a deployment with a
333
+ Credentials page and no vault, a Tokens page and no issuance, and an Activity
334
+ page with nothing behind it — pages for things that deployment could not do.
335
+ The Node template now ships sign-in, vault, tokens, and activity as four
336
+ clearly-marked commented blocks in `src/index.ts`, each one an environment
337
+ variable and an uncommented block away, plus a deployment-owned
338
+ `src/file-activity.ts` that is compiled rather than commented. The Worker
339
+ example wires the first three outright and comments the fourth, which needs a
340
+ D1 database nobody can create for you. Existing deployments can ignore this
341
+ entirely: nothing in the package's runtime surface moved, and both READMEs
342
+ walk through the enablement.
343
+
344
+ Finally, the tarball is half of what it was, and nothing that left it was
345
+ reachable. `exports` resolves only into `dist/`, so the packed `src/` was
346
+ never imported by anything — it was there to back the source and declaration
347
+ maps, and all three went together. Out with them, and out with the 230 KB
348
+ README hero image, which npmjs.com renders from the repository anyway. An
349
+ install unpacks to 1.8 MB instead of 3.8 MB. The code, the types, the CLI, the
350
+ template, the Worker example, and every guide are exactly where they were —
351
+ and there are four more guides than there were mid-release, because the four
352
+ stubs `check:package` had been excluding got written instead.
353
+
354
+ Which is the quiet half of this release. The five prebuilt connections used to
355
+ encode five sets of private judgment about what a good provider surface looks
356
+ like; that judgment is now two written convention sets, H1–H14 for hand-written
357
+ `api()` surfaces and P1–P13 for `remoteMcp()` proxies, each rule carrying its
358
+ reason and the agent cost it reduces. The placeholder guides in
359
+ `documentation/` — which covered, with some irony, the load-bearing subsystems,
360
+ while the newest features had the best docs — are written against the code as
361
+ it is, and none is left. And the ethos bullet that promised
362
+ "observable, never administrable" was retired for one that is true: operator
363
+ routes manage authentication material for capabilities declared in deployment
364
+ configuration, and a suite now snapshots every declared structure and demands
365
+ it back byte-identical after each operator mutation.
366
+
367
+ ### Added
368
+
369
+ - **Two written provider convention sets.**
370
+ [`documentation/provider-conventions.md`](./documentation/provider-conventions.md)
371
+ states H1–H14 for hand-written `api()` surfaces, where Connecta owns every
372
+ name, schema, projection, and error, and P1–P13 for `remoteMcp()` proxies,
373
+ where the downstream owns the catalog and Connecta owns the endpoint,
374
+ credential, classification, guide, and budget. Every convention carries its
375
+ rule, its reason, and which of the four agent costs it reduces — discovery
376
+ tokens, wrong-tool selection, argument retries, result size — and names the
377
+ budgets at which the surface itself starts dropping characters: 160 for a
378
+ tool description in search, 240 in describe, 1,024 bytes per compact schema,
379
+ 120 for a guide summary. A description longer than its budget is written for
380
+ nobody (#339).
381
+ - **The core subsystem guides, written.** `architecture.md`,
382
+ `request-admission.md`, `call-admission.md`, and `operations.md` were
383
+ identical seven-line placeholders pointing at git history; they now describe
384
+ the code as it is — the two lifetimes, the ordered route table and why each position is
385
+ behavior rather than taste, the import-graph purity rule and what it actually
386
+ prevents, both admission pools and why `/mcp` admits before it authenticates,
387
+ and the connector-partitioned downstream policy. `operations.md` also carries
388
+ the test map AGENTS.md had been deferring since the docs restructure: all 61
389
+ suites plus the two browser specs, with each Node-only suite's reason for not
390
+ running in workerd, so "this suite exists" and "this suite is justified" are
391
+ one lookup. `connector-guides.md` gains the general authoring half #339 left
392
+ open, including the `required` flag in full. With no stubs left, the four
393
+ `!documentation/…` negations in `files` went too (#348).
394
+ - **A maintainer-run provider drift check.** `npm run drift:check` diffs the
395
+ live Linear, Stripe, and Mixpanel catalogs against their vetted manifests by
396
+ name — added, no longer served, annotation conflicts, and schema changes — and
397
+ cross-checks its totals against the runtime `detectCatalogDrift()`, because
398
+ two readings of one manifest that disagree mean one of them is lying. Its
399
+ second half compares committed touched-endpoint manifests
400
+ (`scripts/drift/cloudflare-endpoints.json`, `scripts/drift/notion-endpoints.json`:
401
+ method, path, reviewed spec revision, reviewed deprecation, contract digest)
402
+ with each provider's published OpenAPI document, and `--record` refreshes
403
+ them. Deprecation is reported as a transition in either direction, so a
404
+ reviewed one stops being news. A missing credential, an unreachable
405
+ specification, or a `--provider` the selected half does not check stops the
406
+ run and says which one.
407
+ Written up in
408
+ [`documentation/provider-conventions.md`](./documentation/provider-conventions.md#the-maintainer-run-drift-check)
409
+ (#351).
410
+ - **Hosted-provider drift detection at refresh.** Linear, Stripe, and Mixpanel
411
+ each ship a vetted manifest and compare it with the live catalog while
412
+ serving a refresh that was going to happen anyway. `ConnectorStatus` gains
413
+ `catalogDrift` (four counts and the time they were observed), `/health` gains
414
+ the same per connector, `connecta doctor` reports it without failing on it,
415
+ and `ActivitySink` gains an optional `recordCatalogDrift` that receives one
416
+ payload-free event per change in the counts. Both read surfaces report what
417
+ the answering runtime observed — the observation is isolate-local, not
418
+ persisted — and both project the counts rather than echo the connector seam.
419
+ The policy is written up in
420
+ [`documentation/provider-conventions.md`](./documentation/provider-conventions.md#the-runtime-drift-policy)
421
+ (#343).
422
+ - **The Node template is Docker-ready.** `Dockerfile`, `docker-compose.yml`,
423
+ and `.dockerignore` ship with `connecta init`. The image installs
424
+ `@zackbart/connecta` from the registry like any other consumer, runs as the
425
+ non-root `node` user with state on a named volume, probes the always-open
426
+ `/health` route, and keeps Node in the foreground so `compose down` stops it
427
+ promptly. `PUBLIC_URL` and `CONNECTA_STATE_FILE` now configure the generated
428
+ `src/index.ts`, which is what makes one source serve both run paths (#344).
429
+ - **The package smoke exercises the generated container.** `check:package`
430
+ builds and runs the initialized deployment through Compose and points
431
+ `connecta doctor` at it; it fails rather than skips when Docker is missing
432
+ in CI (#344).
433
+ - **A deterministic named-surface measurement lane.**
434
+ `npm --prefix eval/current-version run report:cloudflare-surface` measures the
435
+ maintained Cloudflare connection one tool at a time and writes a JSON and
436
+ Markdown artifact. It needs no model, no network, and no credential: the real
437
+ constructor, schemas, validation path, handlers, and catalog service run, and
438
+ only `fetch` is a probe that records the request (#350).
439
+ - **Five provider audit reports** in
440
+ [`documentation/provider-audit.md`](./documentation/provider-audit.md), with
441
+ a verdict per convention, the fix for every miss, and every accepted
442
+ exception recorded with its argument (#342).
443
+ - **A convention test over the shipped surface.**
444
+ `test/provider-conventions.test.ts` walks both `api()` providers on every run
445
+ and enforces the mechanically checkable bar — naming, description budgets,
446
+ closed schemas described at every depth, compact-render budgets, declared
447
+ outputs, structured guides, and credential tests — so a convention met once
448
+ stays met. The one accepted gap, the undescribed name/value members of
449
+ Cloudflare's escape-hatch request parts, is listed by path in the suite with
450
+ its argument rather than left for a shallower check to miss (#342).
451
+ - **Guide coverage the schemas cannot carry.** Stripe and Mixpanel guides now
452
+ name their id-resolution rules, say the hosted catalog is not a fixed set, and
453
+ give the `auth_required` → `authorize_connector` recovery route. Notion's
454
+ guide states that it deliberately has no raw-REST escape hatch (#342).
455
+ - **A guarded fetch transport for hand-written connectors.** One factory
456
+ supplies the machinery every `api()` HTTP surface was re-deriving: strict
457
+ base-origin and path confinement checked after URL normalization, encoded
458
+ query and JSON body construction, `ctx.signal` propagation, a required
459
+ response-byte ceiling enforced while reading, a flat refusal to follow a
460
+ redirect or to let a request header shadow an authentication one, and an
461
+ unreachable provider normalized to a retryable `unavailable`. Authentication
462
+ and status interpretation stay in provider callbacks — the helper never
463
+ guesses what a 403 means. Cloudflare and Notion both run on it; it is held
464
+ internal this release rather than exported, and
465
+ [`documentation/connectors.md`](./documentation/connectors.md#the-guarded-fetch-transport)
466
+ records why (#341).
467
+ - **The operator feature set in both deployment shapes.** The Node template
468
+ gains commented, documented configuration for Clerk operator sign-in, the
469
+ credential vault, access-token issuance, and payload-free activity, the four
470
+ environment variables they read (passed through Compose and defaulted in the
471
+ Dockerfile so the container works the moment a block is uncommented), and
472
+ `src/file-activity.ts` — a deployment-owned `ActivityStore` that appends one
473
+ JSON line per call and rewrites the log back down to the newest 5,000 once it
474
+ runs a slack window past that, repairing a torn trailing line on the way in
475
+ rather than appending onto it. The Worker example wires the credential vault
476
+ to a new `CREDENTIAL_ENCRYPTION_KEY` secret beside the Clerk and access-token
477
+ configuration it already had, and carries the D1 activity wiring and its
478
+ binding as commented lines rather than as README-only instructions. Both
479
+ READMEs walk through enabling each half — including the part neither vault
480
+ can supply, a connector that declares a `credential` slot, which is what puts
481
+ the Credentials page in the nav — and both say why `connecta doctor` reports
482
+ none of it: doctor holds a bearer, and a client key does not get to learn a
483
+ deployment's configuration topology (#345).
484
+
485
+ ### Changed
486
+
487
+ - **The operator boundary is stated as authentication material.** "Observable,
488
+ never administrable" had stopped describing the surface — operator routes
489
+ rotate credentials, issue and revoke access tokens, and drive downstream
490
+ OAuth, each under its own accepted decision. The ethos bullet, the invariant,
491
+ and a new decisions row now say the true thing: operator routes may manage
492
+ authentication material for capabilities *declared* in deployment
493
+ configuration, and may never change the connector set, the declared tool
494
+ catalog or annotations, requested OAuth scopes, admission policy,
495
+ authorization rules, or caller tool scope. The word "declared" is doing work
496
+ twice over — a broader-scoped replacement token widens downstream reach and
497
+ no browser page can honestly promise otherwise, and a remote MCP server's
498
+ catalog is discovered rather than declared, so storing a credential can take
499
+ an `mcp()` connector from no tools to N. That is discovery arriving, which is
500
+ exactly why those routes call `invalidateStored()`.
501
+ `test/operator-boundary.test.ts` snapshots every declared structure, drives
502
+ each operator mutation route against both a static and a re-listing
503
+ connector, and requires the snapshot back byte-identical; a second case
504
+ proves the snapshot can fail. No runtime behavior changed (#338).
505
+ - **The operator UI is a component app.** The hand-written DOM layer is gone,
506
+ replaced by a small Preact app compiled by the same esbuild step and inlined
507
+ into a shell that is now a mount point rather than a page. Nothing builds
508
+ HTML from strings any more, so the escaping every rendered value used to
509
+ depend on is structural; the served markup is identical on all four pages and
510
+ still carries no operator data. Credential, token, and OAuth flows gained
511
+ deliberate loading, error, empty, and success states — a failed save keeps
512
+ the form and what was typed in it, a failed list offers a retry, and an empty
513
+ collection says what would fill it. Each connector card also reads the drift
514
+ the last catalog refresh saw (#343) as four category counts, with `clean`,
515
+ `warning`, and "not observed in this runtime" kept as three distinct answers
516
+ — no tool name, schema, or payload rides that panel. Preact rides in as a
517
+ `devDependency` inlined into the committed bundle and never reaches a
518
+ deployment's dependency tree (#347).
519
+
520
+ - **`api()` enforces its construction contract.** Every tool requires a
521
+ non-empty `description` and an explicit boolean `annotations.readOnlyHint`;
522
+ a missing or non-boolean classification throws with the address that needs
523
+ fixing. The classification is never inferred from a tool name, description,
524
+ schema, HTTP method, or the other annotations (#340).
525
+ - **An unenforceable `inputSchema` fails at construction.** A schema the
526
+ validator cannot compile throws when the connector is built, whether or not
527
+ `validateArgs` is on — opting out of enforcement is not opting out of the
528
+ schema being real. A schema that only reveals itself on first use, such as an
529
+ unresolvable `$ref`, now fails that call as non-retryable `invalid_args`
530
+ instead of forwarding raw arguments to the handler (#340).
531
+ - **`linear()` requires `access`.** There is no safe default between Linear's
532
+ two endpoints: `"read-write"` hands out writes nobody asked for, and
533
+ `"read-only"` breaks a writing deployment at Linear, at runtime, where no
534
+ agent can repair it. Construction now throws naming both options. Add
535
+ `access: "read-write"` to keep an existing deployment's behavior (#342).
536
+ - **`mixpanel()` declares no call-admission budget.** The old hardcoded 600
537
+ calls per hour transcribed a limit Mixpanel meters *per user*, which a
538
+ per-runtime counter cannot approximate in either direction. Supply
539
+ `callAdmission` explicitly if the account needs a ceiling, as Linear already
540
+ did (#342).
541
+ - **`mixpanel()` titles itself by region.** The default title is now
542
+ `Mixpanel (us)`, `(eu)`, or `(in)`, and the guide opens with the residency,
543
+ because a project lives in exactly one and search never shows a description.
544
+ An unknown region throws at construction (#342).
545
+ - **Provider guides are structured everywhere.** All five declare an explicit
546
+ `summary` instead of leaning on the guide's first line, which was truncating
547
+ the routing fact at 120 characters on three of them (#342).
548
+ - **Smaller discovery payloads.** Over-budget tool descriptions
549
+ (`cloudflare_api_get`, `cloudflare_api_mutate`, `create_dns_record`, Notion's
550
+ `search`) were trimmed to the 240-character describe budget, and shared
551
+ property descriptions were cut so `cloudflare_api_upload` and
552
+ `query_data_source` render inside the 1,024-byte compact budget instead of
553
+ degrading and costing a describe round trip (#342).
554
+ - **Cloudflare and Notion never follow a redirect.** Both used `fetch`'s
555
+ default `redirect: "follow"` and now send `redirect: "manual"`; a 3xx fails
556
+ as non-retryable `connector_call_failed` instead of re-sending the
557
+ connector's credential to whatever origin the `Location` names (#341).
558
+ - **Cloudflare and Notion bound the response they will read.** 8 MiB and
559
+ 4 MiB respectively, enforced against a declared `Content-Length` before the
560
+ first byte and again while the body streams. Both are ceilings on absurdity
561
+ rather than quotas — anything near them was already past whatever
562
+ `maxResultBytes` the deployment set — but a `cloudflare_api_get` reading an
563
+ R2 object or Worker script past 8 MiB now fails as non-retryable instead of
564
+ returning it (#341).
565
+ - **`cloudflare()` checks its `baseUrl` at construction.** A non-loopback
566
+ plain-http origin, URL-embedded credentials, or a query or fragment throws
567
+ where the connector is written rather than on the first call. A deployment
568
+ overriding `baseUrl` with an http proxy must move it to https or bind it to
569
+ loopback; the default Cloudflare base is unaffected (#341).
570
+
571
+ ### Fixed
572
+
573
+ - **`connecta doctor` names the sandbox that actually ran the program.** It
574
+ printed "QuickJS executed" at every deployment it had ever checked, including
575
+ the Worker example, whose sandbox is a Dynamic Worker — the one field the
576
+ Worker README says doctor confirms, reported wrong. The deployment now says
577
+ what its executor is: `/health` carries the configured executor's name when
578
+ it has one (an explicit `name`, else a class-shaped executor's constructor
579
+ name), sanitized and bounded because it lands in a public response body and
580
+ an operator's terminal. Doctor reports that name, and a deployment whose
581
+ executor identifies as nothing gets `code executed` rather than a guess. The
582
+ Node template still reads `QuickJS executed`; the Worker example now reads
583
+ `DynamicWorkerExecutor executed` (#368).
584
+ - **The Node template's `.env.example` ships an empty `CONNECTA_TOKEN`.**
585
+ `docker-compose.yml` has always promised to refuse a deployment with no
586
+ inbound auth, but its `${CONNECTA_TOKEN:?…}` guard only fires on unset or
587
+ empty — and the file it reads shipped `replace-me`, which is neither. Copying
588
+ `.env.example` and running the README's Docker block therefore produced a
589
+ healthy, port-published deployment whose bearer token was a string published
590
+ in this repository. The value is now empty, so both Compose and `npm start`
591
+ refuse until an operator sets one (#367).
592
+ - **The Worker example names the optional peer it imports.**
593
+ `examples/worker` wires `clerkAuth` by default and calls itself the starting
594
+ template for a deployment, but its README listed only `@cloudflare/codemode`
595
+ as an extra install. `@clerk/backend` is an optional peer that never installs
596
+ with Connecta, and `auth/clerk` imports it at the top level, so a copied
597
+ deployment following the README verbatim died at
598
+ `Could not resolve "@clerk/backend"` before it ever reached Cloudflare. The
599
+ deploy section now carries the whole install line for a copy in its own
600
+ repository (#367).
601
+ - **A malformed Clerk publishable key fails like a configuration mistake.**
602
+ `clerkAuth` derives its Frontend API origin by base64-decoding
603
+ `publishableKey` and used to hand a bad key straight to `atob`, so the
604
+ placeholder the Workers example ships raised a bare `InvalidCharacterError`
605
+ from inside the returned object — and, on a deployment that builds per
606
+ request, turned every route including `/health` into a 500 whose stack named
607
+ base64 instead of the environment variable. The key's shape is now checked
608
+ where `allowedDomains` is, at construction, and the throw names the option.
609
+ It does not quote the rejected value back: the usual way to land here is
610
+ pasting the secret key into the publishable slot (#366).
611
+ - **Notion declares the `required` lists it was missing.** `search`,
612
+ `list_users`, `get_self`, and `create_page` now say which arguments a call
613
+ must carry, so a malformed call is refused locally instead of at Notion. The
614
+ fail-closed schema handling those lists rely on is the package default as of
615
+ #340 (#342).
616
+ - **Cloudflare's cursor pagination says so in the schema.**
617
+ `list_zone_rulesets`, `list_kv_keys`, `list_r2_buckets`, and
618
+ `list_r2_objects` now state on both the `cursor` argument and the
619
+ `nextCursor` result that they page by cursor and return no `page` object —
620
+ previously only the usage guide said it (#342).
621
+ - **Nested schema properties describe themselves.** The six fields inside
622
+ `bulk_write_kv_values`'s `entries[]` — including the expiry pair, whose units
623
+ and 60-second floor were the entire question — and Notion's
624
+ `sorts[].direction` were shipping bare types, because H5's description rule
625
+ had only ever been read at the top level (#342).
626
+
627
+ ### Removed
628
+
629
+ - **`ApiOptions.strictValidation`.** Fail-closed schema handling is the only
630
+ behavior, so the opt-in has nothing left to switch. Delete the option;
631
+ nothing else changes (#340).
632
+ - **Three Cloudflare named tools; the connection ships 52, down from 55.**
633
+ `set_r2_cors` declared a free-form rule body, so its schema validated the ids
634
+ and waved through the part of the call that fails, and `get_r2_metrics` put
635
+ one account id into a path and returned the response unprojected — both
636
+ measurably weaker than the raw call that replaces them. `delete_r2_cors`
637
+ measured clean and went anyway, to keep the CORS write pair together: with
638
+ the write unnamed, a named delete would leave half of policy management on
639
+ each route. All three are one raw call away, and the guide says which one
640
+ (#350).
641
+ - **`examples/node` and `examples/docker`.** Both were diffs from the
642
+ template. `examples/` is the Worker deployment now, and the root
643
+ `.dockerignore` that existed only for the repository-context Docker build
644
+ went with them (#344).
645
+ - **`src/`, `.js.map`, `.d.ts.map`, and `assets/` — from the tarball only.**
646
+ All of them are still in the repository; none of them ships. The published
647
+ package is 167 files and 550 KB, down from 357 and 1.1 MB. The stub guides
648
+ left with them and came back written (#348), which is why the file count is
649
+ four higher than the trim alone left it.
650
+ Stepping into Connecta's TypeScript from an installed copy no longer works;
651
+ the emitted JavaScript and the `.d.ts` files beside it do. `check:package`
652
+ now fails on a packed `src/`, `.map`, or `assets/` path, derives the shipped
653
+ guide list from which guides are still stubs, so filling one in ships it, and
654
+ refuses a packed document whose relative link lands on a guide the tarball
655
+ does not carry — the three that pointed at `operations.md` now name it
656
+ instead (#346).
657
+
5
658
  ## 0.15.1 — 2026-08-12
6
659
 
7
660
  The Cloudflare connection now supports legacy user-scoped Global API Keys as