@zackbart/connecta 0.18.3 → 0.19.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 (69) hide show
  1. package/CHANGELOG.md +73 -4
  2. package/dist/catalog-service.d.ts +16 -13
  3. package/dist/catalog-service.js +106 -115
  4. package/dist/catalog.js +29 -46
  5. package/dist/connector-scope.js +2 -7
  6. package/dist/connectors/api.d.ts +4 -16
  7. package/dist/connectors/api.js +19 -46
  8. package/dist/connectors/guarded-fetch.d.ts +9 -23
  9. package/dist/connectors/guarded-fetch.js +38 -76
  10. package/dist/connectors/remote-mcp.js +36 -79
  11. package/dist/errors.d.ts +6 -27
  12. package/dist/errors.js +8 -5
  13. package/dist/execute.d.ts +24 -22
  14. package/dist/execute.js +98 -145
  15. package/dist/executor-result.d.ts +1 -0
  16. package/dist/executor-result.js +4 -11
  17. package/dist/executors/quickjs-child.js +1 -3
  18. package/dist/executors/quickjs-runtime.js +1 -3
  19. package/dist/executors/quickjs.js +1 -3
  20. package/dist/index.js +27 -57
  21. package/dist/invocation.js +113 -183
  22. package/dist/meta-tools.d.ts +15 -28
  23. package/dist/meta-tools.js +33 -89
  24. package/dist/providers/cloudflare.d.ts +2 -18
  25. package/dist/providers/cloudflare.js +1460 -2451
  26. package/dist/providers/linear.d.ts +4 -41
  27. package/dist/providers/linear.js +8 -39
  28. package/dist/providers/mixpanel.d.ts +3 -25
  29. package/dist/providers/mixpanel.js +7 -22
  30. package/dist/providers/notion.d.ts +1 -15
  31. package/dist/providers/notion.js +44 -173
  32. package/dist/providers/revenuecat.d.ts +4 -57
  33. package/dist/providers/revenuecat.js +10 -93
  34. package/dist/providers/stripe.d.ts +1 -12
  35. package/dist/providers/stripe.js +7 -45
  36. package/dist/registry.d.ts +9 -34
  37. package/dist/registry.js +9 -103
  38. package/dist/routes/mcp.js +1 -1
  39. package/dist/routes/oauth.js +3 -3
  40. package/dist/routes/shared.d.ts +15 -15
  41. package/dist/routes/shared.js +1 -3
  42. package/dist/timeout.d.ts +8 -7
  43. package/dist/timeout.js +47 -38
  44. package/dist/types.d.ts +3 -3
  45. package/dist/ui.d.ts +1 -25
  46. package/dist/ui.js +18 -45
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/documentation/call-admission.md +1 -1
  50. package/documentation/cloudflare.md +1 -1
  51. package/documentation/code-mode.md +7 -7
  52. package/documentation/connectors.md +24 -1
  53. package/documentation/linear.md +1 -1
  54. package/documentation/mixpanel.md +1 -1
  55. package/documentation/notion.md +1 -1
  56. package/documentation/operations.md +18 -14
  57. package/documentation/provider-conventions.md +1 -1
  58. package/documentation/revenuecat.md +1 -1
  59. package/documentation/stripe.md +1 -1
  60. package/documentation/upgrading.md +13 -4
  61. package/ethos.md +75 -121
  62. package/package.json +3 -4
  63. package/templates/node/package.json +1 -1
  64. package/documentation/code-first-exploration.md +0 -292
  65. package/documentation/mcp-2026-07-28.md +0 -46
  66. package/documentation/mcp-ui-design.md +0 -382
  67. package/documentation/program-ui-read-calls.md +0 -213
  68. package/documentation/provider-audit.md +0 -198
  69. package/documentation/rich-output-design.md +0 -211
@@ -186,7 +186,7 @@ async function serveMcp(request, opts, baseUrl, actor, registry, runtimeContext)
186
186
  // it — without this line no host reads execute_code's _meta.ui, no host
187
187
  // fetches the shell, and the whole design is inert. This is the one
188
188
  // extension connecta advertises; the versioned extensions framework
