@zackbart/connecta 0.37.0 → 0.38.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 (183) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +2 -2
  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/auth/clerk-sdk/generated.js +2 -1
  8. package/dist/auth/cloudflare-access.d.ts +3 -1
  9. package/dist/auth/cloudflare-access.js +22 -11
  10. package/dist/auth/downstream-oauth.js +195 -270
  11. package/dist/auth/downstream-revocation.js +1 -1
  12. package/dist/auth/oauth-grant.js +11 -0
  13. package/dist/auth/oauth-token-response.js +1 -1
  14. package/dist/auth/vercel-agent.js +2 -1
  15. package/dist/auth-elicitation.js +2 -2
  16. package/dist/branding.d.ts +1 -15
  17. package/dist/branding.js +2 -2
  18. package/dist/bytes.js +42 -0
  19. package/dist/call-admission-context.js +2 -5
  20. package/dist/call-admission.js +5 -0
  21. package/dist/call-recovery.js +0 -3
  22. package/dist/catalog-cache.js +26 -7
  23. package/dist/catalog-fingerprint.js +2 -18
  24. package/dist/catalog-revision.js +9 -0
  25. package/dist/catalog-service.js +51 -20
  26. package/dist/config-defaults.d.ts +1 -0
  27. package/dist/config-defaults.js +1 -0
  28. package/dist/config-schema.d.ts +1 -1
  29. package/dist/config-schema.js +6 -2
  30. package/dist/config.d.ts +41 -14
  31. package/dist/config.js +21 -71
  32. package/dist/connector-caller.js +1 -1
  33. package/dist/connectors/api-connector.js +4 -3
  34. package/dist/connectors/guarded-fetch.d.ts +0 -6
  35. package/dist/connectors/guarded-fetch.js +1 -11
  36. package/dist/connectors/remote-mcp.js +93 -69
  37. package/dist/connectors/resource-uri.js +0 -4
  38. package/dist/credentials.js +3 -0
  39. package/dist/d1.d.ts +0 -89
  40. package/dist/d1.js +0 -34
  41. package/dist/downstream-input.js +2 -2
  42. package/dist/execute.js +161 -116
  43. package/dist/executor-result.js +2 -1
  44. package/dist/executors/quickjs-runtime.js +2 -25
  45. package/dist/executors/quickjs.js +1 -1
  46. package/dist/guest-runtime.js +5 -3
  47. package/dist/guest-types.d.ts +5 -5
  48. package/dist/http/client.js +1 -1
  49. package/dist/http/errors.js +2 -2
  50. package/dist/http/json-schema.js +1 -1
  51. package/dist/http/node-transport.js +0 -1
  52. package/dist/http/oauth.js +1 -1
  53. package/dist/http/paging.js +6 -9
  54. package/dist/http/provider.js +1 -1
  55. package/dist/http/safety.js +1 -1
  56. package/dist/http/services.js +1 -8
  57. package/dist/http/tools.js +1 -7
  58. package/dist/http/vendor.js +7 -7
  59. package/dist/identity.js +2 -6
  60. package/dist/index.js +1 -2
  61. package/dist/invocation-auth.js +2 -1
  62. package/dist/invocation.js +8 -2
  63. package/dist/meta-tools.js +27 -27
  64. package/dist/native-tools.js +2 -1
  65. package/dist/oauth-handoff.js +2 -2
  66. package/dist/operation-vocabulary.js +1 -1
  67. package/dist/operator-record.d.ts +8 -2
  68. package/dist/operator-record.js +18 -50
  69. package/dist/operator-ui/contract.d.ts +4 -2
  70. package/dist/operator-ui/generated.js +3 -3
  71. package/dist/operator-ui/model.d.ts +7 -2
  72. package/dist/program-writes.js +3 -8
  73. package/dist/provider-call.js +0 -7
  74. package/dist/provider.d.ts +1 -1
  75. package/dist/providers/_shared/google/kit.js +1 -6
  76. package/dist/providers/_shared/google/service-account.js +1 -3
  77. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  78. package/dist/providers/_shared/guide.js +3 -0
  79. package/dist/providers/_shared/json.js +12 -0
  80. package/dist/providers/_shared/rest/compact-index.js +1 -1
  81. package/dist/providers/_shared/rest/kit.js +1 -3
  82. package/dist/providers/_shared/rest/operation-index.js +1 -3
  83. package/dist/providers/_shared/rest/value-safety.js +26 -28
  84. package/dist/providers/breeze/api.js +2 -7
  85. package/dist/providers/breeze/index.js +4 -2
  86. package/dist/providers/breeze/tools.js +3 -4
  87. package/dist/providers/ccb/api.js +1 -6
  88. package/dist/providers/ccb/index.js +2 -1
  89. package/dist/providers/ccb/tools.js +1 -6
  90. package/dist/providers/cloudflare/api.js +2 -2
  91. package/dist/providers/cloudflare/index.js +2 -1
  92. package/dist/providers/cloudflare/safety.js +3 -3
  93. package/dist/providers/docs/tools.js +2 -10
  94. package/dist/providers/drive/tools.js +12 -25
  95. package/dist/providers/forms/tools.js +1 -9
  96. package/dist/providers/gmail/tools.js +5 -21
  97. package/dist/providers/overflow/api.js +1 -3
  98. package/dist/providers/overflow/index.js +2 -1
  99. package/dist/providers/overflow/tools.js +3 -7
  100. package/dist/providers/planning-center/api.js +2 -1
  101. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  102. package/dist/providers/planning-center/lib/json-api.js +1 -9
  103. package/dist/providers/planning-center/tools.js +2 -1
  104. package/dist/providers/sheets/tools.js +1 -9
  105. package/dist/providers/slides/tools.js +4 -13
  106. package/dist/providers/stripe/index.js +2 -1
  107. package/dist/providers/stripe/safety.js +2 -2
  108. package/dist/providers/tithely/api.js +2 -7
  109. package/dist/providers/tithely/index.js +3 -2
  110. package/dist/providers/tithely/tools.js +7 -5
  111. package/dist/providers/vercel/api.js +1 -6
  112. package/dist/providers/vercel/lib/pin.js +1 -1
  113. package/dist/providers/vercel/safety.js +1 -1
  114. package/dist/providers/vercel/tools.js +2 -1
  115. package/dist/redaction-marker.js +2 -2
  116. package/dist/registry.d.ts +4 -1
  117. package/dist/registry.js +35 -22
  118. package/dist/request-principal.js +15 -2
  119. package/dist/request-state.js +2 -2
  120. package/dist/routes/access-tokens.js +1 -1
  121. package/dist/routes/activity-disclosure.js +1 -1
  122. package/dist/routes/connect.js +2 -2
  123. package/dist/routes/credentials.js +1 -1
  124. package/dist/routes/dispatch.js +10 -0
  125. package/dist/routes/mcp.js +23 -21
  126. package/dist/routes/oauth-management.js +1 -1
  127. package/dist/routes/operator.js +53 -7
  128. package/dist/routes/shared.d.ts +8 -8
  129. package/dist/routes/shared.js +22 -15
  130. package/dist/routes/ui-config.js +6 -6
  131. package/dist/routes/ui-data.js +2 -2
  132. package/dist/routes/ui.js +9 -4
  133. package/dist/runtime/call-admission.js +1 -1
  134. package/dist/runtime/call.js +1 -1
  135. package/dist/runtime/settlement.js +1 -1
  136. package/dist/runtime/shared-read.js +2 -15
  137. package/dist/sent-secrets.d.ts +14 -6
  138. package/dist/sent-secrets.js +231 -85
  139. package/dist/server.js +4 -4
  140. package/dist/skills.js +8 -35
  141. package/dist/sqlite.d.ts +0 -19
  142. package/dist/sqlite.js +2 -83
  143. package/dist/storage/keys.js +29 -124
  144. package/dist/storage/sql-native.js +14 -111
  145. package/dist/storage/sql.d.ts +1 -24
  146. package/dist/storage/sql.js +1 -12
  147. package/dist/tool-error.js +4 -0
  148. package/dist/types.d.ts +4 -3
  149. package/dist/ui.js +3 -0
  150. package/dist/url-safety.js +0 -4
  151. package/dist/usage-guide.js +2 -2
  152. package/dist/version.d.ts +1 -1
  153. package/dist/version.js +1 -1
  154. package/dist/worker.js +1 -1
  155. package/documentation/README.md +6 -42
  156. package/documentation/architecture.md +190 -507
  157. package/documentation/auth.md +172 -58
  158. package/documentation/code-mode.md +98 -66
  159. package/documentation/deploying.md +19 -40
  160. package/documentation/integrating.md +8 -19
  161. package/documentation/meta-tools.md +23 -6
  162. package/documentation/migration-0.33.md +5 -5
  163. package/documentation/migration-0.34.md +2 -4
  164. package/documentation/migration-retired-layouts.md +74 -0
  165. package/documentation/operator-tests.md +19 -119
  166. package/documentation/operator-ui.md +46 -8
  167. package/documentation/provider-auth-migration.md +4 -10
  168. package/documentation/provider-migration-0.29.md +4 -15
  169. package/examples/worker/AGENTS.md +4 -6
  170. package/examples/worker/README.md +20 -115
  171. package/guides/connecting-agents.md +7 -30
  172. package/guides/how-it-works.md +2 -2
  173. package/guides/safety-and-trust.md +4 -1
  174. package/package.json +19 -19
  175. package/templates/node/README.md +9 -26
  176. package/templates/node/package.json +1 -1
  177. package/dist/auth/oauth-v2-migration.js +0 -135
  178. package/dist/provider-call-facts.d.ts +0 -115
  179. package/dist/provider-call.d.ts +0 -112
  180. package/dist/storage/d1-copy.js +0 -255
  181. package/dist/storage/sql-schema.d.ts +0 -6
  182. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  183. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
