@zackbart/connecta 0.22.3 → 0.23.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.
package/dist/skills.js CHANGED
@@ -1,59 +1,58 @@
1
- export const CONNECTA_INSTRUCTIONS = 'Choose a route before discovery. A known-address read needs only call_tool. Unknown-address read-only work starts with one execute_code program that discovers, calls, and returns the answer; use the same route for reduction, multiple or dependent calls, loops, joins, or branches. Only readOnlyHint: true tools run there. Keep catalog inspection and unannotated, write-capable, or destructive work top level: search_tools then call_destructive_tool when a call is needed. After auth_required use authorize_connector. After a truncated direct result use get_result. connecta.ui(html) exists only inside execute_code, not in connector search; return the same summary data the HTML renders. Guidance is on demand: fetch skills({ name: "usage" }) only when these instructions and the tool description are insufficient or a run needs repair.';
1
+ export const CONNECTA_INSTRUCTIONS = 'Choose a route before discovery. A known-address read needs only call_tool. Unknown-address read-only work starts with execute_code to discover, call, and return the answer; use the same route for reduction, multiple or dependent calls, loops, joins, or branches. Keep discovery and calls together when schemas suffice; do not return catalog matches alone. Inspect unfamiliar result shapes with a small sample before proceeding. Only readOnlyHint: true tools run there. Keep catalog inspection and unannotated, write-capable, or destructive work top level: search_tools then call_destructive_tool when a call is needed. After auth_required use authorize_connector. After a truncated direct result use get_result. Guidance is on demand: fetch skills({ name: "usage" }) only when these instructions and the tool description are insufficient or a run needs repair.';
2
2
  const USAGE_SKILL_BASE = `# Connecta usage
3
3
 
4
4
  ## The surface
5
5
 
6
6
  Seven tools: \`execute_code\`, \`search_tools\`, \`call_tool\`, \`call_destructive_tool\`, \`authorize_connector\`, \`get_result\`, \`skills\`. Read-only discovery and multi-call work live in a program. Top-level search remains for catalog inspection and approval-required work.
7
7
 
8
- The always-loaded MCP instructions are authoritative for choosing the top-level route. Read this skill at most once, and only when their program workflow is insufficient or a run needs repair.
8
+ Follow the MCP instructions for routing. Read at most once for syntax or repair.
9
9
 
10
10
  ## Inside a program
11
11
 
12
- Write one plain-JavaScript async arrow function. TypeScript syntax and portable imports do not work. Return JSON-shaped data and reduce large results before returning.
12
+ Write one plain-JavaScript async arrow function, without TypeScript or imports. Return reduced JSON-shaped data.
13
13
 
14
14
  The minimum guest API is:
15
15
 
16
- - \`<connectorId>.<toolName>(args)\` calls a sanitized shortcut. Non-identifier characters become \`_\`; leading digits gain \`_\`; reserved words gain a trailing \`_\`.
17
16
  - \`connecta.call("connector.tool", args)\` uses the canonical address and returns the unwrapped value.
18
17
  - \`connecta.search(args)\` returns \`{ tools, total, offset, limit, hasMore }\`; \`connecta.describe(args)\` returns \`{ tools }\`.
19
- - \`connecta.batch(calls)\` runs 2–10 independent calls. Each outcome is \`{ address, ok: true, data }\` or \`{ address, ok: false, error, errorDetails }\`.
20
- - \`console.log(...)\` is captured. \`connecta.emit(block)\` and \`connecta.ui(html)\` produce rich output.
18
+ - Use \`Promise.all\` for independent calls, or \`Promise.allSettled\` to keep successes and failures.
19
+ - \`console.log(...)\` is captured. \`connecta.emit(block)\` produces rich output.
21
20
 
22
21
  ## Discover and select
23
22
 
24
- Search inside the run and finish the task there. A discovery-only program wastes a round trip. Use 2–4 distinctive action/object terms, not the full request. Use separate short searches for distinct operations.
23
+ Search and call in one run when schemas suffice. For unfamiliar result formats, return a small sample and continue; do not guess a parser. Use 2–4 distinctive action/object terms; search distinct operations separately.
25
24
 
26
- For top-level catalog inspection or approval-required discovery, omit \`limit\` initially (the default is 10), then page with a limit up to 50 if needed. Empty or whitespace-only queries browse all tools. A non-empty query with no ASCII terms returns no matches; mixed input searches with its ASCII terms. \`includeSchemas: "compact"\` adds bounded input and available output shapes. An observed shape carries \`outputSchemaSource: "observed"\`; treat it as routing evidence rather than a provider contract. Plain objects expose \`inputKeys\`, \`requiredInputKeys\`, and \`outputKeys\`; truncation flags mark incomplete shapes; matches also carry declared annotations.
25
+ For top-level catalog inspection or approval-required discovery, omit \`limit\` initially (the default is 8), then page with a limit up to 50 if needed. Empty or whitespace-only queries browse all tools. A non-empty query with no ASCII terms returns no matches; mixed input searches with its ASCII terms. \`includeSchemas: "compact"\` adds bounded input and available output shapes. An \`outputSchemaSource: "observed"\` shape is a hint, not a contract. Plain objects expose \`inputKeys\`, \`requiredInputKeys\`, and \`outputKeys\`; truncation flags mark incomplete shapes; matches also carry declared annotations.
27
26
 
28
27
  - \`connecta.search({})\` loads all catalogs. Pass \`connector: "<id>"\` when the integration is obvious. Use \`safety: "readOnly"\` for program calls. These inputs filter discovery; they grant no authority.
29
- - Request \`includeSchemas: "compact"\`. Check address, purpose, annotations, required inputs, truncation, safety, and available outputs. Never select only because a result ranks first or has fewer required inputs.
28
+ - Use \`includeSchemas: "json"\` for programmatic schema inspection; compact schemas are text, not objects with \`.properties\`. Check connectorTitle for the account/environment, then address, purpose, annotations, inputs, and outputs. Never select only because a result ranks first or has fewer required inputs.
30
29
  - Supply every \`requiredInputKey\` from the task or a prior result. For dependencies, match the earlier \`outputKey\` to the later required key. An empty required-key list does not permit invented arguments. Missing \`outputKeys\` means inspect \`outputSchema\`.
31
30
  - Use \`connecta.describe({ address })\` or \`{ addresses }\` when a compact schema is truncated or insufficient. Use \`format: "json"\` only for exact constraints. Write the property names the schema displays; never guess positions or aliases.
32
- - Reduce through available output keys. Treat an observed key as a hint, since later results may omit it or add others. Do not guess collection roots such as \`items\` or \`results\`. If a match or result key is missing, inspect, re-search, or describe inside the same run instead of returning discovery for another call.
31
+ - Reduce through available output keys. Treat an observed key as a hint, since later results may omit it or add others. Do not guess collection roots such as \`items\` or \`results\`. If a match is missing, re-search or describe. If a result shape is unclear, return a small sample for inspection before continuing.
33
32
  - Match provider identifiers and names exactly after resolving them from source data or a connector guide. A broad regular expression that merely finds a plausible value is not identity resolution.
34
- - Preserve the schema's JSON types exactly: a numeric id is a number, not a numeric-looking string. Call the search and describe functions directly; batch accepts canonical connector tool addresses, not guest API function names.
33
+ - Preserve the schema's JSON types exactly: a numeric id is a number, not a numeric-looking string.
35
34
  - Validate tabular headers, row arrays, and row widths before mapping them. Never let a header or partial row become data.
36
35
 
37
36
  Only tools explicitly annotated \`readOnlyHint: true\` are reachable. The catalog, credential, admission, and read-only gates run below the sandbox; code cannot widen its authority.
38
37
 
39
38
  ## Errors and repair
40
39
 
41
- Caught Connecta errors expose \`message\`, \`code\`, \`retryable\`, and \`details\`. Batch failures expose the same classification in \`errorDetails\`. Branch on fields, never prose. Do not retry \`retryable: false\`, and do not retry \`rate_limited\` immediately because portable code has no timer.
40
+ Caught Connecta errors expose \`message\`, \`code\`, \`retryable\`, and \`details\`. Promise rejections retain these fields. Branch on fields, never prose. After a shared argument failure, repair one call before repeating it across other records. Do not retry \`retryable: false\`, and do not retry \`rate_limited\` immediately because portable code has no timer.
42
41
 
43
42
  - \`destructive_tool_requires_approval\`: stop the program and use the returned canonical address with top-level \`call_destructive_tool\`.
44
43
  - \`auth_required\`: let the failure reach the model, then use top-level \`authorize_connector\`, give its handoff to the operator, and retry after recovery.
45
44
  - A truncated direct-call result: follow its \`get_result\` action. A truncated program result has no page handle; filter, map, or slice inside a new program.
46
45
  - Unknown addresses and tools carry scoped search recovery. Use it inside the current run. Do not invent an address.
47
46
 
48
- For a direct call, \`resultMode: "value"\` unwraps the result. \`timeoutMs\` sets its deadline. \`maxRetries\` is honored only for safely annotated tools. \`diagnostics: true\` adds timing.
47
+ For a direct call, \`resultMode: "value"\` unwraps the result. \`timeoutMs\` sets its deadline. Every call makes one attempt; use the returned error classification and retry hint to decide whether to reissue. \`diagnostics: true\` adds timing.
49
48
 
50
49
  \`get_result({ id, offset?, maxBytes? })\` returns \`{ text, offset, nextOffset?, totalBytes }\` for a direct-call result. Both sizes are byte counts: \`maxBytes\` must be a whole number at least 1 and defaults to the deployment cap; \`offset\` must be a whole number at least 0 and defaults to 0. An offset inside a multi-byte character moves back to its first byte, and the response reports the served offset. Follow \`nextOffset\` to reassemble pages. An unknown or expired id is an error.
51
50
 
52
- Limits: 20 host calls per run, 10 calls per batch, and a 15-second deadline per host call.
51
+ Limits: 20 host calls per run and a 15-second deadline per host call.
53
52
 
54
53
  ## Runtime portability
55
54
 
56
- Portable code uses only connector globals, \`connecta\`, and \`console.*\`. QuickJS blocks imports and lacks fetch, process, timers, crypto, and WebSocket. Dynamic Workers must use only \`{ loader }\`; bindings, modules, or globalOutbound grant ambient authority. With loader only, environment maps are empty; node:fs/http/https are absent; outbound fetch, WebSocket, node:net, and node:tls are denied; DNS is unresolved. Runtime builtins remain through \`import()\` and \`process.getBuiltinModule()\`, including node:path and cloudflare:workers; this set can drift. Timers, process, crypto, WebSocket, and data: fetch remain. Avoid every runtime-only capability because QuickJS fails.
55
+ Portable code uses standard JavaScript builtins, \`connecta\`, and \`console.*\`. QuickJS blocks imports and lacks fetch, process, timers, crypto, and WebSocket. Dynamic Workers must use only \`{ loader }\`; bindings, modules, or globalOutbound grant ambient authority. With loader only, environment maps are empty; node:fs/http/https are absent; outbound fetch, WebSocket, node:net, and node:tls are denied; DNS is unresolved. Runtime builtins remain through \`import()\` and \`process.getBuiltinModule()\`, including node:path and cloudflare:workers; this set can drift. Timers, process, crypto, WebSocket, and data: fetch remain. Avoid every runtime-only capability because QuickJS fails.
57
56
 
58
57
  ## Examples
59
58
 
@@ -61,13 +60,29 @@ One read-only call at a known address:
61
60
 
62
61
  \`async () => await connecta.call("crm.get_account", { id: "acct_42" })\`
63
62
 
64
- Dependent calls, only when the second needs a value from the first:
63
+ Dependent lookup: verify connector \`ci\`, run 42, and these schema fields first.
65
64
 
66
- \`async () => { const { tools } = await connecta.search({ query: "pipeline run job logs", safety: "readOnly", includeSchemas: "compact" }); const address = (suffix) => { const tool = tools.find((entry) => entry.address.endsWith(suffix)); if (!tool) throw new Error("missing " + suffix); return tool.address; }; const run = await connecta.call(address(".get_run"), { runId: 42 }); const logs = await connecta.call(address(".get_job_logs"), { jobId: run.failedJobId }); return logs.map(({ timestamp, message }) => ({ timestamp, message })); }\`
65
+ \`\`\`js
66
+ async () => {
67
+ const find = async name => {
68
+ const page = await connecta.search({ connector: "ci", query: name, safety: "readOnly", includeSchemas: "compact" });
69
+ if (page.queryAnalysis?.catalogError) throw new Error(JSON.stringify(page.queryAnalysis.catalogError));
70
+ return page.tools.find(tool => tool.name === name);
71
+ };
72
+ const runTool = await find("get_run");
73
+ if (!runTool) return { gap: "Run lookup not resolved" };
74
+ const run = await connecta.call(runTool.address, { runId: 42 });
75
+ if (!run.failedJobId) return { status: run.status, gap: "No failed job identified" };
76
+ const logsTool = await find("get_job_logs");
77
+ if (!logsTool) return { status: run.status, gap: "Job logs not resolved" };
78
+ const logs = await connecta.call(logsTool.address, { jobId: run.failedJobId });
79
+ return logs.filter(row => row.level === "error").map(({ timestamp, message }) => ({ timestamp, message }));
80
+ }
81
+ \`\`\`
67
82
 
68
- ## Rendering a view
83
+ ## Media output
69
84
 
70
- \`connecta.emit\` accepts text, image, or audio blocks and delivers them only on success. \`connecta.ui(html)\` renders one success-only display view outside model context. The view has local HTML and JavaScript only: no network, connector calls, discovery, conversation messages, or host links. One shared budget applies to the UI and emitted content, not separate budgets. Fetch and check the data shape first. On empty or missing data, return a trimmed first record instead of rendering. Otherwise render returned variables and return the same summary because the model reads the return value, not the view. A second, invalid, or over-budget UI call throws catchably.
85
+ \`connecta.emit\` accepts text, image, or audio blocks and delivers them only on success. Its byte and block budgets are separate from the JSON return budget. Return data for the client to render as a view.
71
86
 
72
87
  `;
