@zackbart/connecta 0.37.1 → 0.39.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 (265) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +5 -4
  3. package/bin/connecta.mjs +1 -39
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +2 -4
  6. package/dist/agent-identity.js +1 -1
  7. package/dist/agent-routes.js +231 -0
  8. package/dist/auth/clerk-sdk/generated.js +2 -1
  9. package/dist/auth/downstream-oauth/consent.js +519 -0
  10. package/dist/auth/downstream-oauth/grant-store.js +439 -0
  11. package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
  12. package/dist/auth/downstream-oauth/refresh-records.js +447 -0
  13. package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
  14. package/dist/auth/downstream-oauth.js +66 -2516
  15. package/dist/auth/downstream-revocation.js +1 -1
  16. package/dist/auth/native.js +27 -0
  17. package/dist/auth/oauth-grant.js +24 -0
  18. package/dist/auth/oauth-token-response.js +1 -1
  19. package/dist/auth/static-oauth.js +153 -131
  20. package/dist/auth/vercel-agent.js +2 -1
  21. package/dist/auth-elicitation.js +9 -3
  22. package/dist/authorization-handoff.js +11 -5
  23. package/dist/branding.d.ts +1 -15
  24. package/dist/branding.js +2 -2
  25. package/dist/bytes.js +42 -0
  26. package/dist/call-admission-context.js +2 -5
  27. package/dist/call-recovery.js +0 -3
  28. package/dist/catalog-cache.js +130 -43
  29. package/dist/catalog-fingerprint.js +2 -18
  30. package/dist/catalog-policy.js +18 -0
  31. package/dist/catalog-service.js +285 -48
  32. package/dist/catalog.js +18 -2
  33. package/dist/config-defaults.d.ts +1 -0
  34. package/dist/config-defaults.js +1 -0
  35. package/dist/config-schema.d.ts +1 -1
  36. package/dist/config-schema.js +6 -2
  37. package/dist/config.d.ts +57 -25
  38. package/dist/config.js +30 -77
  39. package/dist/connector-caller.d.ts +1 -1
  40. package/dist/connector-health.js +59 -0
  41. package/dist/connector-status.js +117 -0
  42. package/dist/connectors/api-connector.d.ts +7 -2
  43. package/dist/connectors/api-connector.js +11 -7
  44. package/dist/connectors/guarded-fetch.d.ts +0 -6
  45. package/dist/connectors/guarded-fetch.js +1 -11
  46. package/dist/connectors/negotiation-cache.js +22 -4
  47. package/dist/connectors/remote-mcp/call.js +164 -0
  48. package/dist/connectors/remote-mcp/catalog.js +278 -0
  49. package/dist/connectors/remote-mcp/config.js +263 -0
  50. package/dist/connectors/remote-mcp/connection.js +671 -0
  51. package/dist/connectors/remote-mcp/errors.js +488 -0
  52. package/dist/connectors/remote-mcp/oauth.js +469 -0
  53. package/dist/connectors/remote-mcp/resources.js +473 -0
  54. package/dist/connectors/remote-mcp/runtime.js +1 -0
  55. package/dist/connectors/remote-mcp/transport.js +544 -0
  56. package/dist/connectors/remote-mcp.js +61 -2898
  57. package/dist/connectors/resource-uri.js +0 -4
  58. package/dist/credential-contract.d.ts +23 -0
  59. package/dist/credential-reads.js +17 -0
  60. package/dist/credential-verification.js +53 -0
  61. package/dist/credentials.d.ts +7 -0
  62. package/dist/credentials.js +60 -25
  63. package/dist/d1.d.ts +0 -89
  64. package/dist/d1.js +0 -34
  65. package/dist/describe-config.d.ts +12 -2
  66. package/dist/describe-config.js +2 -0
  67. package/dist/discovery-health.js +199 -0
  68. package/dist/downstream-input.js +4 -2
  69. package/dist/errors.d.ts +55 -17
  70. package/dist/errors.js +95 -41
  71. package/dist/execute.js +172 -125
  72. package/dist/executor-result.js +5 -2
  73. package/dist/executors/quickjs-runtime.js +2 -25
  74. package/dist/executors/quickjs.js +1 -1
  75. package/dist/guest-functions.js +83 -0
  76. package/dist/guest-runtime.js +63 -20
  77. package/dist/guest-types.d.ts +24 -10
  78. package/dist/http/argument-problems.js +35 -0
  79. package/dist/http/client.js +1 -1
  80. package/dist/http/errors.js +7 -6
  81. package/dist/http/fields.js +192 -13
  82. package/dist/http/hatch-response.js +73 -0
  83. package/dist/http/hatch.js +88 -23
  84. package/dist/http/json-schema.js +1 -1
  85. package/dist/http/node-transport.js +0 -1
  86. package/dist/http/oauth.js +4 -3
  87. package/dist/http/paging.js +6 -9
  88. package/dist/http/provider.js +33 -5
  89. package/dist/http/safety.js +9 -1
  90. package/dist/http/services.js +1 -8
  91. package/dist/http/tools.js +45 -30
  92. package/dist/http/vendor.js +7 -7
  93. package/dist/identity.js +2 -6
  94. package/dist/index.d.ts +1 -1
  95. package/dist/index.js +9 -2
  96. package/dist/invocation-auth.js +2 -1
  97. package/dist/invocation.js +21 -6
  98. package/dist/membership.d.ts +13 -5
  99. package/dist/membership.js +34 -2
  100. package/dist/meta-tools.js +50 -35
  101. package/dist/native-recovery.js +4 -16
  102. package/dist/native-tools.js +6 -2
  103. package/dist/oauth-handoff.js +2 -2
  104. package/dist/oauth-sealing.js +10 -0
  105. package/dist/operation-vocabulary.js +136 -14
  106. package/dist/operator-record.d.ts +2 -2
  107. package/dist/operator-record.js +2 -49
  108. package/dist/operator-ui/contract.d.ts +12 -2
  109. package/dist/operator-ui/generated.js +3 -3
  110. package/dist/operator-ui/model.d.ts +22 -1
  111. package/dist/program-writes.js +3 -8
  112. package/dist/provider-call.js +0 -7
  113. package/dist/provider.d.ts +1 -1
  114. package/dist/providers/_shared/google/kit.js +1 -6
  115. package/dist/providers/_shared/google/service-account.js +1 -3
  116. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  117. package/dist/providers/_shared/guide.js +3 -0
  118. package/dist/providers/_shared/json.js +12 -0
  119. package/dist/providers/_shared/rest/compact-index.js +1 -1
  120. package/dist/providers/_shared/rest/kit.js +1 -3
  121. package/dist/providers/_shared/rest/operation-index.js +49 -53
  122. package/dist/providers/_shared/rest/value-safety.js +106 -28
  123. package/dist/providers/breeze/api.js +9 -8
  124. package/dist/providers/breeze/index.js +4 -2
  125. package/dist/providers/breeze/skill.generated.js +1 -1
  126. package/dist/providers/breeze/tools.js +7 -7
  127. package/dist/providers/ccb/api.js +3 -7
  128. package/dist/providers/ccb/index.js +7 -1
  129. package/dist/providers/ccb/skill.generated.js +1 -1
  130. package/dist/providers/ccb/tools.js +1 -6
  131. package/dist/providers/cloudflare/api.js +10 -3
  132. package/dist/providers/cloudflare/index.js +2 -1
  133. package/dist/providers/cloudflare/safety.js +3 -3
  134. package/dist/providers/cloudflare/skill.generated.js +1 -1
  135. package/dist/providers/docs/lib/schemas.js +24 -22
  136. package/dist/providers/docs/safety.js +2 -0
  137. package/dist/providers/docs/skill.generated.js +1 -1
  138. package/dist/providers/docs/tools.js +4 -14
  139. package/dist/providers/drive/api.js +1 -0
  140. package/dist/providers/drive/lib/contracts.js +8 -5
  141. package/dist/providers/drive/skill.generated.js +1 -1
  142. package/dist/providers/drive/tools.js +23 -41
  143. package/dist/providers/forms/api.js +1 -0
  144. package/dist/providers/forms/skill.generated.js +1 -1
  145. package/dist/providers/forms/tools.js +11 -16
  146. package/dist/providers/github/api.js +1 -0
  147. package/dist/providers/github/lib/discovery.js +1 -1
  148. package/dist/providers/github/skill.generated.js +1 -1
  149. package/dist/providers/github/tools.js +61 -17
  150. package/dist/providers/gmail/lib/schemas.js +60 -50
  151. package/dist/providers/gmail/skill.generated.js +1 -1
  152. package/dist/providers/gmail/tools.js +12 -26
  153. package/dist/providers/granola/index.js +2 -0
  154. package/dist/providers/granola/skill.generated.js +1 -1
  155. package/dist/providers/overflow/api.js +8 -4
  156. package/dist/providers/overflow/index.js +2 -1
  157. package/dist/providers/overflow/skill.generated.js +1 -1
  158. package/dist/providers/overflow/tools.js +3 -7
  159. package/dist/providers/planning-center/api.js +5 -2
  160. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  161. package/dist/providers/planning-center/lib/json-api.js +1 -9
  162. package/dist/providers/planning-center/skill.generated.js +1 -1
  163. package/dist/providers/planning-center/tools.js +2 -1
  164. package/dist/providers/quickbooks/safety.js +80 -39
  165. package/dist/providers/quickbooks/skill.generated.js +1 -1
  166. package/dist/providers/sheets/api.js +1 -0
  167. package/dist/providers/sheets/skill.generated.js +1 -1
  168. package/dist/providers/sheets/tools.js +14 -23
  169. package/dist/providers/slides/skill.generated.js +1 -1
  170. package/dist/providers/slides/tools.js +19 -26
  171. package/dist/providers/stripe/index.js +2 -1
  172. package/dist/providers/stripe/safety.js +2 -2
  173. package/dist/providers/stripe/skill.generated.js +1 -1
  174. package/dist/providers/tithely/api.js +5 -8
  175. package/dist/providers/tithely/index.js +3 -2
  176. package/dist/providers/tithely/skill.generated.js +1 -1
  177. package/dist/providers/tithely/tools.js +7 -5
  178. package/dist/providers/vercel/api.js +2 -7
  179. package/dist/providers/vercel/lib/pin.js +1 -1
  180. package/dist/providers/vercel/safety.js +1 -1
  181. package/dist/providers/vercel/skill.generated.js +1 -1
  182. package/dist/providers/vercel/tools.js +6 -4
  183. package/dist/redaction-marker.js +2 -2
  184. package/dist/registry.d.ts +11 -5
  185. package/dist/registry.js +46 -20
  186. package/dist/request-principal.js +1 -1
  187. package/dist/request-state.js +2 -2
  188. package/dist/routes/access-tokens.js +1 -1
  189. package/dist/routes/activity-disclosure.js +1 -1
  190. package/dist/routes/connect.js +2 -2
  191. package/dist/routes/credentials.js +130 -61
  192. package/dist/routes/dispatch.js +5 -1
  193. package/dist/routes/mcp.js +34 -17
  194. package/dist/routes/oauth-management.js +1 -1
  195. package/dist/routes/operator.js +6 -5
  196. package/dist/routes/shared.d.ts +13 -8
  197. package/dist/routes/shared.js +15 -9
  198. package/dist/routes/ui-config.js +3 -3
  199. package/dist/routes/ui-data.js +126 -20
  200. package/dist/routes/ui.js +17 -1
  201. package/dist/runtime/call-admission.js +1 -1
  202. package/dist/runtime/call.js +8 -3
  203. package/dist/runtime/native-storage.js +9 -0
  204. package/dist/runtime/request-reads.js +182 -0
  205. package/dist/runtime/request-scope.js +16 -0
  206. package/dist/runtime/settlement.js +1 -1
  207. package/dist/runtime/shared-read.js +2 -15
  208. package/dist/runtime/storage.js +12 -0
  209. package/dist/sent-secrets.d.ts +1 -2
  210. package/dist/sent-secrets.js +3 -8
  211. package/dist/server.js +4 -4
  212. package/dist/skills.js +44 -54
  213. package/dist/sqlite.d.ts +0 -19
  214. package/dist/sqlite.js +2 -83
  215. package/dist/storage/keys.js +69 -128
  216. package/dist/storage/memory.js +8 -2
  217. package/dist/storage/sql-native.js +27 -101
  218. package/dist/storage/sql.d.ts +1 -24
  219. package/dist/storage/sql.js +13 -14
  220. package/dist/storage-digest.js +32 -0
  221. package/dist/timeout.js +8 -0
  222. package/dist/tool-error.js +4 -0
  223. package/dist/types.d.ts +33 -0
  224. package/dist/url-safety.js +0 -4
  225. package/dist/usage-guide.js +18 -37
  226. package/dist/validate.d.ts +3 -0
  227. package/dist/validate.js +220 -158
  228. package/dist/version.d.ts +1 -1
  229. package/dist/version.js +1 -1
  230. package/dist/worker.js +1 -1
  231. package/dist/write-outcome.js +0 -3
  232. package/documentation/README.md +19 -55
  233. package/documentation/architecture.md +436 -515
  234. package/documentation/auth.md +229 -88
  235. package/documentation/code-mode.md +201 -94
  236. package/documentation/deploying.md +25 -43
  237. package/documentation/integrating.md +18 -19
  238. package/documentation/meta-tools.md +225 -51
  239. package/documentation/migration-0.33.md +10 -6
  240. package/documentation/migration-0.34.md +5 -5
  241. package/documentation/migration-retired-layouts.md +74 -0
  242. package/documentation/operator-tests.md +20 -120
  243. package/documentation/operator-ui.md +108 -16
  244. package/documentation/pool-membership-upgrade.md +44 -0
  245. package/documentation/provider-auth-migration.md +4 -10
  246. package/documentation/provider-migration-0.29.md +4 -15
  247. package/documentation/providers.md +105 -0
  248. package/examples/worker/AGENTS.md +14 -16
  249. package/examples/worker/README.md +24 -118
  250. package/examples/worker/src/connecta.config.ts +1 -0
  251. package/guides/connecting-agents.md +9 -31
  252. package/guides/how-it-works.md +6 -4
  253. package/guides/safety-and-trust.md +4 -1
  254. package/package.json +19 -19
  255. package/templates/node/AGENTS.md +13 -12
  256. package/templates/node/README.md +9 -26
  257. package/templates/node/package.json +1 -1
  258. package/templates/node/src/connecta.config.ts +1 -0
  259. package/dist/auth/oauth-v2-migration.js +0 -135
  260. package/dist/provider-call-facts.d.ts +0 -115
  261. package/dist/provider-call.d.ts +0 -112
  262. package/dist/storage/d1-copy.js +0 -255
  263. package/dist/storage/sql-schema.d.ts +0 -6
  264. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  265. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