@@ -5,6 +5,7 @@ import { isMaintainer, isMember } from "../membership.js";
5
5
  import { identityStorageKey, validIdentityReference } from "../identity.js";
6
6
  import { assertsAgents, isAgentName } from "../agent-identity.js";
7
7
  import { authenticate } from "../auth-admission.js";
8
+ import { attachFailureFacts, carryFailureFacts, failureRecord, logFailure } from "../operator-record.js";
8
9
  export { msg } from "../errors.js";
9
10
  import { authCall, authFlow, authInvoke, authProgram, registerAuth } from "../auth/native.js";
10
11
  import { runEdge } from "../runtime/run.js";
@@ -87,17 +88,17 @@ const providerChallengeNative = /* @__PURE__ */ registerAuth(providerChallenge,
87
88
  return authFlow(function* () {
88
89
  const privateHeaders = new Headers(response.headers);
89
90
  privateHeaders.set("Cache-Control", "no-store");
90
- response = new Response(response.body, {
91
+ response = carryFailureFacts(response, new Response(response.body, {
91
92
  status: response.status,
92
93
  statusText: response.statusText,
93
94
  headers: privateHeaders,
94
- });
95
+ }));
95
96
  if (response.status !== 401)
96
97
  return response;
97
98
  if (!response.headers.has("WWW-Authenticate")) {
98
99
  const headers = new Headers(response.headers);
99
100
  headers.set("WWW-Authenticate", refusingProvider?.challenge?.(request, baseUrl) ?? "Bearer");
100
- response = new Response(response.body, { status: response.status, statusText: response.statusText, headers });
101
+ response = carryFailureFacts(response, new Response(response.body, { status: response.status, statusText: response.statusText, headers }));
101
102
  }
102
103
  const path = new URL(request.url).pathname;
103
104
  const pool = /^\/mcp\/([a-z0-9_-]+)$/.exec(path)?.[1];
@@ -113,7 +114,7 @@ const providerChallengeNative = /* @__PURE__ */ registerAuth(providerChallenge,
113
114
  return response;
114
115
  const headers = new Headers(response.headers);
115
116
  headers.set("WWW-Authenticate", challenge);
116
- return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
117
+ return carryFailureFacts(response, new Response(response.body, { status: response.status, statusText: response.statusText, headers }));
117
118
  });
118
119
  });