73
88
  /** Deployment-scoped guide routing appended to the shared usage guide. */
@@ -95,12 +110,33 @@ export function hasConnectorGuides(connectors) {
95
110
  function usageSkill(_connectors) {
96
111
  return USAGE_SKILL;
97
112
  }
113
+ const INVESTIGATE_SKILL = `# Investigate across services
114
+
115
+ ## Plan the investigation
116
+
117
+ Start from the user's question and the evidence that would answer it. Select the app, account, and environment from connector titles, purposes, and relevant guides before looking up records. Reuse verified ids within the task; never carry an id across connectors just because its name matches.
118
+
119
+ - Purchase verification: resolve the same customer and environment across payment, subscription access, and analytics. Check each separately; a recorded payment does not prove access, and a missing analytics event does not prove payment failure.
120
+ - Experiment checks: confirm the project, experiment, time window, and exposure population before comparing outcomes. Return the requested comparison and any missing evidence; do not expand into an unrelated analytics audit.
121
+ - Customer or deployment investigations: locate the exact customer or deployment first, then follow only the records needed to explain the reported symptom. Use provider links or ids so the answer can be checked.
122
+
123
+ Establish capability limits early. A partial search is not proof that a tool is absent, and an unavailable catalog is not an empty dataset. Try a scoped search for the missing operation, inspect its guide when relevant, and distinguish unsupported work from missing data or authorization. If the required evidence is unavailable, return what was verified and the specific gap instead of approximating a different question through repeated calls.
124
+
125
+ A host transport error that requires reconnecting this MCP server cannot be repaired by a downstream tool. Reconnect in the host; do not repeatedly call \`authorize_connector\` through the failed connection.
126
+
127
+ Return the answer first, then the evidence and any unresolved gap. Include the app/environment, time window, and source ids or links needed to check it. Separate observed facts from inferences. Stop when the requested evidence is sufficient.
128
+ `;
98
129
  const AVAILABLE_SKILLS = [
99
130
  {
100
131
  name: "usage",
101
132
  description: "How to route work between one execute_code program and Connecta's explicit call, authorization, and result tools.",
102
133
  content: usageSkill,
103
134
  },
135
+ {
136
+ name: "investigate",
137
+ description: "Plan purchase verification, experiment checks, and customer or deployment investigations across services; resolve scope and capability limits before querying.",
138
+ content: () => INVESTIGATE_SKILL,
139
+ },
104
140
  ];
105
141
  /**
106
142
  * Namespace for operator-authored per-connector guides. Built-in skill names
package/dist/types.d.ts CHANGED
@@ -312,19 +312,6 @@ export interface Connector {
312
312
  * server's RFC 9207 `iss` response parameter for SDK validation.
313
313
  */
314
314
  finishAuth?(code: string, ctx: ConnectorContext, callbackParams?: URLSearchParams): Promise<void>;
315
- /**
316
- * Optional: serve a connector-owned HTTP route — for example a signed
317
- * download link minted by one of the connector's tools. Called only after
318
- * every built-in route misses, so a connector can never shadow `/mcp`,
319
- * `/`, `/credentials`, `/activity`, `/health`, or the credential API. The
320
- * first connector to return a Response wins, in registration order; return
321
- * null to decline. See documentation/architecture.md route 9.
322
- *
323
- * These routes are PUBLIC: connecta applies no auth gate to them. A
324
- * connector that serves data here MUST authenticate the request itself — for
325
- * example with a signed capability token in the URL.
326
- */
327
- handleRequest?(request: Request, ctx: ConnectorContext): Promise<Response | null>;
328
315
  }