189
- // stays declined as a general surface (documentation/mcp-2026-07-28.md).
189
+ // stays declined as a general surface (https://github.com/zackbart/connecta/blob/main/records/mcp-2026-07-28.md).
190
190
  capabilities: {
191
191
  extensions: {
192
192
  [MCP_APPS_EXTENSION]: { mimeTypes: [PROGRAM_UI_MIME_TYPE] },
@@ -174,9 +174,9 @@ function html(body, status = 200, branding) {
174
174
  * list the flat 400 was meant to withhold. So zero-I/O refusals read the same
175
175
  * keys in the same `conn:<id>:` namespace, where an unconfigured id gets misses.
176
176
  *
177
- * This is deliberately *not* a constant-time claim, and documentation/connectors.md says
178
- * so in prose: a hit and a miss are not identical in a KV store, and a connector
179
- * shipping its own `verifyState` may do more or less work. What it
177
+ * This is deliberately *not* a constant-time claim: a hit and a miss are not
178
+ * identical in a KV store, and a connector shipping its own `verifyState` may
179
+ * do more or less work. What it
180
180
  * removes is the order-of-magnitude "no I/O versus a round trip" difference,
181
181
  * which is the only part of the signal that makes enumeration cheap.
182
182
  *
@@ -6,38 +6,39 @@ import type { DeferredWork } from "../connector-scope.js";
6
6
  import type { AdmissionController } from "../executor-admission.js";
7
7
  import type { Registry } from "../registry.js";
8
8
  import type { ConnectaBranding, Executor, InboundAuth, Logger } from "../types.js";
9
+ export { msg } from "../errors.js";
9
10
  export interface ServerOptions {
10
11
  registry: Registry;
11
12
  auth: InboundAuth[];
12
- publicUrl?: string;
13
+ publicUrl?: string | undefined;
13
14
  serverInfo: Implementation;
14
15
  logger: Logger;
15
- activity?: ActivityStore;
16
- activityReadGate?: ActivityReadGate;
17
- activityDeploymentId?: string;
18
- deploymentInfo?: Record<string, unknown>;
16
+ activity?: ActivityStore | undefined;
17
+ activityReadGate?: ActivityReadGate | undefined;
18
+ activityDeploymentId?: string | undefined;
19
+ deploymentInfo?: Record<string, unknown> | undefined;
19
20
  /** Deadline for call_tool/call_destructive_tool calls that pass no timeoutMs. Off when unset. */
20
- defaultToolTimeoutMs?: number;
21
+ defaultToolTimeoutMs?: number | undefined;
21
22
  /** Per-connector deadline for the search/describe probe fan-out. Default 30_000. */
22
- probeTimeoutMs?: number;
23
+ probeTimeoutMs?: number | undefined;
23
24
  /** Maximum simultaneous connector discovery operations. Default 4. */
24
- discoveryConcurrency?: number;
25
+ discoveryConcurrency?: number | undefined;
25
26
  /** Aggregate serialized-byte budget for connecta.emit per run. Default 4_000_000. */
26
- maxEmittedBytes?: number;
27
+ maxEmittedBytes?: number | undefined;
27
28
  /** Block-count budget for connecta.emit per run. Default 32. */
28
- maxEmittedBlocks?: number;
29
+ maxEmittedBlocks?: number | undefined;
29
30
  /** Required sandbox backing the execute_code meta-tool. */
30
31
  executor: Executor;
31
32
  /** Sanitized identity of the configured sandbox, when it has one. */
32
- executorName?: string;
33
+ executorName?: string | undefined;
33
34
  /** Global FIFO boundary for all non-preflight `/mcp` requests. */
34
35
  requestAdmission: AdmissionController;
35
36
  /** Encrypted connector-credential storage backing the Credentials page. */
36
- credentialVault?: CredentialVault;
37
+ credentialVault?: CredentialVault | undefined;
37
38
  /** Hashed deployment access tokens backing MCP admission and the Tokens page. */
38
- accessTokens?: AccessTokenManager;
39
+ accessTokens?: AccessTokenManager | undefined;
39
40
  /** Optional browser UI and OAuth result-page labels. */
40
- branding?: ConnectaBranding;
41
+ branding?: ConnectaBranding | undefined;
41
42
  }
42
43
  export interface RuntimeExecutionContext {
43
44
  waitUntil(promise: Promise<unknown>): void;
@@ -51,7 +52,6 @@ export interface RouteContext {
51
52
  defer: DeferredWork | undefined;
52
53
  runtimeContext: RuntimeExecutionContext | undefined;
53
54
  }
54
- export declare function msg(err: unknown): string;
55
55
  export declare function privateJson(body: unknown, init?: ResponseInit): Response;
56
56
  /**
57
57
  * Bounded, escaped form of a caller-influenced value (an identity id or OAuth
@@ -1,7 +1,5 @@
1
1
  import { operatorPageForPath } from "../ui.js";
2
- export function msg(err) {
3
- return err instanceof Error ? err.message : String(err);
4
- }
2
+ export { msg } from "../errors.js";
5
3
  export function privateJson(body, init = {}) {
6
4
  const headers = new Headers(init.headers);
7
5
  headers.set("Content-Type", "application/json");
package/dist/timeout.d.ts CHANGED
@@ -5,13 +5,14 @@
5
5
  export declare const DEFAULT_PROBE_TIMEOUT_MS = 30000;
6
6
  /** A finite, positive integer number of milliseconds, or undefined. */
7
7
  export declare function normalizeTimeoutMs(value: number | undefined): number | undefined;
8
- /**
9
- * Reject `promise` after `ms` if it has not settled, so one hung downstream
10
- * cannot stall a whole fan-out. This form bounds only the caller-facing wait;
11
- * use `withAbortableTimeout` when the operation accepts an AbortSignal and the
12
- * underlying work must stop too.
13
- */
14
- export declare function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T>;
8
+ /** Resolve after `ms`, or false when the caller aborts first. */
9
+ export declare function sleep(ms: number, signal?: AbortSignal): Promise<boolean>;
10
+ export interface DeadlineOptions {
11
+ timeoutMs?: number;
12
+ signal?: AbortSignal;
13
+ timeoutError: Error;
14
+ }
15
+ export declare function withDeadline<T>(operation: (signal: AbortSignal) => Promise<T>, options: DeadlineOptions): Promise<T>;
15
16
  /**
16
17
  * Give one operation a caller-facing deadline and the matching cancellation
17
18
  * signal. The timeout rejects with the stable, labelled error while aborting
package/dist/timeout.js CHANGED
@@ -12,24 +12,53 @@ export function normalizeTimeoutMs(value) {
12
12
  }
13
13
  return Math.max(1, Math.trunc(value));
14
14
  }
15
- /**
16
- * Reject `promise` after `ms` if it has not settled, so one hung downstream
17
- * cannot stall a whole fan-out. This form bounds only the caller-facing wait;
18
- * use `withAbortableTimeout` when the operation accepts an AbortSignal and the
19
- * underlying work must stop too.
20
- */
21
- export function withTimeout(promise, ms, label) {
22
- return new Promise((resolve, reject) => {
23
- const timer = setTimeout(() => {
24
- reject(new Error(`${label} timed out after ${ms}ms`));
25
- }, ms);
26
- promise.then((value) => {
15
+ /** Resolve after `ms`, or false when the caller aborts first. */
16
+ export function sleep(ms, signal) {
17
+ return new Promise((resolve) => {
18
+ let settled = false;
19
+ const finish = (value) => {
20
+ if (settled)
21
+ return;
22
+ settled = true;
27
23
  clearTimeout(timer);
24
+ signal?.removeEventListener("abort", cancel);
28
25
  resolve(value);
29
- }, (err) => {
26
+ };
27
+ const timer = setTimeout(() => finish(true), ms);
28
+ const cancel = () => finish(false);
29
+ signal?.addEventListener("abort", cancel, { once: true });
30
+ if (signal?.aborted)
31
+ cancel();
32
+ });
33
+ }
34
+ export function withDeadline(operation, options) {
35
+ const controller = new AbortController();
36
+ const forwardAbort = () => controller.abort(options.signal?.reason);
37
+ options.signal?.addEventListener("abort", forwardAbort, { once: true });
38
+ if (options.signal?.aborted)
39
+ forwardAbort();
40
+ let rejectAbort;
41
+ const aborted = new Promise((_, reject) => { rejectAbort = reject; });
42
+ const onAbort = () => rejectAbort(controller.signal.reason ?? options.timeoutError);
43
+ controller.signal.addEventListener("abort", onAbort, { once: true });
44
+ if (controller.signal.aborted)
45
+ onAbort();
46
+ const timer = options.timeoutMs === undefined
47
+ ? undefined
48
+ : setTimeout(() => controller.abort(options.timeoutError), options.timeoutMs);
49
+ let work;
50
+ try {
51
+ work = operation(controller.signal);
52
+ }
53
+ catch (error) {
54
+ work = Promise.reject(error);
55
+ }
56
+ return Promise.race([work, aborted]).finally(() => {
57
+ controller.abort();
58
+ if (timer)
30
59
  clearTimeout(timer);
31
- reject(err);
32
- });
60
+ controller.signal.removeEventListener("abort", onAbort);
61
+ options.signal?.removeEventListener("abort", forwardAbort);
33
62
  });
34
63
  }
35
64
  /**
@@ -38,28 +67,8 @@ export function withTimeout(promise, ms, label) {
38
67
  * any in-flight work that honors the signal.
39
68
  */
40
69
  export function withAbortableTimeout(operation, ms, label) {
41
- const controller = new AbortController();
42
- return new Promise((resolve, reject) => {
43
- const timeoutError = new Error(`${label} timed out after ${ms}ms`);
44
- const timer = setTimeout(() => {
45
- controller.abort(timeoutError);
46
- reject(timeoutError);
47
- }, ms);
48
- let promise;
49
- try {
50
- promise = operation(controller.signal);
51
- }
52
- catch (err) {
53
- clearTimeout(timer);
54
- reject(err);
55
- return;
56
- }
57
- promise.then((value) => {
58
- clearTimeout(timer);
59
- resolve(value);
60
- }, (err) => {
61
- clearTimeout(timer);
62
- reject(err);
63
- });
70
+ return withDeadline(operation, {
71
+ timeoutMs: ms,
72
+ timeoutError: new Error(`${label} timed out after ${ms}ms`),
64
73
  });
65
74
  }
package/dist/types.d.ts CHANGED
@@ -311,8 +311,8 @@ export interface Connector {
311
311
  * download link minted by one of the connector's tools. Called only after
312
312
  * every built-in route misses, so a connector can never shadow `/mcp`,
313
313
  * `/`, `/credentials`, `/activity`, `/health`, or the credential API. The
314
- * first connector to return a
315
- * Response wins, in registration order; return null to decline.
314
+ * first connector to return a Response wins, in registration order; return
315
+ * null to decline. See documentation/architecture.md route 9.
316
316
  *
317
317
  * These routes are PUBLIC: connecta applies no auth gate to them. A
318
318
  * connector that serves data here MUST authenticate the request itself — for
@@ -351,7 +351,7 @@ export interface ExecutorProvider {
351
351
  * Optional trusted sandbox-side setup run after provider globals exist.
352
352
  * Connecta uses this to install lazy connector namespace proxies without
353
353
  * materializing one host closure per tool. This is host-authored code, never
354
- * model input.
354
+ * model input. See documentation/code-mode.md#what-an-executor-must-implement.
355
355
  */
356
356
  prelude?: string;
357
357
  }
package/dist/ui.d.ts CHANGED
@@ -26,11 +26,6 @@ export declare function resolveBranding(branding?: ConnectaBranding): ResolvedBr
26
26
  * `resolveBranding` does — a warning helper must never throw.
27
27
  */
28
28
  export declare function droppedBrandingUrls(branding?: ConnectaBranding): string[];
29
- /**
30
- * True only for absolute `http:`/`https:` URLs. Downstream connectors control
31
- * their `authorizationUrl`, so a hostile/misconfigured one could hand back a
32
- * `javascript:` (or other) scheme; gate it before it can become an href.
33
- */
34
29
  export declare function isSafeHttpUrl(url: unknown): boolean;
35
30
  /**
36
31
  * True for values allowed in the page's `<link rel="icon" href>`: an absolute
@@ -52,26 +47,7 @@ export declare function isSafeHttpUrl(url: unknown): boolean;
52
47
  * on its own it would accept an authority that happened to equal the probe host.
53
48
  */
54
49
  export declare function isSafeIconHref(href: unknown): boolean;
55
- /**
56
- * True only for an absolute `https:` URL — the gate every `uiAuth` URL passes:
57
- * `frontendApiUrl`, which becomes the operator shell's sign-in loader source,
58
- * and `signInUrl`/`signUpUrl`, which ClerkJS uses as *navigation targets* when
59
- * the operator signs in. With those three gated, no operator-config value
60
- * reaches the browser in a URL position — attribute or navigation — without
61
- * validation, and there is no exception left to remember.
62
- *
63
- * Stricter than `isSafeHttpUrl` on purpose: no `http:` carve-out, no loopback
64
- * carve-out, and no relative form. Nobody types `frontendApiUrl` — the shipped
65
- * Clerk adapter derives it from the publishable key, and Clerk's Frontend API is
66
- * always https — and a cleartext script source on an operator page would be a
67
- * downgrade even where a browser's mixed-content rules had not already blocked
68
- * it. `signInUrl`/`signUpUrl` *are* typed by the operator, but what belongs
69
- * there is a hosted Account Portal address (`https://accounts.<domain>` or
70
- * `https://<slug>.accounts.dev`), which is https as well; `http:` would carry a
71
- * sign-in over cleartext, and a path relative to this origin is meaningless
72
- * because this server hosts no sign-in page of its own. So the looser gate would
73
- * buy nothing real, and the same strictness holds for all three.
74
- */
50
+ /** Absolute HTTPS gate for the `UiAuthConfig` URL fields documented in types.ts. */
75
51
  export declare function isSafeHttpsUrl(url: unknown): boolean;
76
52
  /**
77
53
  * Names of the `uiAuth` URLs an inbound-auth provider supplied that failed their
package/dist/ui.js CHANGED
@@ -95,17 +95,19 @@ export function droppedBrandingUrls(branding) {
95
95
  * their `authorizationUrl`, so a hostile/misconfigured one could hand back a
96
96
  * `javascript:` (or other) scheme; gate it before it can become an href.
97
97
  */
98
- export function isSafeHttpUrl(url) {
98
+ function safeUrl(url, schemes) {
99
99
  if (typeof url !== "string")
100
100
  return false;
101
101
  try {
102
- const scheme = new URL(url).protocol;
103
- return scheme === "http:" || scheme === "https:";
102
+ return schemes.includes(new URL(url).protocol);
104
103
  }
105
104
  catch {
106
105
  return false;
107
106
  }
108
107
  }
108
+ export function isSafeHttpUrl(url) {
109
+ return safeUrl(url, ["http:", "https:"]);
110
+ }
109
111
  /**
110
112
  * Only the second check's base; any origin works because the check is whether
111
113
  * the href stays on whatever origin it is resolved against. It is deliberately
@@ -149,35 +151,9 @@ export function isSafeIconHref(href) {
149
151
  return false;
150
152
  }
151
153
  }
152
- /**
153
- * True only for an absolute `https:` URL — the gate every `uiAuth` URL passes:
154
- * `frontendApiUrl`, which becomes the operator shell's sign-in loader source,
155
- * and `signInUrl`/`signUpUrl`, which ClerkJS uses as *navigation targets* when
156
- * the operator signs in. With those three gated, no operator-config value
157
- * reaches the browser in a URL position — attribute or navigation — without
158
- * validation, and there is no exception left to remember.
159
- *
160
- * Stricter than `isSafeHttpUrl` on purpose: no `http:` carve-out, no loopback
161
- * carve-out, and no relative form. Nobody types `frontendApiUrl` — the shipped
162
- * Clerk adapter derives it from the publishable key, and Clerk's Frontend API is
163
- * always https — and a cleartext script source on an operator page would be a
164
- * downgrade even where a browser's mixed-content rules had not already blocked
165
- * it. `signInUrl`/`signUpUrl` *are* typed by the operator, but what belongs
166
- * there is a hosted Account Portal address (`https://accounts.<domain>` or
167
- * `https://<slug>.accounts.dev`), which is https as well; `http:` would carry a
168
- * sign-in over cleartext, and a path relative to this origin is meaningless
169
- * because this server hosts no sign-in page of its own. So the looser gate would
170
- * buy nothing real, and the same strictness holds for all three.
171
- */
154
+ /** Absolute HTTPS gate for the `UiAuthConfig` URL fields documented in types.ts. */
172
155
  export function isSafeHttpsUrl(url) {
173
- if (typeof url !== "string")
174
- return false;
175
- try {
176
- return new URL(url).protocol === "https:";
177
- }
178
- catch {
179
- return false;
180
- }
156
+ return safeUrl(url, ["https:"]);
181
157
  }
182
158
  /**
183
159
  * Names of the `uiAuth` URLs an inbound-auth provider supplied that failed their
@@ -303,18 +279,21 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
303
279
  : {}),
304
280
  };
305
281
  });
282
+ const credentialCard = {
283
+ label: c.credential.label,
284
+ ...(c.credential.description
285
+ ? { description: c.credential.description }
286
+ : {}),
287
+ ...(c.credential.placeholder
288
+ ? { placeholder: c.credential.placeholder }
289
+ : {}),
290
+ };
306
291
  try {
307
292
  const metadata = await credentialVault.metadata(c.id);
308
293
  const fields = credentialFields(metadata);
309
294
  const shape = storedCredentialShape(c.credential, metadata?.fields ?? null);
310
295
  credential = {
311
- label: c.credential.label,
312
- ...(c.credential.description
313
- ? { description: c.credential.description }
314
- : {}),
315
- ...(c.credential.placeholder
316
- ? { placeholder: c.credential.placeholder }
317
- : {}),
296
+ ...credentialCard,
318
297
  ...(fields?.length ? { fields } : {}),
319
298
  configured: shape.state === "valid",
320
299
  removable: Boolean(metadata),
@@ -341,13 +320,7 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
341
320
  catch {
342
321
  const fields = credentialFields();
343
322
  credential = {
344
- label: c.credential.label,
345
- ...(c.credential.description
346
- ? { description: c.credential.description }
347
- : {}),
348
- ...(c.credential.placeholder
349
- ? { placeholder: c.credential.placeholder }
350
- : {}),
323
+ ...credentialCard,
351
324
  ...(fields?.length ? { fields } : {}),
352
325
  configured: false,
353
326
  removable: true,
package/dist/version.d.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export declare const CONNECTA_VERSION = "0.18.3";
7
+ export declare const CONNECTA_VERSION = "0.19.0";
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.18.3";
7
+ export const CONNECTA_VERSION = "0.19.0";
@@ -140,4 +140,4 @@ typed error code.
140
140
  | --- | --- |
141
141
  | Independent partitions, exact rolling-window reset and retry, queued cancellation charging no budget, synchronous cancel during partition derivation, validated values snapshotted rather than read from mutable config, bounded partition state and contained `partitionKey` failures, empty and multi-rule policies refused | `test/call-admission.test.ts` (controller) |
142
142
  | One base-registry limiter shared by direct and program calls, batch bounds with input order preserved, cancellation threading, no dispatch or retry or health poisoning after cancellation, short proactive windows retried without poisoning health, payload-free `/health` aggregates | `test/call-admission.test.ts` (integration, Node + Workers) |
143
- | Where provider budgets are allowed to come from at all | [provider conventions P12](./provider-conventions.md#p12--declare-an-admission-budget-only-when-the-provider-documents-a-number), [provider audit](./provider-audit.md) |
143
+ | Where provider budgets are allowed to come from at all | [provider conventions P12](./provider-conventions.md#p12--declare-an-admission-budget-only-when-the-provider-documents-a-number), [provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md) |
@@ -410,4 +410,4 @@ the window notices.
410
410
  This connection is audited against
411
411
  [the provider conventions](./provider-conventions.md). Its verdict per
412
412
  convention, including every recorded exception, is the Cloudflare section of
413
- [the provider audit](./provider-audit.md).
413
+ [the provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md).
@@ -5,14 +5,14 @@ is promised: what it can reach, what it gets back, how failures look, what it
5
5
  may retry, what bounds it runs under, and what its execution leaves behind in
6
6
  the activity surface. It is the interface a model actually programs against, so
7
7
  it is specified in prose first and implemented second — the same discipline the
8
- [MCP spec bump](./mcp-2026-07-28.md) followed.
8
+ [MCP spec bump](https://github.com/zackbart/connecta/blob/main/records/mcp-2026-07-28.md) followed.
9
9
 
10
10
  Two executors implement this document: QuickJS in a child process on Node, and
11
11
  `DynamicWorkerExecutor` from `@cloudflare/codemode` on Workers. Divergence between
12
12
  them is a bug unless it appears in [Executor exceptions](#executor-exceptions),
13
13
  which names the reason. Anyone can implement a third from this document alone.
14
14
 
15
- The [code-first exploration](./code-first-exploration.md) is the evidence behind
15
+ The [code-first exploration](https://github.com/zackbart/connecta/blob/main/records/code-first-exploration.md) is the evidence behind
16
16
  the direction; [`ethos.md`](../ethos.md) carries the verdicts. Where its prototype
17
17
  and this document disagree, this document wins. Clause identifiers (`A1`, `E3`, …)
18
18
  are stable and cited by the tests in [Verification](#verification).
@@ -173,7 +173,7 @@ word gets `_` appended (`my-service.get.thing` → `my_service.get_thing`). The
173
173
  globals are lazy: no catalog is fetched until a program touches one. The
174
174
  bounded deployment inventory in the `execute_code` description shows each
175
175
  canonical connector id and labels the shortcut only when it differs; the
176
- [discovery guide](./meta-tools.md#discovery-context) defines that bound.
176
+ [discovery guide](./meta-tools.md#discovery-context) defines that bound. The sugar is frozen: every expansion invents a collision class `A1` already solves ([#223](https://github.com/zackbart/connecta/issues/223)).
177
177
 
178
178
  **A3.** A shortcut that resolves to more than one tool fails closed with
179
179
  `ambiguous_tool_alias`, naming the colliding tool names and pointing at
@@ -426,7 +426,7 @@ on data nobody asked for.
426
426
 
427
427
  **R5.** `console.log`, `console.warn`, and `console.error` are captured in call order and returned as a single `logs` string, capped at 4,000 characters with a truncation marker. Logs survive failure — they ride along with the error result, which is what makes them worth writing. How a non-string argument renders is not contract (`X4`).
428
428
 
429
- **R6.** Nothing else is added to a normal program result. Passing `diagnostics: true` adds one request-local, payload-free `diagnostics` block; a program that emitted adds `emitted: N` and its blocks (`M2`). Omitted, `false`, and emit-free are byte-for-byte the ordinary response path.
429
+ **R6.** Nothing else is added to a normal program result. Passing `diagnostics: true` adds one request-local, payload-free `diagnostics` block; a program that emitted adds `emitted: N` and its blocks (`M2`). Omitted, `false`, and emit-free are byte-for-byte the ordinary response path. Diagnostics exist so catalog, connector, and executor costs are distinguishable without persisting payloads or charging normal responses ([#247](https://github.com/zackbart/connecta/issues/247)).
430
430
 
431
431
  **R7.** Timing separates admission, provider setup, total executor wall time, catalog work, and connector work. Catalog and connector values are cumulative, so parallel work can exceed executor wall time. Each used operation kind (`search`, `describe`, `call`, `batch`) gets one aggregate with count, failures, duration, returned serialized bytes, and catalog/connector time; batch adds only its total child count.
432
432
 
@@ -437,7 +437,7 @@ on data nobody asked for.
437
437
  MCP-native output a return value cannot carry: base64 is not projectable, so a
438
438
  block that survives intake uncapped (`S5`) must not die at the `R2` exit
439
439
  guard. The argument and the refused alternatives live in the
440
- [design record](./rich-output-design.md) and `ethos.md`
440
+ [design record](https://github.com/zackbart/connecta/blob/main/records/rich-output-design.md) and `ethos.md`
441
441
  ([#267](https://github.com/zackbart/connecta/issues/267),
442
442
  [#270](https://github.com/zackbart/connecta/issues/270)).
443
443
 
@@ -498,7 +498,7 @@ one MCP Apps view per successful run, assembled where composition already
498
498
  happens. Programs supply HTML content and nothing else — the only `ui://` URI in
499
499
  the system is connecta's build-time shell, so nothing a client could dereference
500
500
  is derived from anything a program said. The argument, the refused shapes, and
501
- the security posture live in the [design record](./mcp-ui-design.md)
501
+ the security posture live in the [design record](https://github.com/zackbart/connecta/blob/main/records/mcp-ui-design.md)
502
502
  ([#266](https://github.com/zackbart/connecta/issues/266),
503
503
  [#277](https://github.com/zackbart/connecta/issues/277)); this section is the
504
504
  contract, and it wins where the two disagree.
@@ -599,7 +599,7 @@ seven-tool surface, guest API, catalog, Apps delivery, and runtime do not change
599
599
  ([#286](https://github.com/zackbart/connecta/issues/286),
600
600
  [#418](https://github.com/zackbart/connecta/issues/418)).
601
601
 
602
- Bounded view reads follow normative [`V1`–`V8`](./program-ui-read-calls.md) ([#287](https://github.com/zackbart/connecta/issues/287), [#289](https://github.com/zackbart/connecta/issues/289)).
602
+ Bounded view reads follow normative [`V1`–`V8`](https://github.com/zackbart/connecta/blob/main/records/program-ui-read-calls.md) ([#287](https://github.com/zackbart/connecta/issues/287), [#289](https://github.com/zackbart/connecta/issues/289)).
603
603
 
604
604
  ## Retry semantics
605
605
 
@@ -160,6 +160,29 @@ is that machinery extracted once ([#341](https://github.com/zackbart/connecta/is
160
160
  one `guardedFetch({ provider, baseUrl, headers, maxResponseBytes, authenticate })`
161
161
  factory returning the transport a connector sends every request through.
162
162
 
163
+ ```ts
164
+ const send = guardedFetch({
165
+ provider: "Billing",
166
+ baseUrl: "https://billing.internal.example/v1",
167
+ maxResponseBytes: 4 * 1024 * 1024,
168
+ headers: { Accept: "application/json" },
169
+ authenticate: async (ctx) => {
170
+ const token = await ctx.credential?.get();
171
+ if (!token) throw new ConnectorCallError("auth_required", "...");
172
+ return { Authorization: `Bearer ${token}` };
173
+ },
174
+ });
175
+
176
+ const invoice = await send(
177
+ { method: "GET", path: `/invoices/${id}` },
178
+ ctx,
179
+ (response) => {
180
+ if (!response.ok) throw billingFailure(response.status);
181
+ return response.json();
182
+ },
183
+ );
184
+ ```
185
+
163
186
  What it owns is mechanical and provider-independent:
164
187
 
165
188
  - **Confinement.** A request path is provider-relative, carries no query or
@@ -212,7 +235,7 @@ supplies the third caller that would settle it.
212
235
  ## MCP version skew
213
236
 
214
237
  Connecta deliberately sits between protocol generations
215
- ([full revision inventory](./mcp-2026-07-28.md)):
238
+ ([full revision inventory](https://github.com/zackbart/connecta/blob/main/records/mcp-2026-07-28.md)):
216
239
 
217
240
  - **Inbound:** `/mcp` serves both the 2026-07-28 revision and legacy 2025
218
241
  clients. Modern clients negotiate with `server/discover` and do not send
@@ -179,4 +179,4 @@ settings at construction.
179
179
  This connection is audited against
180
180
  [the provider conventions](./provider-conventions.md). Its verdict per
181
181
  convention, including every recorded exception, is the Linear section of
182
- [the provider audit](./provider-audit.md).
182
+ [the provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md).
@@ -145,4 +145,4 @@ either way and still needs restrained use.
145
145
  This connection is audited against
146
146
  [the provider conventions](./provider-conventions.md). Its verdict per
147
147
  convention, including every recorded exception, is the Mixpanel section of
148
- [the provider audit](./provider-audit.md).
148
+ [the provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md).
@@ -268,4 +268,4 @@ from this connection, not hidden behind a generic call.
268
268
  This connection is audited against
269
269
  [the provider conventions](./provider-conventions.md). Its verdict per
270
270
  convention, including every recorded exception, is the Notion section of
271
- [the provider audit](./provider-audit.md).
271
+ [the provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md).