119
120
  function recognizedProvider(request, auth, runtimeContext) {
@@ -162,7 +163,7 @@ const authorizeNative = /* @__PURE__ */ registerAuth(authorize, function (reques
162
163
  return { ok: false, response: privateJson({ error: "authenticated user required" }, { status: 403 }) };
163
164
  }
164
165
  // Every caller of this function is a human route. Agents reach pools only at
165
- // `/agent/<pool>` (#837), so a workload or `cta_` credential refuses here
166
+ // `/agent/<pool>`, so a workload or `cta_` credential refuses here
166
167
  // without verification, and never falls back to a cookie, session or Access context.
167
168
  if (machineCredential) {
168
169
  return {
@@ -213,6 +214,12 @@ const authorizeNative = /* @__PURE__ */ registerAuth(authorize, function (reques
213
214
  if (!admission.admitted)
214
215
  return { ok: false, response: admission.response };
215
216
  const result = admission.result;
217
+ if (!result.ok && result.response.status === 503 && provider.kind === "cloudflare-access") {
218
+ logFailure(logger, "Cloudflare Access verification unavailable", failureRecord({
219
+ accessContext: Boolean(runtimeContext?.access),
220
+ authorizationHeader: credential.kind,
221
+ }, result.response));
222
+ }
216
223
  // Only built-in agent credential adapters assert an agent, and they never
217
224
  // answer here. Any `agent` claim refuses like an unknown token.
218
225
  const agent = result.ok ? result.agent : undefined;
@@ -272,10 +279,10 @@ const authorizeNative = /* @__PURE__ */ registerAuth(authorize, function (reques
272
279
  ok: false,
273
280
  response: (yield authCall(providerChallenge, [
274
281
  lastResponse ??
275
- privateJson({ error: "unauthorized" }, {
282
+ attachFailureFacts(privateJson({ error: "unauthorized" }, {
276
283
  status: 401,
277
284
  headers: { "WWW-Authenticate": "Bearer" },
278
- }),
285
+ }), { reason: "authentication_provider_missing" }),
279
286
  request,
280
287
  baseUrl,
281
288
  auth,
@@ -292,7 +299,7 @@ function agentChallenge() {
292
299
  };
293
300
  }
294
301
  /**
295
- * Admission at an agent endpoint, `/agent/<pool>` (#837). Only the built-in
302
+ * Admission at an agent endpoint, `/agent/<pool>`. Only the built-in
296
303
  * agent credential adapters are consulted: Clerk and Access never run, cookies and Clerk
297
304
  * handshake parameters are stripped, and the runtime context, which carries
298
305
  * Access's `ctx.access` even on a bypassed path, is never passed. Anything but
@@ -369,7 +376,7 @@ export function isSameOrigin(request, baseUrl) {
369
376
  return false;
370
377
  }
371
378
  }
372
- export function withSecurityHeaders(response, requestUrl, _path) {
379
+ export function withSecurityHeaders(response, requestUrl) {
373
380
  const headers = response.headers.get("Content-Type")?.toLowerCase().startsWith("text/html")
374
381
  ? htmlSecurityHeaders(response.headers)
375
382
  : new Headers(response.headers);
@@ -385,7 +392,7 @@ export function withSecurityHeaders(response, requestUrl, _path) {
385
392
  });
386
393
  }
387
394
  /**
388
- * Plain `/mcp` and every operator route admit a maintainer (#837). An open
395
+ * Plain `/mcp` and every operator route admit a maintainer. An open
389
396
  * deployment has no inbound auth and so no maintainers: its one anonymous
390
397
  * caller keeps the whole default endpoint, as the startup warning says.
391
398
  */
@@ -394,7 +401,7 @@ export function admitsMaintainer(opts, authz) {
394
401
  }
395
402
  /**
396
403
  * The declared pool `/mcp/<name>` serves to this person, or undefined for the
397
- * same 404. An agent is never admitted here, even when listed (#837).
404
+ * same 404. An agent is never admitted here, even when listed.
398
405
  */
399
406
  export function memberPool(opts, name, identity) {
400
407
  if (identity.agent !== undefined)
@@ -410,7 +417,7 @@ export function agentPool(opts, name, identity) {
410
417
  return pool?.members.some((member) => member.kind === "agent" && isMember(member, identity)) ? pool : undefined;
411
418
  }
412
419
  /**
413
- * The connectors of every pool this identity is a member of (#837): what a
420
+ * The connectors of every pool this identity is a member of: what a
414
421
  * pool member may ever manage, and only their personal ones among these.
415
422
  */
416
423
  export function memberConnectors(opts, identity) {
@@ -428,7 +435,7 @@ export function memberConnectors(opts, identity) {
428
435
  }
429
436
  /**
430
437
  * Whether this identity may start, finish, or remove a connector's auth
431
- * (#837). Only an interactive person ever may; an agent or any other machine
438
+ * Only an interactive person ever may; an agent or any other machine
432
439
  * credential manages nothing. Shared auth is deployment-wide, so it takes a
433
440
  * maintainer. A personal connector takes a stable principal that is a
434
441
  * maintainer, whose plain `/mcp` includes it, or a member of a pool that
@@ -447,12 +454,12 @@ export function mayManageConnector(opts, authz, connector) {
447
454
  const pools = memberConnectors(opts, identity);
448
455
  return pools === "all" || pools.has(connector.id);
449
456
  }
450
- /** Agent token list, issue, rotate, and revoke: an interactive maintainer only (#837). */
457
+ /** Agent token list, issue, rotate, and revoke: an interactive maintainer only. */
451
458
  export function managesAgentTokens(opts, authz) {
452
459
  return (authz.identity.interactive && authz.identity.agent === undefined && isMaintainer(opts.maintainers, authz.identity));
453
460
  }
454
461
  /**
455
- * Activity reads: a maintainer principal, interactive or not (#837). Members
462
+ * Activity reads: a maintainer principal, interactive or not. Members
456
463
  * and agents read none, and an open deployment has no maintainer.
457
464
  */
458
465
  export function readsActivity(opts, authz) {
@@ -23,7 +23,7 @@ import { closeScope } from "../runtime/connector-scope.js";
23
23
  import { withDeadlineEffect } from "../runtime/run.js";
24
24
  import { uiProblemFor } from "../ui.js";
25
25
  import { workspaceSubjectOf } from "../workspace-subject.js";
26
- import { authorized, refuse, scopeFor, serveOperator } from "./operator.js";
26
+ import { authorized, operatorViewerKey, refuse, scopeFor, serveOperator, } from "./operator.js";
27
27
  import { isMaintainer, isMember } from "../membership.js";
28
28
  import { admitsMaintainer, mayManageConnector, privateJson, readsActivity } from "./shared.js";
29
29
  const cachedMaintainers = new WeakSet();
@@ -338,7 +338,7 @@ function matrixFor(context, visible, rows, ownersVisible) {
338
338
  function maintainerRead(context, authz) {
339
339
  const { opts } = context;
340
340
  return Effect.gen(function* () {
341
- const registry = opts.registry.scoped(scopeFor(authz));
341
+ const registry = opts.registry.scoped(scopeFor(authz, opts.config.auth.length > 0));
342
342
  const selected = context.url.searchParams.get("connector");
343
343
  const visible = registry.listConnectors().filter((connector) => selected === null || connector.id === selected);
344
344
  if (selected !== null && visible.length === 0)
@@ -359,7 +359,7 @@ function maintainerRead(context, authz) {
359
359
  .map(([id, tools]) => [id, Object.fromEntries(Object.entries(tools).filter(([name]) => validName(name)))]));
360
360
  // A pool owner is a Workspace mailbox address. Only an authenticated
361
361
  // maintainer reads it; an open deployment's anonymous caller, which this
362
- // route admits, gets null, the same as an unset owner (#837).
362
+ // route admits, gets null, the same as an unset owner.
363
363
  const ownersVisible = opts.config.auth.length > 0 && isMaintainer(opts.maintainers, authz.identity);
364
364
  config.pools = config.pools.map((pool) => ({
365
365
  ...pool,
@@ -442,7 +442,7 @@ function maintainerRead(context, authz) {
442
442
  agents,
443
443
  matrix,
444
444
  connectors: [],
445
- you: { interactive: authz.identity.interactive },
445
+ you: { interactive: authz.identity.interactive, viewerKey: yield* operatorViewerKey(context, authz) },
446
446
  };
447
447
  return privateJson(contract);
448
448
  });
@@ -553,7 +553,7 @@ function memberRead(context, authz, pools) {
553
553
  return Effect.gen(function* () {
554
554
  const allIds = opts.registry.listConnectors().map((c) => c.id);
555
555
  const ids = new Set(allIds.filter((id) => pools.some(([, pool]) => servesConnector(pool, id))));
556
- const registry = opts.registry.scoped(scopeFor(authz, [...ids]));
556
+ const registry = opts.registry.scoped(scopeFor(authz, opts.config.auth.length > 0, [...ids]));
557
557
  const selected = context.url.searchParams.get("connector");
558
558
  const visible = registry
559
559
  .listConnectors()
@@ -583,7 +583,7 @@ function memberRead(context, authz, pools) {
583
583
  agents: [],
584
584
  matrix: [],
585
585
  connectors,
586
- you: { interactive: authz.identity.interactive },
586
+ you: { interactive: authz.identity.interactive, viewerKey: yield* operatorViewerKey(context, authz) },
587
587
  };
588
588
  return privateJson(contract);
589
589
  });
@@ -30,10 +30,10 @@ export function uiData(registry, baseUrl, options) {
30
30
  * connecta described the failure itself, that failure's typed facts. The
31
31
  * message is never logged, whatever it looks like: a connector's own
32
32
  * `status()` can quote a downstream error body, which can quote the
33
- * secret it just rejected (INV-6). An `ok` status is simply dropped.
33
+ * secret it just rejected (INV-6). Non-error `ok` and `per_caller` states are dropped.
34
34
  */
35
35
  const logStatus = (id, state, failure, requestScope) => {
36
- if (state === "ok")
36
+ if (state === "ok" || state === "per_caller")
37
37
  return;
38
38
  const logger = registry.contextFor(id, baseUrl, requestScope).logger;
39
39
  logFailure(logger, "operator status", failureRecord({ connector: id, state }, failure), state === "auth_required" ? "info" : "warn");
package/dist/routes/ui.js CHANGED
@@ -9,7 +9,7 @@ import { Effect } from "effect";
9
9
  import { CONNECTA_VERSION } from "../version.js";
10
10
  import { CONNECTA_FAVICON_ICO } from "../favicon.js";
11
11
  import { CONNECTA_FAVICON_SVG, operatorPageForPath, renderUiHtml } from "../ui.js";
12
- import { authorized, refuse, scopeFor, serveOperator, visibleRegistry, } from "./operator.js";
12
+ import { authorized, operatorViewerKey, refuse, scopeFor, serveOperator, visibleRegistry, } from "./operator.js";
13
13
  import { managesAgentTokens, mayManageConnector, privateJson, readsActivity } from "./shared.js";
14
14
  import { routeUiConfig, scanOperatorActivity } from "./ui-config.js";
15
15
  import { uiData } from "./ui-data.js";
@@ -144,7 +144,7 @@ function connectorDetail(context, id) {
144
144
  const connector = registry.getConnector(id);
145
145
  if (!connector)
146
146
  return yield* refuse("unknown connector", 404);
147
- const data = yield* uiData(opts.registry.scoped(scopeFor(authz, [connector.id])), baseUrl, {
147
+ const data = yield* uiData(opts.registry.scoped(scopeFor(authz, opts.config.auth.length > 0, [connector.id])), baseUrl, {
148
148
  serverInfo: opts.config.serverInfo,
149
149
  activityContext: operatorActivityContext(context, authz),
150
150
  credentialVault: opts.config.vault,
@@ -168,7 +168,11 @@ function connectorDetail(context, id) {
168
168
  signal: request.signal,
169
169
  timeoutError: new Error("Activity observation timed out."),
170
170
  }).pipe(Effect.catch(() => Effect.succeed("unavailable")));
171
- return privateJson({ ...detail, permissions: permissions(connector) });
171
+ return privateJson({
172
+ ...detail,
173
+ viewerKey: yield* operatorViewerKey(context, authz),
174
+ permissions: permissions(connector),
175
+ });
172
176
  });
173
177
  }
174
178
  /** The summary: every visible connector as "loading", with no probe at all. */
@@ -178,10 +182,11 @@ function summary(context) {
178
182
  const authz = yield* authorized(context);
179
183
  const registry = yield* visibleRegistry(context, authz);
180
184
  const { visible, mayManage, permissions, activityEnabled, credentialManagement } = operatorView(context, authz, registry);
181
- // Only maintainers reach here (#837); setup commands are offered for every
185
+ // Only maintainers reach here; setup commands are offered for every
182
186
  // declared pool, since they administer them all.
183
187
  const pools = [...opts.pools.keys()];
184
188
  return privateJson({
189
+ viewerKey: yield* operatorViewerKey(context, authz),
185
190
  ...(pools.length ? { pools } : {}),
186
191
  serverInfo: opts.config.serverInfo,
187
192
  connectaVersion: CONNECTA_VERSION,
@@ -7,7 +7,7 @@
7
7
  // block: the one place outside its instance methods that may read its private
8
8
  // bookkeeping, and one that emits nothing into the `.d.ts`.
9
9
  //
10
- // The two controllers stay separate on purpose (#453): request admission
10
+ // The two controllers stay separate on purpose: request admission
11
11
  // bounds executor work, this bounds calls to one downstream connector, and a
12
12
  // shared queue between them was refused.
13
13
  import { Effect } from "effect";
@@ -106,7 +106,7 @@ function ended() {
106
106
  return new ConnectorCallError("connector_call_failed", "This call has ended; a capability kept from it can no longer be used.", { retryable: false });
107
107
  }
108
108
  /** Build one call's services. Invocation calls this in the scoped attempt that owns the permit (B.1). */
109
- export function openCall(inputs) {
109
+ function openCall(inputs) {
110
110
  let open = true;
111
111
  const parentSignal = inputs.parent ? Context.get(inputs.parent.services, Signal) : undefined;
112
112
  const parents = [inputs.signal, parentSignal, inputs.requestSignal].filter((signal) => signal !== undefined);
@@ -10,7 +10,7 @@
10
10
  // it was created.") when the Worker's compatibility date is 2025-06-16 or
11
11
  // later, in releases since September 2026. The refusal throws out of the call
12
12
  // that completed the Deferred, failing the request that released a slot, and
13
- // the waiter never resumes (#1106). Awaiting a Promise around that wait is no
13
+ // the waiter never resumes. Awaiting a Promise around that wait is no
14
14
  // help: the fiber waiting on the Deferred inside the Promise is resumed in the
15
15
  // completing request just the same.
16
16
  //
@@ -1,18 +1,5 @@
1
- // One read that several readers in one request wait on, each on its own terms.
2
- //
3
- // Discovery and the registry both coalesce a connector's catalog read inside
4
- // one request, so a search, a describe, and the call after them cost one
5
- // downstream listing. The read used to run under whichever reader asked
6
- // first: its signal was the read's signal, so a short-deadline call that
7
- // happened to start the read failed a search that joined it with the call's
8
- // timeout, and a cancelled search cancelled a read others were still waiting
9
- // on (#571).
10
- //
11
- // So the read owns its signal. Each reader waits under its own cancellation,
12
- // and the read is cancelled only when no reader is left to want it. Nothing
13
- // here is shared across requests: a SharedRead lives in one request's map and
14
- // is waited on only by that request's fibers, which is why a plain count of
15
- // readers is enough and no edge is needed between them.
1
+ // Request-local catalog reads own their signal. Each reader waits under its own
2
+ // cancellation; the read is cancelled only when no reader remains (INV-7).
16
3
  import { Deferred, Effect, Exit } from "effect";
17
4
  import { forkRequest } from "./run.js";
18
5
  import { requestOwner } from "./request-scope.js";
@@ -47,6 +47,9 @@ export declare class SentSecrets {
47
47
  request(input: RequestInfo | URL, init?: RequestInit): void;
48
48
  /** Serialized wire text about to be returned, stored, or paged, under payload rules. */
49
49
  text(value: string): string;
50
+ /** An SDK response body, checked after it has been fully read so late registrations
51
+ * still apply. An idle set leaves the original serialization untouched. */
52
+ serializedAgentOutput(body: string, json: boolean): string;
50
53
  /** A plain-data snapshot of `value` with every match removed: own enumerable data properties
51
54
  * (an error's own fields, non-enumerable ones included), read once each. Accessors are never
52
55
  * invoked, except an error's stack and the accessors its class defines, read once into the
@@ -154,10 +157,16 @@ export declare class SentSecrets {
154
157
  * decoded views are matched together, as one lane between unmatched separators. Each decode
155
158
  * is paid for from the call's work budget; a part the budget cannot check is withheld. */
156
159
  private encodedLeaves;
157
- /** The parts' joined text, matched once, with each part's replacement. A tiny or short
158
- * private value equal to a whole part withholds it. A number or a serialized container that
159
- * a match touches is withheld whole. A bounded lane also loses an unfinished tail, and is
160
- * withheld whole when the set could not tell where one begins. */
160
+ /** Check replacements as returned, including their placeholder seams. Numbers touched by
161
+ * the first scan get one chance beside the replaced strings; every later hit withholds its
162
+ * entire part. Each changing round therefore removes at least one remaining part. Unchanged
163
+ * lanes pay for no retry. Large lanes recheck changed parts and the seams touching them; an
164
+ * unaffordable check withholds every part it read and all original retention candidates. */
165
+ private settleParts;
166
+ /** The parts' joined text, with each part's replacement. A touched number is checked again
167
+ * after string replacement before withholding it whole. Tiny or short whole private values,
168
+ * deep matches and unchecked runs still withhold changed numbers whole. A bounded lane also
169
+ * loses an unfinished tail, and withholds every touched number without a second check. */
161
170
  private safeParts;
162
171
  /** Leaves too large to match joined within the work budget: each is matched alone, in order,
163
172
  * and one that does not fit is withheld without spending what a smaller one needs. A run
@@ -215,7 +224,6 @@ export declare function agentOutputOperations<T extends Record<string, (...args:
215
224
  /** Slot reads cover custom handlers too, including keys put in query strings. A field named
216
225
  * as an identifier (an application or client ID, a username, a public key) is matched whole. */
217
226
  export declare function registerCredentialValues(secrets: SentSecrets, values: import("./types.js").ConnectorCredentialValues | null): void;
218
- export declare function trackCredentialReads(ctx: ConnectorContext): void;
219
227
  /** A connector's result or error as it enters: every connector's credentials and the
220
228
  * request's private arguments, under payload rules. */
221
229
  export declare function redactSentSecrets<T>(ctx: ConnectorContext, value: T): T;
@@ -226,7 +234,7 @@ export declare function requestSentSecrets(ctx: ConnectorContext): SentSecrets;
226
234
  * extracted from. Redact it whole first: a cut through an echoed form can leave a remnant
227
235
  * shorter than any fragment the output boundary matches, and a normalized number or
228
236
  * extracted substring may no longer hold the form at all. */
229
- export declare function redactBeforeCut<T>(ctx: ConnectorContext, value: T): T;
237
+ export declare const redactBeforeCut: typeof redactSentSecrets;
230
238
  /** Base64, or base64url with `url`, of bytes or UTF-8 text Connecta returns to an agent: a
231
239
  * download, an attachment, a REST body, a page cursor. Every such encoding goes through here,
232
240
  * since no later pass reads through an encoding the output does not declare: the content is