329
316
  export interface ConnectorUsageGuide {
330
317
  /** Markdown returned verbatim by `skills({ name: "connector:<id>" })`. */
@@ -355,8 +342,7 @@ export interface ExecutorProvider {
355
342
  fns: Record<string, (...args: unknown[]) => Promise<unknown>>;
356
343
  /**
357
344
  * Optional trusted sandbox-side setup run after provider globals exist.
358
- * Connecta uses this to install lazy connector namespace proxies without
359
- * materializing one host closure per tool. This is host-authored code, never
345
+ * Connecta uses this to restore typed host errors. This is host-authored code, never
360
346
  * model input. See documentation/code-mode.md#what-an-executor-must-implement.
361
347
  */
362
348
  prelude?: string;
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.22.3";
7
+ export declare const CONNECTA_VERSION = "0.23.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.22.3";
7
+ export const CONNECTA_VERSION = "0.23.0";
@@ -71,11 +71,10 @@ read top to bottom.
71
71
  | 3 | `/.well-known/*` | Auth providers' `handleMetadata`, open. 404 when none handles it. |
72
72
  | 4 | `/health` | Open JSON: status, connector count, `serverInfo`, the configured executor's sanitized name when it has one, catalog-drift counts, admission snapshots, reserved route names, and `deployment` when `deploymentInfo` is set. Payload-free by construction, and it never joins the MCP queue. |
73
73
  | 5 | `/oauth/callback/<connectorId>` | Downstream-OAuth completion, open, `verifyState` before `finishAuth`. Personal flows first resolve the short-lived state hash to the principal partition. |
74
- | 6 | `/favicon.*`, `/ui` → `/`, the operator shells, `/ui/data` | The operator surface ([operator UI](./operator-ui.md)). The shells are open and data-free; `/ui/data` behind them is gated. Built-ins are matched before connector routes, so a connector cannot shadow a page. |
74
+ | 6 | `/favicon.*`, `/ui` → `/`, the operator shells, `/ui/data` | The operator surface ([operator UI](./operator-ui.md)). The shells are open and data-free; `/ui/data` behind them is gated. |
75
75
  | 7 | `/ui/activity` | Gated, plus the optional `activity.readGate`. `GET` only; 404 with no `activity.store.list`. |
76
76
  | 8 | `/mcp` | **Admission before auth**, then the auth gate, then a fresh MCP server. |
77
- | 9 | connector `handleRequest` | Registration order, open. Dispatched only after every built-in misses, so a connector can *add* a route and never shadow one of connecta's. First non-null response wins; a throw is a 500, not a fall-through. |
78
- | 10 | — | 404. |
77
+ | 9 | | 404. Custom HTTP routes belong to the deployment. |
79
78
 
80
79
  Every response leaves through `withSecurityHeaders`: `nosniff`, a no-referrer
81
80
  policy, HSTS on HTTPS, and — on the operator shells — a nonce-based script CSP
@@ -83,7 +82,7 @@ and framing denial. `test/server-route-contracts.test.ts` pins this ordering
83
82
  and the exact refusal bodies; it exists because the ordering is invisible in
84
83
  any one file and a reordering reads like a harmless refactor.
85
84
 
86
- `/mcp` itself is four steps, in this order and for these reasons:
85
+ `/mcp` itself is five steps, in this order and for these reasons:
87
86
 
88
87
  1. **Admit.** One permit from the deployment-wide FIFO pool, taken before auth
89
88
  so an unauthenticated flood costs a permit rather than a Clerk lookup
@@ -102,8 +101,7 @@ any one file and a reordering reads like a harmless refactor.
102
101
  than silently serving the full registry. Retiring a scoping boundary into
103
102
  fail-open is the one outcome worse than the 404.
104
103
  5. **Serve.** A fresh `McpServer` per request, the seven meta-tools registered
105
- against the registry, the Apps shell resource registered (and
106
- `resources/list` deliberately answering with nothing), and the response
104
+ against the registry and the response
107
105
  handed back.
108
106
 
109
107
  ## Layers below the meta-tools
@@ -115,7 +113,7 @@ owns or hands out, and a change usually belongs in exactly one of them:
115
113
  | --- | --- |
116
114
  | `src/registry.ts` | The connector set, identity-scoped views, personal storage partitions, address resolution, catalog TTL/persistence/completeness, refresh single-flight, connector health, per-connector call limiters, and drift. Construction-time refusals live here. |
117
115
  | `src/catalog-service.ts` | Request-local tool listing, search, and describe. It coalesces reads inside one request and opts agent reads into the runtime's deferred catalog channel when one exists. |
118
- | `src/invocation.ts` | One tool call: argument validation, call admission, per-attempt timeout, retry with the connector's own `Retry-After` honoured exactly or declined, result unwrapping, size capping, and the activity record. |
116
+ | `src/invocation.ts` | One tool call: argument validation, call admission, one-attempt timeout, provider retry hints, result unwrapping, size capping, and the activity record. |
119
117
  | `src/catalog.ts` | Ranking, description summarizing, and the compact schema renderer discovery shows. |
120
118
  | `src/result-shapes.ts` | Bounded runtime-only inference and merging for output shapes learned from successful read-only calls whose providers declared none. |
121
119
 
@@ -162,8 +160,7 @@ src/
162
160
  server.ts route ordering, HTTPS upgrade, security wrapper
163
161
  routes/ one file per surface; shared.ts holds the auth gate
164
162
  meta-tools.ts the six non-execute meta-tools over the registry
165
- execute.ts execute_code, the sandbox host bridge, emit and ui
166
- apps-shell.ts the one build-time MCP Apps template
163
+ execute.ts execute_code, the sandbox host bridge, emitted media
167
164
  skills.ts MCP instructions, the usage skill, connector guides
168
165
  registry.ts connector set, addresses, health, call limiters
169
166
  catalog-service.ts request-local catalog access, search, and describe
@@ -194,8 +191,7 @@ src/
194
191
  authenticated subject. Anything cached per request still dies with it.
195
192
  Putting a downstream client or credential on the wrong side of those lines
196
193
  is the highest-severity mistake available here.
197
- - **Route order is behavior.** Moving a built-in below the connector dispatch
198
- hands a connector the ability to shadow it. Moving a mutation route below the
194
+ - **Route order is behavior.** Moving a mutation route below the
199
195
  wildcard `OPTIONS` opts it into CORS preflight.
200
196
  - **Admission runs before auth, on purpose.** Reordering them to "authenticate
201
197
  first" makes the cheapest possible attack the most expensive request.
@@ -133,14 +133,13 @@ through the
133
133
 
134
134
  Worker-level Access runs before every connecta route. Consequently:
135
135
 
136
- - `/health`, operator pages, downstream OAuth callbacks, connector-owned
137
- routes, and `/mcp` all require Access unless a more-specific hostname/path
136
+ - `/health`, operator pages, downstream OAuth callbacks, and `/mcp` all require Access unless a more-specific hostname/path
138
137
  policy says otherwise;
139
138
  - a static connecta bearer and a `cta_…` token are not standalone edge
140
139
  credentials, because Cloudflare rejects them before connecta sees them; and
141
- - a connector that intentionally exposes a public webhook needs a
142
- more-specific Access application and bypass policy. Do not bypass connecta's
143
- OAuth discovery paths when Managed OAuth is enabled.
140
+ - custom public webhooks belong to the deployment outside Connecta and need
141
+ their own Access routing policy. Keep Connecta's OAuth discovery paths
142
+ protected when Managed OAuth is enabled.
144
143
 
145
144
  The [Worker example](../examples/worker/) carries the complete deployment shape
146
145
  and the [upgrade guide](./upgrading.md#0200--0212) gives the reversible Clerk
@@ -1,7 +1,7 @@
1
1
  # Downstream call admission
2
2
 
3
3
  One admitted MCP request can fan out into many downstream calls. A program
4
- calls tools from loops, from `connecta.batch`, and from branches the caller
4
+ calls tools from loops, from parallel `connecta.call` calls, and from branches the caller
5
5
  never saw. The deployment-wide request pool
6
6
  ([request admission](./request-admission.md)) bounds the envelope; it cannot
7
7
  see anything inside it. A provider that publishes "60 requests per minute"
@@ -76,16 +76,15 @@ not be a way to reset a live budget. Exhausted partition capacity is
76
76
 
77
77
  The registry owns the limiter, and `InvocationService.invoke` acquires a permit
78
78
  immediately before `Connector.callTool` and releases it in `finally`. Both call
79
- paths top-level `call_tool` and a program's `connecta.call` or
80
- `connecta.batch` — reach that same seam, so a program cannot buy itself a
79
+ paths, top-level `call_tool` and program calls through `connecta.call`,
80
+ reach that same seam, so a program cannot buy itself a
81
81
  second limit by taking the other route.
82
82
 
83
- - **A retry is another attempt.** It reacquires and can consume another budget
84
- entry, because the provider counts it that way. Backoff never holds a
85
- concurrency permit.
86
- - **A proactive short-window `rate_limited` refusal** participates in the
87
- ordinary retry policy and counts as an attempt. Activity records the final
88
- outcome and the attempt count.
83
+ - **A caller retry is another call.** It reacquires and can consume another
84
+ budget entry. Connecta returns retry hints without waiting or retrying.
85
+ - **A proactive short-window `rate_limited` refusal** returns its retry hint
86
+ without waiting or dispatching. Activity records one failed admission attempt;
87
+ a caller may reissue after the window.
89
88
  - **A queued cancellation consumes nothing.** It is removed from the queue with
90
89
  no rolling-window entry charged.
91
90
  - **Caller cancellation is terminal.** It is non-retryable, releases its
@@ -114,7 +113,7 @@ from "we are throttling ourselves".
114
113
  ## Enforcement scope
115
114
 
116
115
  This is deliberately **per-runtime**. It completely contains fan-out inside one
117
- request, including a wide `connecta.batch` in one Worker isolate. A rolling
116
+ request, including parallel `connecta.call` calls in one Worker isolate. A rolling
118
117
  budget is exact inside one Node process or Worker isolate, and best-effort
119
118
  across isolates, replicas, and restarts.
120
119
 
@@ -139,5 +138,5 @@ typed error code.
139
138
  | Invariant | Suite |
140
139
  | --- | --- |
141
140
  | 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
- | 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) |
141
+ | One base-registry limiter shared by direct and program calls, promise concurrency with input order preserved, cancellation threading, no dispatch or retry or health poisoning after cancellation, retry hints returned without waiting or poisoning health, payload-free `/health` aggregates | `test/call-admission.test.ts` (integration, Node + Workers) |
143
142
  | 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) |