package/dist/config.d.ts CHANGED
@@ -18,8 +18,10 @@ declare const discovery: {
18
18
  /**
19
19
  * Deadline (ms) for each downstream probe/catalog call fanned out by
20
20
  * `search_tools` and by `connecta.search`/`connecta.describe` inside
21
- * `execute_code`. Defaults to 30_000. A timed-out connector degrades
22
- * independently; this does not apply to tool calls. Catalog walks receive the
21
+ * `execute_code`. Defaults to 30_000. An unscoped search caps each probe at
22
+ * 5_000 and its whole fan-out at 9_000, and skips a connector whose
23
+ * discovery recently timed out for up to 60 seconds. A timed-out connector
24
+ * degrades independently; this does not apply to tool calls. Catalog walks receive the
23
25
  * same cancellation signal, which aborts an in-flight page where supported and
24
26
  * prevents another from starting.
25
27
  */
@@ -28,11 +30,12 @@ declare const discovery: {
28
30
  declare const calls: {
29
31
  /**
30
32
  * Deadline (ms) for `call_tool`/`call_destructive_tool` calls that pass no
31
- * `timeoutMs`. An explicit per-call value wins. Opt-in: unset by default, so
32
- * existing long-running calls gain no surprise deadline.
33
+ * `timeoutMs`. An explicit per-call value wins. Unset by default, when those
34
+ * calls fall back to `execute.hostCallTimeoutMs`.
33
35
  *
34
- * Each call makes one attempt. `execute_code` host calls are
35
- * unaffected because they already carry their own bound.
36
+ * Each call makes one attempt. `connecta.call` inside `execute_code` uses
37
+ * it too, ahead of `execute.hostCallTimeoutMs`; `connecta.result`,
38
+ * `connecta.skill` and `connecta.read` do not.
36
39
  */
37
40
  defaultTimeoutMs: import("./config-schema.js").Field<number, number | undefined, true>;
38
41
  /**
@@ -60,9 +63,12 @@ declare const execute: {
60
63
  /** Host calls one program may make. Default 20. */
61
64
  maxHostCalls: import("./config-schema.js").Field<number, number, true>;
62
65
  /**
63
- * Deadline for each host call a program makes, in milliseconds. Default
64
- * 15_000. Raise it for providers whose legitimate calls run longer, such as
65
- * analytics queries; `call_tool`'s own `timeoutMs` is unaffected.
66
+ * Deadline for a program's `connecta.result`, `connecta.skill` and
67
+ * `connecta.read`, in milliseconds. Default 15_000. Discovery uses
68
+ * `discovery.probeTimeoutMs`. `connecta.call` and the direct call tools use it only when
69
+ * `calls.defaultTimeoutMs` is unset, and an explicit per-call `timeoutMs`
70
+ * always wins. Raise it for providers whose legitimate calls run longer,
71
+ * such as analytics queries.
66
72
  */
67
73
  hostCallTimeoutMs: import("./config-schema.js").Field<number, number, true>;
68
74
  /**
@@ -78,6 +84,14 @@ declare const execute: {
78
84
  * budget every call already spends. Default 10.
79
85
  */
80
86
  maxWrites: import("./config-schema.js").Field<number, number, true>;
87
+ /**
88
+ * Serialized characters a program may return, shared with emitted text,
89
+ * before Connecta stashes the return and answers with a paging notice.
90
+ * Default 24_000 (~6k tokens). Raise it when agents need large results whole
91
+ * and the model's context can afford them: each paging round trip costs a
92
+ * model step. From 4_000 to 1_000_000.
93
+ */
94
+ maxResultChars: import("./config-schema.js").Field<number, number, true>;
81
95
  };
82
96
  declare const codeAdmission: {
83
97
  /** Simultaneous work admitted to this pool. */
@@ -179,8 +193,9 @@ declare const pool: {
179
193
  /**
180
194
  * Who may open `/mcp/<name>`: principal ids in the namespace of the one
181
195
  * interactive auth provider, `{ namespace, id }`, or `"agent:<name>"` for a
182
- * declared agent. Required and non-empty; the pool serves exactly its
183
- * `tools` to these members and the same 404 to everyone else.
196
+ * declared agent. All members must be people or all must be agents. Required
197
+ * and non-empty; the pool serves exactly its `tools` to these members and
198
+ * the same 404 to everyone else.
184
199
  */
185
200
  members: import("./config-schema.js").Field<readonly MemberRef[], readonly MemberRef[], false>;
186
201
  /**
@@ -188,8 +203,8 @@ declare const pool: {
188
203
  * connector whose `subject` is a mapping function. Humans always act as
189
204
  * themselves through that mapping, and a fixed `subject` address applies to
190
205
  * everyone. Without an owner, a mapping-subject Workspace connector is
191
- * absent for this pool's agents, as a personal connector is; people in the
192
- * pool still see it.
206
+ * absent for this pool's agents, as a personal connector is. People use a
207
+ * separate pool and always act as themselves.
193
208
  */
194
209
  owner: import("./config-schema.js").Field<string, string | undefined, true>;
195
210
  /**
@@ -206,7 +221,7 @@ declare const pool: {
206
221
  * `<connector>__<tool>` and annotated from its classification, for clients
207
222
  * that run their own tool search; calls take the same invocation path.
208
223
  */
209
- surface: import("./config-schema.js").Field<"native" | "meta", "native" | "meta" | undefined, true>;
224
+ surface: import("./config-schema.js").Field<"meta" | "native", "meta" | "native" | undefined, true>;
210
225
  /**
211
226
  * `false` drops `execute_code` from a native pool and lists `read_result`
212
227
  * for paging instead. Only valid with `surface: "native"`: the meta surface
@@ -242,7 +257,7 @@ declare const connectaConfig: {
242
257
  readOnly?: boolean | undefined;
243
258
  owner?: string | undefined;
244
259
  description?: string | undefined;
245
- surface?: "native" | "meta" | undefined;
260
+ surface?: "meta" | "native" | undefined;
246
261
  executeCode?: boolean | undefined;
247
262
  tools: readonly string[];
248
263
  members: readonly MemberRef[];
@@ -252,7 +267,7 @@ declare const connectaConfig: {
252
267
  readonly members: readonly MemberRef[];
253
268
  readonly owner: string | undefined;
254
269
  readonly description: string | undefined;
255
- readonly surface: "native" | "meta" | undefined;
270
+ readonly surface: "meta" | "native" | undefined;
256
271
  readonly executeCode: boolean | undefined;
257
272
  }>> | undefined, true>;
258
273
  /** Exact connector id -> tool name -> verdict. Hidden tools cannot be discovered or called.
@@ -314,8 +329,10 @@ declare const connectaConfig: {
314
329
  /**
315
330
  * Deadline (ms) for each downstream probe/catalog call fanned out by
316
331
  * `search_tools` and by `connecta.search`/`connecta.describe` inside
317
- * `execute_code`. Defaults to 30_000. A timed-out connector degrades
318
- * independently; this does not apply to tool calls. Catalog walks receive the
332
+ * `execute_code`. Defaults to 30_000. An unscoped search caps each probe at
333
+ * 5_000 and its whole fan-out at 9_000, and skips a connector whose
334
+ * discovery recently timed out for up to 60 seconds. A timed-out connector
335
+ * degrades independently; this does not apply to tool calls. Catalog walks receive the
319
336
  * same cancellation signal, which aborts an in-flight page where supported and
320
337
  * prevents another from starting.
321
338
  */
@@ -333,11 +350,12 @@ declare const connectaConfig: {
333
350
  readonly shape: {
334
351
  /**
335
352
  * Deadline (ms) for `call_tool`/`call_destructive_tool` calls that pass no
336
- * `timeoutMs`. An explicit per-call value wins. Opt-in: unset by default, so
337
- * existing long-running calls gain no surprise deadline.
353
+ * `timeoutMs`. An explicit per-call value wins. Unset by default, when those
354
+ * calls fall back to `execute.hostCallTimeoutMs`.
338
355
  *
339
- * Each call makes one attempt. `execute_code` host calls are
340
- * unaffected because they already carry their own bound.
356
+ * Each call makes one attempt. `connecta.call` inside `execute_code` uses
357
+ * it too, ahead of `execute.hostCallTimeoutMs`; `connecta.result`,
358
+ * `connecta.skill` and `connecta.read` do not.
341
359
  */
342
360
  defaultTimeoutMs: import("./config-schema.js").Field<number, number | undefined, true>;
343
361
  /**
@@ -371,6 +389,7 @@ declare const connectaConfig: {
371
389
  hostCallTimeoutMs?: number | undefined;
372
390
  watchdogMs?: number | undefined;
373
391
  maxWrites?: number | undefined;
392
+ maxResultChars?: number | undefined;
374
393
  }, {
375
394
  readonly maxEmittedBytes: number;
376
395
  readonly maxEmittedBlocks: number;
@@ -378,6 +397,7 @@ declare const connectaConfig: {
378
397
  readonly hostCallTimeoutMs: number;
379
398
  readonly watchdogMs: number;
380
399
  readonly maxWrites: number;
400
+ readonly maxResultChars: number;
381
401
  }, true> & {
382
402
  readonly shape: {
383
403
  /**
@@ -391,9 +411,12 @@ declare const connectaConfig: {
391
411
  /** Host calls one program may make. Default 20. */
392
412
  maxHostCalls: import("./config-schema.js").Field<number, number, true>;
393
413
  /**
394
- * Deadline for each host call a program makes, in milliseconds. Default
395
- * 15_000. Raise it for providers whose legitimate calls run longer, such as
396
- * analytics queries; `call_tool`'s own `timeoutMs` is unaffected.
414
+ * Deadline for a program's `connecta.result`, `connecta.skill` and
415
+ * `connecta.read`, in milliseconds. Default 15_000. Discovery uses
416
+ * `discovery.probeTimeoutMs`. `connecta.call` and the direct call tools use it only when
417
+ * `calls.defaultTimeoutMs` is unset, and an explicit per-call `timeoutMs`
418
+ * always wins. Raise it for providers whose legitimate calls run longer,
419
+ * such as analytics queries.
397
420
  */
398
421
  hostCallTimeoutMs: import("./config-schema.js").Field<number, number, true>;
399
422
  /**
@@ -409,6 +432,14 @@ declare const connectaConfig: {
409
432
  * budget every call already spends. Default 10.
410
433
  */
411
434
  maxWrites: import("./config-schema.js").Field<number, number, true>;
435
+ /**
436
+ * Serialized characters a program may return, shared with emitted text,
437
+ * before Connecta stashes the return and answers with a paging notice.
438
+ * Default 24_000 (~6k tokens). Raise it when agents need large results whole
439
+ * and the model's context can afford them: each paging round trip costs a
440
+ * model step. From 4_000 to 1_000_000.
441
+ */
442
+ maxResultChars: import("./config-schema.js").Field<number, number, true>;
412
443
  };
413
444
  };
414
445
  /**
@@ -654,5 +685,6 @@ export declare function executeLimits(config: ResolvedConfig): {
654
685
  hostCallTimeoutMs: number;
655
686
  watchdogMs: number;
656
687
  maxWrites: number;
688
+ maxResultChars: number;
657
689
  };
658
690
  export {};
package/dist/config.js CHANGED
@@ -69,8 +69,10 @@ const discovery = {
69
69
  /**
70
70
  * Deadline (ms) for each downstream probe/catalog call fanned out by
71
71
  * `search_tools` and by `connecta.search`/`connecta.describe` inside
72
- * `execute_code`. Defaults to 30_000. A timed-out connector degrades
73
- * independently; this does not apply to tool calls. Catalog walks receive the
72
+ * `execute_code`. Defaults to 30_000. An unscoped search caps each probe at
73
+ * 5_000 and its whole fan-out at 9_000, and skips a connector whose
74
+ * discovery recently timed out for up to 60 seconds. A timed-out connector
75
+ * degrades independently; this does not apply to tool calls. Catalog walks receive the
74
76
  * same cancellation signal, which aborts an in-flight page where supported and
75
77
  * prevents another from starting.
76
78
  */
@@ -79,11 +81,12 @@ const discovery = {
79
81
  const calls = {
80
82
  /**
81
83
  * Deadline (ms) for `call_tool`/`call_destructive_tool` calls that pass no
82
- * `timeoutMs`. An explicit per-call value wins. Opt-in: unset by default, so
83
- * existing long-running calls gain no surprise deadline.
84
+ * `timeoutMs`. An explicit per-call value wins. Unset by default, when those
85
+ * calls fall back to `execute.hostCallTimeoutMs`.
84
86
  *
85
- * Each call makes one attempt. `execute_code` host calls are
86
- * unaffected because they already carry their own bound.
87
+ * Each call makes one attempt. `connecta.call` inside `execute_code` uses
88
+ * it too, ahead of `execute.hostCallTimeoutMs`; `connecta.result`,
89
+ * `connecta.skill` and `connecta.read` do not.
87
90
  */
88
91
  defaultTimeoutMs: whole({ min: 1 }),
89
92
  /**
@@ -111,9 +114,12 @@ const execute = {
111
114
  /** Host calls one program may make. Default 20. */
112
115
  maxHostCalls: whole({ min: 1, default: D.execute.maxHostCalls }),
113
116
  /**
114
- * Deadline for each host call a program makes, in milliseconds. Default
115
- * 15_000. Raise it for providers whose legitimate calls run longer, such as
116
- * analytics queries; `call_tool`'s own `timeoutMs` is unaffected.
117
+ * Deadline for a program's `connecta.result`, `connecta.skill` and
118
+ * `connecta.read`, in milliseconds. Default 15_000. Discovery uses
119
+ * `discovery.probeTimeoutMs`. `connecta.call` and the direct call tools use it only when
120
+ * `calls.defaultTimeoutMs` is unset, and an explicit per-call `timeoutMs`
121
+ * always wins. Raise it for providers whose legitimate calls run longer,
122
+ * such as analytics queries.
117
123
  */
118
124
  hostCallTimeoutMs: whole({ min: 1, default: D.execute.hostCallTimeoutMs }),
119
125
  /**
@@ -129,6 +135,14 @@ const execute = {
129
135
  * budget every call already spends. Default 10.
130
136
  */
131
137
  maxWrites: whole({ min: 1, default: D.execute.maxWrites }),
138
+ /**
139
+ * Serialized characters a program may return, shared with emitted text,
140
+ * before Connecta stashes the return and answers with a paging notice.
141
+ * Default 24_000 (~6k tokens). Raise it when agents need large results whole
142
+ * and the model's context can afford them: each paging round trip costs a
143
+ * model step. From 4_000 to 1_000_000.
144
+ */
145
+ maxResultChars: whole({ min: 4_000, max: 1_000_000, default: D.execute.maxResultChars }),
132
146
  };
133
147
  function admissionPool(defaults) {
134
148
  return {
@@ -225,8 +239,9 @@ const pool = {
225
239
  /**
226
240
  * Who may open `/mcp/<name>`: principal ids in the namespace of the one
227
241
  * interactive auth provider, `{ namespace, id }`, or `"agent:<name>"` for a
228
- * declared agent. Required and non-empty; the pool serves exactly its
229
- * `tools` to these members and the same 404 to everyone else.
242
+ * declared agent. All members must be people or all must be agents. Required
243
+ * and non-empty; the pool serves exactly its `tools` to these members and
244
+ * the same 404 to everyone else.
230
245
  */
231
246
  members: required(opaque({ check: assertMemberList }), (path) => `${path} is required: list who may open this pool, as principal ids, { namespace, id }, or "agent:<name>".`),
232
247
  /**
@@ -234,8 +249,8 @@ const pool = {
234
249
  * connector whose `subject` is a mapping function. Humans always act as
235
250
  * themselves through that mapping, and a fixed `subject` address applies to
236
251
  * everyone. Without an owner, a mapping-subject Workspace connector is
237
- * absent for this pool's agents, as a personal connector is; people in the
238
- * pool still see it.
252
+ * absent for this pool's agents, as a personal connector is. People use a
253
+ * separate pool and always act as themselves.
239
254
  */
240
255
  owner: opaque({
241
256
  check: (value, path) => {
@@ -492,48 +507,6 @@ const connectaConfig = {
492
507
  readOnly: required(opaque({ check: assertFlag }), (path) => READ_ONLY_REQUIRED(path)),
493
508
  };
494
509
  const CONFIG = object(connectaConfig);
495
- /** Pause-only options #672 removed with `resume_execution`. */
496
- const RETIRED_PAUSE_OPTIONS = ["ConnectaConfig.execute.resumableWrites", "ConnectaConfig.execute.pausedRunTtlSeconds"];
497
- /** Access options pools replaced (#837, decisions/0006). */
498
- const isRetiredPoolGrant = (path) => /^ConnectaConfig\.pools\.[^.]+\.grant$/.test(path);
499
- /** Management resolvers maintainers and pool membership replaced (#837, decisions/0006). */
500
- const RETIRED_IDENTITY = [
501
- ["credentialAdministration", "maintainers administer shared credentials and OAuth grants. List them in maintainers."],
502
- [
503
- "personalConnection",
504
- "pool members connect and disconnect their own personal connectors in their pools, and maintainers their own. Add people to a pool's members.",
505
- ],
506
- [
507
- "accessTokenManagement",
508
- "maintainers issue, rotate, and revoke agent tokens from an interactive session. List them in maintainers.",
509
- ],
510
- ["activityAccess", "maintainers read activity; activity.readGate can still narrow it. List them in maintainers."],
511
- ];
512
- /** `trust`, on the deployment or a pool, was replaced by `readOnly` (#820). */
513
- const isRetiredTrust = (path) => path === "ConnectaConfig.trust" || (path.startsWith("ConnectaConfig.pools.") && path.endsWith(".trust"));
514
- /**
515
- * `identity` was removed whole (#837), so the walk reports only the section.
516
- * Name each key it still holds, read by descriptor without running a getter
517
- * or echoing a value, so a retired resolver's message names its replacement.
518
- */
519
- function expandRetiredIdentity(config, paths) {
520
- if (!paths.includes("ConnectaConfig.identity"))
521
- return paths;
522
- let keys = [];
523
- try {
524
- const descriptor = Reflect.getOwnPropertyDescriptor(config, "identity");
525
- const value = descriptor && "value" in descriptor ? descriptor.value : undefined;
526
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
527
- keys = Reflect.ownKeys(value).filter((key) => typeof key === "string");
528
- }
529
- }
530
- catch {
531
- keys = [];
532
- }
533
- if (keys.length === 0)
534
- return paths;
535
- return paths.flatMap((path) => path === "ConnectaConfig.identity" ? keys.map((key) => `ConnectaConfig.identity.${key}`) : [path]);
536
- }
537
510
  function rejectUnknownOptions(paths) {
538
511
  if (paths.length === 0)
539
512
  return;
@@ -547,27 +520,6 @@ function rejectUnknownOptions(paths) {
547
520
  : "") +
548
521
  (paths.includes("ConnectaConfig.execute.approval")
549
522
  ? "\nexecute.approval was removed. The MCP client owns approval; set readOnly: true on the deployment or a pool to refuse writes."
550
- : "") +
551
- (paths.some((path) => RETIRED_PAUSE_OPTIONS.includes(path))
552
- ? "\nPrograms no longer pause at writes, so there is nothing to configure: writable endpoints let programs and call_destructive_tool write; readOnly endpoints refuse every write (issue #672). Delete the option."
553
- : "") +
554
- (paths.includes("ConnectaConfig.identity.connectorAccess")
555
- ? "\nidentity.connectorAccess was removed (#837): pools are the access model. List who may use plain /mcp in maintainers, and give each pool its tools and members. A guarded { tool, requireReadOnly: true } grant becomes a pool with readOnly: true."
556
- : "") +
557
- RETIRED_IDENTITY.filter(([key]) => paths.includes(`ConnectaConfig.identity.${key}`))
558
- .map(([key, replacement]) => `\nidentity.${key} was removed (#837): ${replacement}`)
559
- .join("") +
560
- (paths.some((path) => path === "ConnectaConfig.identity" || path.startsWith("ConnectaConfig.identity."))
561
- ? "\nThe identity section was removed (#837): maintainers and pool members decide access and management. Delete it."
562
- : "") +
563
- (paths.includes("ConnectaConfig.grant")
564
- ? "\nThe top-level grant was removed (#837); list the people who may use plain /mcp in maintainers."
565
- : "") +
566
- (paths.some(isRetiredPoolGrant)
567
- ? "\nPool grant was removed (#837); list who may open the pool in its members."
568
- : "") +
569
- (paths.some(isRetiredTrust)
570
- ? '\ntrust was removed (#820); set readOnly, which every deployment now chooses explicitly. trust: "read-only" becomes readOnly: true, which now also refuses call_destructive_tool. trust: "trusted" becomes readOnly: false. A pool inherits the deployment readOnly unless it sets its own.'
571
523
  : ""));
572
524
  }
573
525
  /** Checks that span fields; each value is already individually valid. */
@@ -598,7 +550,7 @@ export function resolveConfig(config) {
598
550
  */
599
551
  export function readConfig(config) {
600
552
  const read = readPlain(config, "ConnectaConfig", CONFIG);
601
- rejectUnknownOptions(expandRetiredIdentity(config, read.unknown));
553
+ rejectUnknownOptions(read.unknown);
602
554
  const input = read.value;
603
555
  const parsed = CONFIG.parse(input, "ConnectaConfig");
604
556
  assertCoherent(parsed);
@@ -648,5 +600,6 @@ export function executeLimits(config) {
648
600
  hostCallTimeoutMs: config.execute.hostCallTimeoutMs,
649
601
  watchdogMs: config.execute.watchdogMs,
650
602
  maxWrites: config.execute.maxWrites,
603
+ maxResultChars: config.execute.maxResultChars,
651
604
  };
652
605
  }
@@ -9,7 +9,7 @@ export interface ConnectorCaller {
9
9
  * with or without a subject, a Connecta-issued access token.
10
10
  */
11
11
  authenticated: boolean;
12
- /** The pool the call arrived on, at `/mcp/<pool>` or `/agent/<pool>`. */
12
+ /** The admitted pool: routed at `/mcp` or selected at `/mcp/<pool>` or `/agent/<pool>`. */
13
13
  pool?: string;
14
14
  /**
15
15
  * That pool's configured `owner`, the Workspace address an agent acts as
@@ -0,0 +1,59 @@
1
+ // Where one connector's bounded health facts are stored for a registry view:
2
+ // the catalog cache's store and owner partition (principal, subject, caller
3
+ // identity and pool, origin) and the admitted endpoint, with the connector's
4
+ // described configuration and the package version, through the deployment's
5
+ // keyed digest (a described endpoint keeps its path), beside the catalog
6
+ // invalidation generation those facts must match (INV-4, INV-5). Shared by
7
+ // discovery health and the operator page's status observations.
8
+ import { Effect } from "effect";
9
+ import { catalogOwnerPartition } from "./catalog-policy.js";
10
+ import { canonicalJson } from "./canonical-json.js";
11
+ import { secretDigest } from "./storage-digest.js";
12
+ import { responseCacheKeys } from "./storage/keys.js";
13
+ import { CONNECTA_VERSION } from "./version.js";
14
+ // A connector's described configuration, serialized once per connector object.
15
+ // A change `describe()` cannot see, such as a static header rotated in
16
+ // deployment code, takes effect on redeploy and leaves an earlier record to
17
+ // lapse within its TTL.
18
+ const configs = new WeakMap();
19
+ function describedConfig(connector) {
20
+ let config = configs.get(connector);
21
+ if (config === undefined) {
22
+ let described = null;
23
+ try {
24
+ described = connector.describe?.() ?? null;
25
+ }
26
+ catch {
27
+ described = null;
28
+ }
29
+ config = canonicalJson([CONNECTA_VERSION, connector.kind ?? null, connector.authScope ?? null, described]);
30
+ configs.set(connector, config);
31
+ }
32
+ return config;
33
+ }
34
+ // The endpoint a request was admitted at (plain `/mcp`, a pool, an agent
35
+ // path). The same person and pool can reach one connector through several,
36
+ // and each keeps its own records. The operator page binds none.
37
+ const endpoints = new WeakMap();
38
+ /** Bind the admitted endpoint to a request scope, before any discovery runs in it. */
39
+ export function bindDiscoveryEndpoint(requestScope, endpoint) {
40
+ endpoints.set(requestScope, endpoint);
41
+ }
42
+ /** Where one connector's records sit in this view's owner partition. */
43
+ export function healthAddress(registry, id, baseUrl, requestScope) {
44
+ return Effect.suspend(() => {
45
+ const connector = registry.getConnector(id);
46
+ if (!connector)
47
+ return Effect.succeed(undefined);
48
+ const ctx = registry.contextFor(id, baseUrl, requestScope);
49
+ const { storage, partition } = catalogOwnerPartition(ctx);
50
+ if (!storage)
51
+ return Effect.succeed(undefined);
52
+ return Effect.promise(async () => ({
53
+ storage,
54
+ digest: await secretDigest(ctx, JSON.stringify([partition, endpoints.get(requestScope) ?? null, describedConfig(connector)])),
55
+ generationKey: responseCacheKeys.generation(id),
56
+ logger: ctx.logger,
57
+ }));
58
+ }).pipe(Effect.catchCause(() => Effect.succeed(undefined)));
59
+ }
@@ -0,0 +1,117 @@
1
+ // The operator page's stored connector status (#1173 item 2.3).
2
+ //
3
+ // A live detail read records what it saw, one observation per connector and
4
+ // viewer partition, beside discovery health in the connector-health family and
5
+ // under the same owner partition and catalog generation (INV-4, INV-5). The
6
+ // detail route can then answer at once from storage, labelled with its age,
7
+ // and the browser asks for a live read separately. An observation holds only
8
+ // a state, a problem code, a tool count and its time (INV-6); the tools stay
9
+ // in the complete catalog cache (INV-8). It is never deferred work: a live
10
+ // read writes it before its response, and nothing refreshes it afterwards.
11
+ import { Effect } from "effect";
12
+ import { healthAddress } from "./connector-health.js";
13
+ import { failureRecord, logFailure } from "./operator-record.js";
14
+ import { causeError } from "./runtime/run.js";
15
+ import { Storage } from "./runtime/services.js";
16
+ import { requestReads } from "./runtime/request-scope.js";
17
+ import { storageGet, storageGetMany, storageSet } from "./runtime/storage.js";
18
+ import { connectorHealthKeys } from "./storage/keys.js";
19
+ /** How long the operator page shows a stored status before only a live read will do. */
20
+ const STATUS_OBSERVATION_TTL_MS = 120_000;
21
+ const STATUS_STATES = new Set([
22
+ "ok",
23
+ "per_caller",
24
+ "auth_required",
25
+ "credential_required",
26
+ "error",
27
+ ]);
28
+ const STATUS_PROBLEMS = new Set([
29
+ "connector_unavailable",
30
+ "connector_timeout",
31
+ "oauth_required",
32
+ "credential_required",
33
+ "auth_required",
34
+ "caller_refused",
35
+ "credential_mismatch",
36
+ "catalog_failed",
37
+ ]);
38
+ /**
39
+ * This viewer's current observation of one connector, and the slot a live
40
+ * read writes its own to when there is none. A record from another generation, past its TTL or
41
+ * of an unknown shape is absent. A storage failure is absent too.
42
+ */
43
+ export function readStatusObservation(registry, id, baseUrl, requestScope) {
44
+ return Effect.gen(function* () {
45
+ const address = yield* healthAddress(registry, id, baseUrl, requestScope);
46
+ if (!address)
47
+ return {};
48
+ const key = connectorHealthKeys.status(id, address.digest);
49
+ const values = yield* storageGetMany([key, address.generationKey]).pipe(Effect.provideService(Storage, address.storage), Effect.catchCause((cause) => Effect.sync(() => {
50
+ logFailure(address.logger, "discovery health read failed", failureRecord({}, causeError(cause)));
51
+ return undefined;
52
+ })));
53
+ if (!values)
54
+ return {};
55
+ const slot = { storage: address.storage, key, logger: address.logger, generation: values[1] ?? null };
56
+ try {
57
+ const value = JSON.parse(values[0] ?? "null");
58
+ if (value?.v === 1 &&
59
+ value.generation === slot.generation &&
60
+ typeof value.state === "string" &&
61
+ STATUS_STATES.has(value.state) &&
62
+ (value.problem === undefined || STATUS_PROBLEMS.has(value.problem)) &&
63
+ Number.isSafeInteger(value.toolCount) &&
64
+ value.toolCount >= 0 &&
65
+ Number.isFinite(value.observedAt) &&
66
+ Date.now() - value.observedAt < STATUS_OBSERVATION_TTL_MS)
67
+ return {
68
+ slot,
69
+ observation: {
70
+ state: value.state,
71
+ ...(value.problem ? { problem: value.problem } : {}),
72
+ toolCount: value.toolCount,
73
+ observedAt: value.observedAt,
74
+ },
75
+ };
76
+ }
77
+ catch {
78
+ /* An unreadable record is absent. */
79
+ }
80
+ return { slot };
81
+ });
82
+ }
83
+ /**
84
+ * Where a live read will record its observation, without reading the stored
85
+ * one. The generation is read through the request's read memo: a live read's
86
+ * catalog listing locates its cache entry by that same key, so the listing
87
+ * reuses this read instead of paying another round trip. A generation is never
88
+ * reused once replaced, so a memoized value can only lapse an observation
89
+ * early, never keep one past an invalidation that preceded this request.
90
+ */
91
+ export function prepareStatusSlot(registry, id, baseUrl, requestScope) {
92
+ return Effect.gen(function* () {
93
+ const address = yield* healthAddress(registry, id, baseUrl, requestScope);
94
+ if (!address)
95
+ return undefined;
96
+ const generation = yield* requestReads(requestScope)
97
+ .read(address.storage, address.generationKey, storageGet(address.generationKey))
98
+ .pipe(Effect.provideService(Storage, address.storage), Effect.catchCause((cause) => Effect.sync(() => {
99
+ logFailure(address.logger, "discovery health read failed", failureRecord({}, causeError(cause)));
100
+ return undefined;
101
+ })));
102
+ if (generation === undefined)
103
+ return undefined;
104
+ return {
105
+ storage: address.storage,
106
+ key: connectorHealthKeys.status(id, address.digest),
107
+ logger: address.logger,
108
+ generation,
109
+ };
110
+ });
111
+ }
112
+ /** Record what a live read saw. A failed write is logged; the page already has its answer. */
113
+ export function storeStatusObservation(slot, observation) {
114
+ return storageSet(slot.key, JSON.stringify({ v: 1, ...observation, generation: slot.generation }), {
115
+ expiresAtMs: Math.ceil(observation.observedAt + STATUS_OBSERVATION_TTL_MS),
116
+ }).pipe(Effect.provideService(Storage, slot.storage), Effect.catchCause((cause) => Effect.sync(() => logFailure(slot.logger, "discovery health write failed", failureRecord({}, causeError(cause))))));
117
+ }
@@ -204,8 +204,8 @@ export declare const API_OPTIONS: import("../config-schema.js").Field<unknown, u
204
204
  credential: import("../config-schema.js").Field<unknown, unknown, true> & {
205
205
  readonly shape: {
206
206
  fields: import("../config-schema.js").Field<unknown[], readonly unknown[] | undefined, true>;
207
- label: import("../config-schema.js").Field<unknown, unknown, true>;
208
207
  description: import("../config-schema.js").Field<unknown, unknown, true>;
208
+ label: import("../config-schema.js").Field<unknown, unknown, true>;
209
209
  placeholder: import("../config-schema.js").Field<unknown, unknown, true>;
210
210
  };
211
211
  };
@@ -228,9 +228,9 @@ export declare const API_OPTIONS: import("../config-schema.js").Field<unknown, u
228
228
  };
229
229
  tools: import("../config-schema.js").Field<unknown[], readonly unknown[] | undefined, true>;
230
230
  title: import("../config-schema.js").Field<unknown, unknown, true>;
231
+ description: import("../config-schema.js").Field<unknown, unknown, true>;
231
232
  authScope: import("../config-schema.js").Field<unknown, unknown, true>;
232
233
  maxResultBytes: import("../config-schema.js").Field<unknown, unknown, true>;
233
- description: import("../config-schema.js").Field<unknown, unknown, true>;
234
234
  testCredential: import("../config-schema.js").Field<unknown, unknown, true>;
235
235
  testCredentials: import("../config-schema.js").Field<unknown, unknown, true>;
236
236
  validateArgs: import("../config-schema.js").Field<unknown, unknown, true>;
@@ -244,6 +244,11 @@ export declare const API_OPTIONS: import("../config-schema.js").Field<unknown, u
244
244
  */
245
245
  export interface ApiOAuthHooks {
246
246
  status(ctx: ConnectorContext): Promise<ConnectorStatus>;
247
+ /**
248
+ * `status` for a call about to enter a handler, which also renews a token
249
+ * within a minute of its stored expiry. A failed renewal rejects.
250
+ */
251
+ prepareCall(ctx: ConnectorContext): Promise<ConnectorStatus>;
247
252
  startAuth(ctx: ConnectorContext, opts?: {
248
253
  force?: boolean;
249
254
  }): Promise<ConnectorStatus>;
@@ -1,3 +1,5 @@
1
+ import { declaresRouteDescriptions, routePhrases } from "../agent-routes.js";
2
+ import { wrapViewCredentials } from "../runtime/call.js";
1
3
  import { storedAuthObservation, withStoredAuth } from "../auth/stored-observation.js";
2
4
  import { Effect, Exit } from "effect";
3
5
  import { registerConnectorVocabulary } from "../operation-vocabulary.js";
@@ -13,7 +15,7 @@ import { describedEndpoint, describedOrigin, describedTools } from "../described
13
15
  import { READ_ONLY, CALL_ADMISSION, CREDENTIAL, USAGE_GUIDE } from "./option-shapes.js";
14
16
  import { assertStaticToolNames } from "../tool-name.js";
15
17
  import { registerInvocationAuth } from "../invocation-auth.js";
16
- import { redactCatalog, redactSentSecrets, sentSecretsFetch, trackCredentialReads } from "../sent-secrets.js";
18
+ import { redactCatalog, redactSentSecrets, sentSecretsFetch } from "../sent-secrets.js";
17
19
  import { callClassification } from "../call-classification.js";
18
20
  import { classifyTool } from "../tool-safety.js";
19
21
  import { closeProviderCall, enterProviderBoundary, inLogicalCall, knownProviderFailure, nestedProviderRequests, providerBoundary, providerCallContext, providerFetch, providerRequests, tracksRequests, } from "../provider-call.js";
@@ -24,7 +26,7 @@ export function defined(value) {
24
26
  }
25
27
  /**
26
28
  * Three things a hand-written surface is refused for at construction rather
27
- * than in production (#340): no description (discovery has nothing to route on
29
+ * than in production: no description (discovery has nothing to route on
28
30
  * and a guess costs a call), no explicit `readOnlyHint` (connecta never infers
29
31
  * the safety class, so an unclassified tool is a deployment bug), and an
30
32
  * `inputSchema` the validator cannot compile (declaring one is optional;
@@ -173,7 +175,7 @@ export function apiConnector(id, opts, oauth) {
173
175
  callTool: (name, args, ctx, options) => runEdge(core(name, args, ctx, options)),
174
176
  };
175
177
  const core = (name, args, ctx) => Effect.gen(function* () {
176
- trackCredentialReads(ctx);
178
+ wrapViewCredentials(ctx);
177
179
  enterProviderBoundary(ctx, connector);
178
180
  const tool = byName.get(name);
179
181
  // Refusals before the handler runs are known failures: nothing was sent.
@@ -295,13 +297,15 @@ export function apiConnector(id, opts, oauth) {
295
297
  // This boundary's identity in a logical call, fixed now: this object with
296
298
  // this function, never whatever the property or a copy holds by then.
297
299
  providerBoundary(connector);
300
+ // Its catalog is authored here, so declared route descriptions follow each request's surface.
301
+ declaresRouteDescriptions(connector);
298
302
  registerCallEntry(connector, core);
299
303
  if (oauth)
300
304
  registerInvocationAuth(connector, async (ctx) => {
301
- // The built-in static OAuth status only reads the stored grant. It never
302
- // refreshes, invokes a handler, or sends a downstream request.
303
- if ((await settleOnClose(ctx, () => oauth.status(ctx))).state === "auth_required") {
304
- throw new ConnectorCallError("downstream_oauth_required", `Connector "${id}" requires authorization. Call authorize_connector.`);
305
+ // The stored grant, as status reads it. Only a token about to expire is
306
+ // renewed, before any handler runs; nothing is sent to the API.
307
+ if ((await settleOnClose(ctx, () => oauth.prepareCall(ctx))).state === "auth_required") {
308
+ throw new ConnectorCallError("downstream_oauth_required", `Connector "${id}" requires authorization. ${routePhrases(ctx).authorizeCall()}`);
305
309
  }
306
310
  });
307
311
  registerConnectorVocabulary(connector, opts.tools);