@zackbart/connecta 0.16.1 → 0.18.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/CHANGELOG.md +180 -0
- package/README.md +4 -0
- package/dist/catalog-service.d.ts +10 -0
- package/dist/catalog-service.js +77 -5
- package/dist/catalog.js +114 -12
- package/dist/errors.d.ts +4 -6
- package/dist/execute.d.ts +7 -0
- package/dist/execute.js +262 -168
- package/dist/invocation.js +3 -1
- package/dist/meta-tools.d.ts +4 -0
- package/dist/meta-tools.js +55 -23
- package/dist/operator-ui/generated.d.ts +1 -1
- package/dist/operator-ui/generated.js +1 -1
- package/dist/operator-ui/model.d.ts +3 -1
- package/dist/providers/mixpanel.d.ts +3 -5
- package/dist/providers/mixpanel.js +73 -5
- package/dist/providers/stripe.d.ts +25 -24
- package/dist/providers/stripe.js +64 -35
- package/dist/registry.d.ts +32 -9
- package/dist/registry.js +217 -33
- package/dist/routes/mcp.js +6 -0
- package/dist/routes/ui.js +1 -1
- package/dist/skills.d.ts +5 -1
- package/dist/skills.js +206 -30
- package/dist/types.d.ts +14 -2
- package/dist/ui.js +4 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +8 -5
- package/documentation/code-mode.md +68 -68
- package/documentation/connector-guides.md +29 -27
- package/documentation/connectors.md +13 -1
- package/documentation/meta-tools.md +53 -19
- package/documentation/mixpanel.md +20 -0
- package/documentation/notion.md +17 -0
- package/documentation/operations.md +24 -21
- package/documentation/operator-ui.md +12 -2
- package/documentation/provider-audit.md +15 -7
- package/documentation/provider-conventions.md +26 -13
- package/documentation/stripe.md +66 -59
- package/documentation/upgrading.md +46 -4
- package/ethos.md +7 -7
- package/examples/worker/README.md +4 -3
- package/package.json +2 -2
- package/templates/node/README.md +7 -0
- package/templates/node/package.json +5 -2
|
@@ -53,8 +53,8 @@ createConnecta({
|
|
|
53
53
|
});
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Dynamic Workers require the Workers Paid plan. The
|
|
57
|
-
|
|
56
|
+
Dynamic Workers require the Workers Paid plan. The supported constructor passes only `loader`; `bindings`, `modules`, or
|
|
57
|
+
`globalOutbound` grant ambient guest authority and violate `P2`. The [Worker example](../examples/worker/README.md#code-mode) carries the full setup.
|
|
58
58
|
|
|
59
59
|
## What an executor must implement
|
|
60
60
|
|
|
@@ -104,8 +104,8 @@ Connecta passes exactly one provider, named `connecta`. An executor must:
|
|
|
104
104
|
uncaught tool failure keeps its type (`E1`).
|
|
105
105
|
6. **Capture `console.log`, `console.warn`, and `console.error`** into `logs` in
|
|
106
106
|
call order (`R5`), bounding what it retains.
|
|
107
|
-
7. **Bound the guest**: wall clock, memory, stack, and CPU (`L3`, `L5`)
|
|
108
|
-
|
|
107
|
+
7. **Bound the guest**: wall clock, memory, stack, and CPU (`L3`, `L5`). Keep
|
|
108
|
+
ambient capabilities within the documented and tested `P2`/`X5` boundary.
|
|
109
109
|
8. **Grant no ambient authority of its own.** Never back this with `eval` or
|
|
110
110
|
`node:vm`: the sandbox is a containment layer on top of connecta's boundary,
|
|
111
111
|
not a replacement for it, and every capability arrives through `fns`.
|
|
@@ -138,10 +138,10 @@ reinterpreted, so do not rely on it.
|
|
|
138
138
|
It is host plumbing, callable but not contract: it takes a connector id and an
|
|
139
139
|
unsanitized-or-sanitized tool name and can change shape without notice.
|
|
140
140
|
|
|
141
|
-
Anything else a runtime happens to expose is outside the contract and
|
|
142
|
-
be used
|
|
143
|
-
filesystem access
|
|
144
|
-
|
|
141
|
+
Anything else a runtime happens to expose is outside the portable contract and
|
|
142
|
+
must not be used. QuickJS grants none of it. A loader-only Dynamic Worker denies
|
|
143
|
+
external egress and filesystem access and keeps its environment maps empty, but
|
|
144
|
+
it exposes the globals and runtime builtins described in `X5`.
|
|
145
145
|
|
|
146
146
|
**P3.** Values cross the host bridge as JSON. Arguments must be
|
|
147
147
|
JSON-serializable and results arrive as plain JSON values. A value outside JSON —
|
|
@@ -154,8 +154,9 @@ scratch storage carried to the next program, and no request-bound object outlive
|
|
|
154
154
|
the request that created it. Within one execution, host calls share one
|
|
155
155
|
downstream request scope.
|
|
156
156
|
|
|
157
|
-
**P5.** Plain JavaScript only. TypeScript syntax is a syntax error
|
|
158
|
-
|
|
157
|
+
**P5.** Plain JavaScript only. TypeScript syntax is a syntax error. Portable code
|
|
158
|
+
does not import: QuickJS blocks imports, while Dynamic Workers expose the `X5`
|
|
159
|
+
runtime modules. Neither executor exposes `require`.
|
|
159
160
|
|
|
160
161
|
## Addressing
|
|
161
162
|
|
|
@@ -172,7 +173,10 @@ global whose properties are its tools, so `<connectorId>.<toolName>(args)` works
|
|
|
172
173
|
with both parts sanitized into JavaScript identifiers — characters outside
|
|
173
174
|
`[A-Za-z0-9_$]` become `_`, a leading digit gets `_` prefixed, and a reserved
|
|
174
175
|
word gets `_` appended (`my-service.get.thing` → `my_service.get_thing`). The
|
|
175
|
-
globals are lazy: no catalog is fetched until a program touches one.
|
|
176
|
+
globals are lazy: no catalog is fetched until a program touches one. The
|
|
177
|
+
bounded deployment inventory in the `execute_code` description shows each
|
|
178
|
+
canonical connector id and labels the shortcut only when it differs; the
|
|
179
|
+
[discovery guide](./meta-tools.md#discovery-context) defines that bound.
|
|
176
180
|
|
|
177
181
|
**A3.** A shortcut that resolves to more than one tool fails closed with
|
|
178
182
|
`ambiguous_tool_alias`, naming the colliding tool names and pointing at
|
|
@@ -240,8 +244,8 @@ can set `includeSchemaKeys: false` to buy the bytes back.
|
|
|
240
244
|
**S3.** Discovery is bounded and the bounds throw rather than silently shrink: a
|
|
241
245
|
`limit` outside 1–100 is `invalid_args`, and a page whose serialized form
|
|
242
246
|
exceeds 256,000 bytes is `result_too_large`, each with a hint naming the ways to
|
|
243
|
-
ask for less.
|
|
244
|
-
(`E1`)
|
|
247
|
+
ask for less. The thrown error carries the stable `code`, `retryable`, and
|
|
248
|
+
`details` fields (`E1`).
|
|
245
249
|
|
|
246
250
|
### connecta.describe
|
|
247
251
|
|
|
@@ -255,10 +259,13 @@ const { tools } = await connecta.describe({
|
|
|
255
259
|
});
|
|
256
260
|
```
|
|
257
261
|
|
|
258
|
-
**S4.** Returns `{ tools }` in
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
**S4.** Returns `{ tools }` in order, one entry per address. An unknown address
|
|
263
|
+
or failed catalog returns `error` plus typed `errorDetails`: `code`, `message`, and `retryable`. Misses
|
|
264
|
+
carry a route-aware `nextAction`; a close miss may add three canonical `suggestions`.
|
|
265
|
+
Catalog failures add only `retryAfterMs` when known. One bad address never fails the whole call. Each failed entry clamps its
|
|
266
|
+
caller-authored `address` to 512 UTF-8 bytes with an `…` marker. Entry order
|
|
267
|
+
correlates a clipped address with its request; successes keep canonical addresses. More than 100
|
|
268
|
+
addresses is `invalid_args`; the same 256,000-byte ceiling applies.
|
|
262
269
|
|
|
263
270
|
### connecta.call
|
|
264
271
|
|
|
@@ -295,9 +302,7 @@ order. A success is `{ address, ok: true, data }`. A failure is
|
|
|
295
302
|
field names the host's internal batch path uses. One failing call never rejects
|
|
296
303
|
the batch, and more than ten calls throws.
|
|
297
304
|
|
|
298
|
-
**S8.** `
|
|
299
|
-
error crosses the bridge as a bare message (`E1`), a batch of one is the supported
|
|
300
|
-
way for a program to *decide* something about a failure rather than report it.
|
|
305
|
+
**S8.** Batch and thrown failures share one vocabulary (`E1`): an entry's `errorDetails.code` and `retryable` equal the fields on the error the same call would throw. Use batch for independent concurrency, not to recover lost type.
|
|
301
306
|
|
|
302
307
|
### connecta.emit
|
|
303
308
|
|
|
@@ -310,24 +315,18 @@ clauses are [Emitted output](#emitted-output) (`M1`–`M10`).
|
|
|
310
315
|
|
|
311
316
|
## Errors
|
|
312
317
|
|
|
313
|
-
**E1.** There are four error channels
|
|
318
|
+
**E1.** There are four error channels. Connecta failures are typed whether caught or uncaught.
|
|
314
319
|
|
|
315
320
|
| Channel | Shape | Typed? |
|
|
316
321
|
| --- | --- | --- |
|
|
317
|
-
| A
|
|
322
|
+
| A caught Connecta host failure | `Error` with `message`, `code`, `retryable`, and `details` | yes |
|
|
318
323
|
| `connecta.batch` outcome | `{ ok: false, error, errorDetails }` | yes |
|
|
319
324
|
| An uncaught **tool or discovery** failure, as the model sees it | `{ error: { code, message, retryable, … } }` with `isError` | yes |
|
|
320
|
-
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
host call
|
|
324
|
-
|
|
325
|
-
classify, use `errorDetails`; to hand a failure to the model with its type
|
|
326
|
-
intact, let it escape uncaught — connecta re-attaches the typed details on the
|
|
327
|
-
way out. The model-facing version of this lives in `execute_code`'s description,
|
|
328
|
-
not in the always-loaded usage skill, which `test/meta-tools.test.ts` caps at
|
|
329
|
-
2,500 bytes — a budget the guide already spends nearly all of, so new text there
|
|
330
|
-
displaces old rather than adding to what every request pays for.
|
|
325
|
+
| Program or execution failure (`E5`, `E6`, a bridge bound in `L6`) | error text | no |
|
|
326
|
+
|
|
327
|
+
Both executor bridges reduce a rejected host call to `new Error(message)`. Connecta restores the typed failure in a trusted prelude with a per-execution authenticated frame (`X11`), without turning the rejection into a returned value.
|
|
328
|
+
`message` remains the human text. `code` and `retryable` are the stable branch fields; `details` is the complete host classification. This covers `call`, connector shortcuts, `search`, `describe`, `emit`, `ui`, rejected batch input, and the host-call budget.
|
|
329
|
+
Program-authored errors stay untyped, and code must never parse error prose.
|
|
331
330
|
|
|
332
331
|
**E2.** The taxonomy: `retryable` is what connecta reports, `Y3` what a program may do.
|
|
333
332
|
|
|
@@ -339,16 +338,18 @@ displaces old rather than adding to what every request pays for.
|
|
|
339
338
|
| `destructive_tool_requires_approval` | the tool is not explicitly read-only | false |
|
|
340
339
|
| `auth_required` | the credential is missing, expired, or rejected | false |
|
|
341
340
|
| `invalid_args` | arguments or discovery bounds were rejected | false |
|
|
342
|
-
| `not_found` | the downstream answered and the resource is not there — the one code that says skip this id rather than stop,
|
|
341
|
+
| `not_found` | the downstream answered and the resource is not there — the one code that says skip this id rather than stop, raised only where the provider tells absence from a permission gap ([H11](./provider-conventions.md#h11--errors-are-mapped-to-what-the-caller-does-next)) | false |
|
|
343
342
|
| `input_required_unsupported` | a downstream asked for mid-call input | false |
|
|
344
343
|
| `rate_limited` | the downstream reported a rate limit | true |
|
|
345
344
|
| `unavailable` | the downstream is down or unreachable | true |
|
|
346
345
|
| `timeout` | the per-call 15-second deadline expired | true |
|
|
347
346
|
| `cancelled` | the run ended while this call was in flight (`E5`) | false |
|
|
348
|
-
| `connector_call_failed` | anything else the connector threw
|
|
347
|
+
| `connector_call_failed` | anything else the connector threw | per message |
|
|
349
348
|
| `batch_call_failed` | a `connecta.batch` entry connecta could not even attempt | per message |
|
|
350
349
|
| `catalog_lookup_failed` | the connector's catalog could not be loaded | per cause |
|
|
351
350
|
| `result_processing_failed` | the result could not be prepared | per message |
|
|
351
|
+
| `result_too_large` | a discovery response exceeded its byte bound | false |
|
|
352
|
+
| `budget_exceeded` | the run exhausted a host-call or emitted-output budget | false |
|
|
352
353
|
|
|
353
354
|
**E3.** `auth_required` carries the same recovery envelope as `call_tool`:
|
|
354
355
|
`connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
|
|
@@ -382,7 +383,7 @@ exactly first, by containment second — so a program that *wraps* a failure's
|
|
|
382
383
|
message in its own text still reports the underlying typed failure. Keeping the
|
|
383
384
|
type beats keeping the prose.
|
|
384
385
|
|
|
385
|
-
**E7.** `retryable` for `unknown_address`, `unknown_tool`, `ambiguous_tool_alias`, and `destructive_tool_requires_approval` is pinned false, never inferred from an address containing `503`, `429`, or `temporar`. The first two carry `nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }` — the same scoped discovery the top-level record names, keyed to the surface the caller actually has. A program cannot call `search_tools`, so it is never told to.
|
|
386
|
+
**E7.** `retryable` for `unknown_address`, `unknown_tool`, `ambiguous_tool_alias`, and `destructive_tool_requires_approval` is pinned false, never inferred from an address containing `503`, `429`, or `temporar`. The first two carry `nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }` — the same scoped discovery the top-level record names, keyed to the surface the caller actually has. A program cannot call `search_tools`, so it is never told to. The message, the derived `query`, and a failed describe entry's `address` clamp caller-authored text to 512 UTF-8 bytes with an `…` marker. Those values land in the text content and `structuredContent`, so an invented 50 KB address would otherwise produce a refusal orders of magnitude past the deployment's result cap. A clipped address still identifies the mistake by its position; a short one — the common case — is exact and untagged.
|
|
386
387
|
|
|
387
388
|
**E8.** A remote MCP tool whose advertised schema rejects the call fails before provider dispatch with `invalid_args`, carrying bounded, value-free `{ path, code, expected }` findings and scoped search recovery keyed `function: "connecta.search"` like every other in-program miss. A declared property reports the schema keyword that failed, never the validator's duplicate `additionalProperties` branch; a truly undeclared property still reports `additionalProperties`. Unsupported schemas pass through; unrecognized provider prose remains `connector_call_failed`.
|
|
388
389
|
|
|
@@ -590,14 +591,14 @@ the same mistake as automatic host-side projection, refused in `ethos.md`
|
|
|
590
591
|
([#282](https://github.com/zackbart/connecta/issues/282)).
|
|
591
592
|
|
|
592
593
|
**U13.** The always-loaded MCP instructions locate `connecta.ui(html)` before an
|
|
593
|
-
agent chooses a route: it
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
therefore rides `initialize`, under a 1,000-character ceiling for the complete
|
|
594
|
+
agent chooses a route: it exists only inside `execute_code`, never in connector
|
|
595
|
+
search, and carries `U12`'s mirrored-return duty. The detailed call, binding,
|
|
596
|
+
budget, and repair rules live in the on-demand `usage` skill. The location
|
|
597
|
+
distinction rides `initialize`, under a 1,000-character ceiling for the complete
|
|
598
598
|
instructions string. This promotes existing contract, not capability: the
|
|
599
599
|
seven-tool surface, guest API, catalog, Apps delivery, and runtime do not change
|
|
600
|
-
([#286](https://github.com/zackbart/connecta/issues/286)
|
|
600
|
+
([#286](https://github.com/zackbart/connecta/issues/286),
|
|
601
|
+
[#418](https://github.com/zackbart/connecta/issues/418)).
|
|
601
602
|
|
|
602
603
|
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)).
|
|
603
604
|
|
|
@@ -608,25 +609,22 @@ annotation-gated `maxRetries`; code mode fixes it at zero, so one
|
|
|
608
609
|
`connecta.call` is exactly one downstream attempt. The program is the retry
|
|
609
610
|
loop, and its budget is visible to it (`L4`).
|
|
610
611
|
|
|
611
|
-
**Y2.** A program may retry a failure whose `errorDetails.retryable` is true,
|
|
612
|
-
learned through `connecta.batch` (`S8`). Every attempt spends host-call budget,
|
|
613
|
-
so a retry loop that ignores the budget converts a transient failure into a
|
|
614
|
-
budget failure.
|
|
612
|
+
**Y2.** A program may retry a caught failure whose `retryable` is true, or a batch failure whose `errorDetails.retryable` is true (`S8`). Every attempt spends host-call budget, so an unchecked loop converts a transient failure into `budget_exceeded`.
|
|
615
613
|
|
|
616
614
|
**Y3.** What must never be retried automatically:
|
|
617
615
|
|
|
618
616
|
- anything with `retryable: false` — a policy refusal, a missing credential, a
|
|
619
617
|
bad address, or malformed arguments will fail identically forever;
|
|
620
|
-
- `rate_limited`, immediately.
|
|
621
|
-
wait
|
|
622
|
-
|
|
623
|
-
`retryAfterMs` in hand.
|
|
618
|
+
- `rate_limited`, immediately. A portable program has no timer, and a
|
|
619
|
+
Dynamic-Worker-only wait would spend the run's wall-clock budget on code that
|
|
620
|
+
fails on QuickJS. Return the failure and let the model, which can wait,
|
|
621
|
+
re-issue with `retryAfterMs` in hand.
|
|
624
622
|
- a cancelled or timed-out *execution*: it is already over (`L1`).
|
|
625
623
|
|
|
626
624
|
**Y4.** Connecta's own retry machinery beneath the meta-tools honours a
|
|
627
625
|
connector-reported `Retry-After` exactly or not at all, and declines windows
|
|
628
626
|
longer than 10 seconds rather than shortening them. A program sees the window
|
|
629
|
-
verbatim as `errorDetails.retryAfterMs`.
|
|
627
|
+
verbatim as `err.details.retryAfterMs` or `errorDetails.retryAfterMs`.
|
|
630
628
|
|
|
631
629
|
## Cancellation and limits
|
|
632
630
|
|
|
@@ -653,13 +651,12 @@ because connecta enforces them above the sandbox:
|
|
|
653
651
|
| Deadline per host call | 15 s |
|
|
654
652
|
| Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
|
|
655
653
|
| `describe` addresses | ≤ 100 |
|
|
654
|
+
| `describe` nearby suggestions | ≤ 3 canonical addresses per failed entry |
|
|
655
|
+
| Caller text echoed by `describe` recovery | ≤ 512 UTF-8 bytes per field, plus `…` |
|
|
656
656
|
| Result | 24,000 serialized characters |
|
|
657
657
|
| Logs presented to the model | 4,000 characters |
|
|
658
658
|
|
|
659
|
-
Exhausting the host-call budget fails that call
|
|
660
|
-
`connector_call_failed` (`E2`) and a message naming the budget — no connector was
|
|
661
|
-
reached, so nothing more specific is true. Retrying it is pointless: the budget
|
|
662
|
-
does not refill inside one execution.
|
|
659
|
+
Exhausting the host-call budget fails that call with non-retryable `budget_exceeded` (`E2`) and a message naming the budget. No connector is reached, and the budget does not refill inside one execution.
|
|
663
660
|
|
|
664
661
|
**L5.** The guest is memory-, stack-, and CPU-bounded, and a program that
|
|
665
662
|
exhausts a bound ends the run with an error instead of degrading the host. The
|
|
@@ -672,7 +669,7 @@ code safe to run at all.
|
|
|
672
669
|
**L6.** A host call's serialized arguments and its serialized result are each
|
|
673
670
|
bounded — QuickJS caps both at 256 KiB (`X10`) — and exceeding either fails that
|
|
674
671
|
call, not the execution, so a program can catch it and ask for less. The failure
|
|
675
|
-
is untyped text (`E1`). An over-bound *result* names the address the program
|
|
672
|
+
is executor-owned untyped text, not a Connecta host failure (`E1`). An over-bound *result* names the address the program
|
|
676
673
|
called, not the internal dispatcher behind the shortcut namespaces; an over-bound
|
|
677
674
|
*argument* payload is refused before it is parsed, so it names no address at
|
|
678
675
|
all — parsing it to write a better message would spend exactly the work the bound
|
|
@@ -750,11 +747,9 @@ Worker renders arguments with `String()` (so an object logs as
|
|
|
750
747
|
latter two. Only the three captured everywhere are contract (`R5`); rendering is
|
|
751
748
|
not.
|
|
752
749
|
|
|
753
|
-
**X5. Leftover
|
|
754
|
-
`
|
|
755
|
-
`
|
|
756
|
-
`process.env` is empty, and timers work. `P2` is the contract — a program that
|
|
757
|
-
uses `setTimeout` is writing Workers-only code, and it will fail on Node.
|
|
750
|
+
**X5. Leftover authority.** QuickJS blocks imports and has no `fetch`, `process`, timers, `crypto`, or `WebSocket`. A Dynamic Worker has those globals plus a non-contract set of runtime builtins through `import()` and `process.getBuiltinModule()`, including `node:path`, `node:crypto`, `node:net`, `node:tls`, `node:dns`, `node:module`, and `cloudflare:workers`. The upstream set can drift; this list is not an allowlist.
|
|
751
|
+
The supported Worker construction is exactly `new DynamicWorkerExecutor({ loader })`. Do not pass `bindings`, `modules`, or `globalOutbound`: each can grant ambient configuration, code, or egress. Under it, `process.env`, lexical `this.env`, and `cloudflare:workers.env` are empty; `node:fs`, `node:http`, and `node:https` are unavailable through either access route; external `fetch`, `WebSocket`, `node:net`, and `node:tls` fail with workerd's outbound-denial error; DNS lookup ends unresolved; and `fetch("data:...")` resolves locally.
|
|
752
|
+
`P2` is the portable contract. Programs use none of this runtime-only authority, including timers and `crypto`, because the same code fails on QuickJS. The `execute_code` description and served `usage` skill say so before an agent writes code.
|
|
758
753
|
|
|
759
754
|
**X6. Stall detection.** QuickJS notices a program awaiting something that can
|
|
760
755
|
never settle and fails fast; the Dynamic Worker waits for its deadline. The fast
|
|
@@ -786,11 +781,16 @@ a `process.send` with a hard ceiling. A program that returns a quarter-megabyte
|
|
|
786
781
|
from one tool call therefore fails on Node and may succeed on Workers — reduce
|
|
787
782
|
inside the program either way (`R1`).
|
|
788
783
|
|
|
784
|
+
**X11. Typed host rejection.** Both executors rebuild Connecta's authenticated host-failure frame as a thrown guest `Error` (`E1`). The per-run secret stays in the trusted prelude closure, and the prelude locks `globalThis.Error`, so guest code and connector prose cannot forge the host transport frame.
|
|
785
|
+
The human message is unchanged; a mismatched frame is ordinary untyped prose.
|
|
786
|
+
|
|
789
787
|
## Changes from earlier code mode
|
|
790
788
|
|
|
791
|
-
|
|
789
|
+
Six behaviors changed with this contract, matching the changelog's Unreleased
|
|
792
790
|
entry. Programs that ran before still run.
|
|
793
791
|
|
|
792
|
+
- **Caught Connecta failures expose their classification** (`E1`, `X11`). Their human message and thrown semantics stay unchanged; `code`, `retryable`, and `details` are additive.
|
|
793
|
+
|
|
794
794
|
- **`connecta.batch` failures gained `errorDetails`** (`S7`). They carried only a
|
|
795
795
|
message, which left a program unable to tell a policy refusal from a transient
|
|
796
796
|
failure. Additive, and it reuses the host's internal batch field names, so a
|
|
@@ -833,21 +833,21 @@ the upstream `Executor` shape assignable.
|
|
|
833
833
|
| Clauses | Test |
|
|
834
834
|
| --- | --- |
|
|
835
835
|
| `P1`, `P5` | `test/guest-api-contract.test.ts` (TypeScript syntax), `test/quickjs-executor.test.ts` (`normalizeCode`) |
|
|
836
|
-
| `P2`, `X5` | `test/guest-api-contract.test.ts` (
|
|
836
|
+
| `P2`, `X5` | `test/guest-api-contract.test.ts` (Dynamic globals plus loader-only filesystem, HTTP, environment, egress, DNS, and local `data:` boundaries), `test/guest-api-contract-quickjs.test.ts` (exact absent globals and blocked imports), `test/deployment-shapes.test.ts` (loader-only Worker construction) |
|
|
837
837
|
| `P3`, `X9` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` |
|
|
838
838
|
| `P4` | `test/guest-api-contract.test.ts` (no cross-run leakage), `test/execute.test.ts` (one catalog load per connector per execution) |
|
|
839
|
-
| `A1`, `A2` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (sanitizing) |
|
|
839
|
+
| `A1`, `A2` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (sanitizing), `test/server.test.ts` (bounded live connector inventory) |
|
|
840
840
|
| `A3` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (colliding alias) |
|
|
841
841
|
| `A4` | `test/execute.test.ts` (namespace collisions, reserved namespace) |
|
|
842
842
|
| `A5` | verdict; `A1`–`A3` are its enforcement |
|
|
843
843
|
| `S1`, `S2` | `test/guest-api-contract.test.ts` (flat page, connector guides, schema keys, and the unfiltered browse that replaces `list_connectors`), `test/execute.test.ts` (guide pagination/partial/no-match behavior and `$ref`/`allOf`), `test/meta-tools.test.ts` (mixed complete/partial ranking and stable pagination) |
|
|
844
844
|
| `S3` | `test/guest-api-contract.test.ts` (typed uncaught bound), `test/execute.test.ts` (count limits, fan-out bound) |
|
|
845
|
-
| `S4` | `test/
|
|
845
|
+
| `S4` | both guest-contract executors (ordered mixed describe results with unknown-address, unknown-tool suggestion, and catalog-failure details), `test/meta-tools.test.ts` (top-level routing, no-suggestion, catalog-failure, and hostile-input bounds) |
|
|
846
846
|
| `S5` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`unwrapMcpResult`) |
|
|
847
847
|
| `S6` | `test/execute.test.ts` (fail-closed annotations, activity parity) |
|
|
848
848
|
| `S7` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (batch cap) |
|
|
849
|
-
| `S8`, `E1` |
|
|
850
|
-
| `E2`, `E8` | `test/guest-api-contract.test.ts` (code → `retryable`, batch and uncaught validation recovery), `test/meta-tools.test.ts` (direct, destructive, batch, provider fallback), `test/validate.test.ts` (bounded payload-free findings), `test/errors.test.ts` |
|
|
849
|
+
| `S8`, `E1`, `X11` | both guest-contract executors (caught call, namespace, discovery, utility, batch-validation, budget, and forgery cases; typed batch equivalence) |
|
|
850
|
+
| `E2`, `E8` | `test/guest-api-contract.test.ts` (code → `retryable`, caught, batch, and uncaught validation recovery), `test/meta-tools.test.ts` (direct, destructive, batch, provider fallback), `test/validate.test.ts` (bounded payload-free findings), `test/errors.test.ts` |
|
|
851
851
|
| `E3` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`auth_required`) |
|
|
852
852
|
| `E4` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (destructive) |
|
|
853
853
|
| `E5` | `test/guest-api-contract.test.ts` (execution-failure channel, in-flight `cancelled`), `test/execute.test.ts` (admission), `test/executor-admission.test.ts`, `test/quickjs-executor.test.ts` (mid-run shutdown) |
|
|
@@ -883,7 +883,7 @@ the upstream `Executor` shape assignable.
|
|
|
883
883
|
| `U7`, `U8` | two arms passing one case table, `test/codemode-compat.test.ts` |
|
|
884
884
|
| `U9` | `test/execute-ui.test.ts` (a `ui` byte aggregate distinct from `emitted`, absent when nothing was accepted) |
|
|
885
885
|
| `U12` | `test/server.test.ts` (the `connecta.ui` bullet carries the return-value clause); a duty on program authors, so the description is the only place it can be enforced |
|
|
886
|
-
| `U13` | `test/code-first-surface.test.ts`, `test/server.test.ts` (served `initialize.instructions` locate UI inside `execute_code`, exclude it from
|
|
886
|
+
| `U13` | `test/code-first-surface.test.ts`, `test/server.test.ts` (served `initialize.instructions` locate UI inside `execute_code`, exclude it from connector search, state the mirrored-return duty, and stay within the complete 1,000-character budget; the usage skill carries detailed call rules) |
|
|
887
887
|
| `X3` | `test/quickjs-executor.test.ts` (cancels a running child) |
|
|
888
888
|
| `X4` | `test/guest-api-contract.test.ts` (string logs only) |
|
|
889
889
|
| `X6` | `test/quickjs-executor.test.ts` (never-settling await) |
|
|
@@ -40,13 +40,17 @@ hand-written connector. It is deployment-owned configuration like everything
|
|
|
40
40
|
else here: an edit and a redeploy, never a runtime registration.
|
|
41
41
|
|
|
42
42
|
`content` is returned byte for byte by `skills({ name: "connector:<id>" })`.
|
|
43
|
-
`summary` is normalized and
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
`summary` is normalized and must fit 120 characters. A longer configured value
|
|
44
|
+
refuses construction instead of silently changing the operator's words. Omit
|
|
45
|
+
it and connecta derives the same bounded summary the skills listing uses: the
|
|
46
|
+
first meaningful body paragraph, joined across Markdown's physical line wraps,
|
|
47
|
+
with frontmatter, fences, rules, comments, and tables skipped. When the
|
|
48
|
+
paragraph does not fit, connecta keeps a useful complete sentence when one
|
|
49
|
+
fits, then prefers a clause or word boundary before adding an ellipsis. A heading is used
|
|
50
|
+
only when the guide has no body, and the connector's description is the last
|
|
51
|
+
resort. A derived summary is usually worse than a written one — it was written
|
|
52
|
+
to open a document, not to answer "is this guide relevant to what I am about
|
|
53
|
+
to do".
|
|
50
54
|
|
|
51
55
|
`connector:<id>` is the only address for a guide, and built-in skill names are
|
|
52
56
|
bare identifiers, so a guide can never shadow or be shadowed by `usage`: a
|
|
@@ -56,15 +60,12 @@ unknown name, unknown connector, connector with no guide — is an explicit
|
|
|
56
60
|
error. Nothing silently falls back to the generic guide, because a generic
|
|
57
61
|
answer to a specific question is worse than no answer.
|
|
58
62
|
|
|
59
|
-
Discovery text is conditional on the deployment actually having a guide.
|
|
60
|
-
|
|
63
|
+
Discovery text is conditional on the deployment actually having a guide. Short
|
|
64
|
+
pointers in the `skills`, `search_tools`, `call_destructive_tool`, and
|
|
61
65
|
`execute_code` descriptions appear only when at least one visible connector
|
|
62
|
-
declares one
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
exception: it stays byte-identical across every deployment, including its
|
|
66
|
-
per-connector-guides section, so an agent that has read it once in a task never
|
|
67
|
-
needs a deployment-local copy of it.
|
|
66
|
+
declares one. The detailed selection rules live only in the built-in `usage`
|
|
67
|
+
skill. That skill stays byte-identical across deployments, including its
|
|
68
|
+
per-connector-guides section, so an agent reads it at most once per task.
|
|
68
69
|
|
|
69
70
|
## What belongs in a guide
|
|
70
71
|
|
|
@@ -160,15 +161,16 @@ schemas will never carry, because it cannot change them
|
|
|
160
161
|
## Tests that enforce this
|
|
161
162
|
|
|
162
163
|
`test/meta-tools.test.ts` owns the guide behavior end to end: the skills
|
|
163
|
-
listing carrying one entry per guided connector, summaries
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
164
|
+
listing carrying one entry per guided connector, summaries joining a
|
|
165
|
+
hard-wrapped opening paragraph and shortening at readable boundaries,
|
|
166
|
+
configured summaries refusing construction past the bound, heading and
|
|
167
|
+
description fallbacks, markup skipping, whitespace-only guides treated as no
|
|
168
|
+
guide, content returned verbatim including surrounding padding, identical
|
|
169
|
+
content in two deployments staying isolated, every miss erroring rather than
|
|
170
|
+
falling back to the generic guide with an identically labelled skills list on
|
|
171
|
+
each branch, the `guide` pointer in search output, and `guideRequired`
|
|
172
|
+
appearing for connector-required conventions, approval-bound tools, and
|
|
173
|
+
truncated schemas — and being absent from a search that asked for no schemas.
|
|
174
|
+
`test/server.test.ts` owns the conditional half: it compares a guide-free
|
|
175
|
+
deployment's four short pointers against a guided one's, and asserts the
|
|
176
|
+
complete `usage` skill is byte-identical between them.
|
|
@@ -252,6 +252,17 @@ the cursor ends, preserve schemas and annotations, and never cache or serve a
|
|
|
252
252
|
partial walk. The fixed TTL is paired with a schema fingerprint so a changed
|
|
253
253
|
catalog invalidates persisted results even within the time window.
|
|
254
254
|
|
|
255
|
+
Agent reads use a complete entry inside `staleCatalogSeconds` immediately and
|
|
256
|
+
defer the refresh that read already demanded. Every live refresh is
|
|
257
|
+
single-flight per connector in one runtime. A blocking operator or direct read
|
|
258
|
+
joins an agent-owned refresh and awaits it; an agent stale read joins an
|
|
259
|
+
operator-owned refresh without awaiting it. The first refresh owns the context
|
|
260
|
+
and deadline. A deferred first refresh owns a fresh scope and signal, then
|
|
261
|
+
closes that scope. Operator status and direct registry reads still await
|
|
262
|
+
freshness. The operator page reports whether the last agent read in this runtime
|
|
263
|
+
was fresh or stale; this payload-free timestamp is not persisted. No timer or
|
|
264
|
+
idle warmup originates downstream traffic.
|
|
265
|
+
|
|
255
266
|
Tool calls must use the shared invocation path. That keeps direct calls, batch
|
|
256
267
|
children, and code-mode host calls aligned on safety, retries, admission,
|
|
257
268
|
timeouts, validation, result guards, and typed failures.
|
|
@@ -259,7 +270,8 @@ timeouts, validation, result guards, and typed failures.
|
|
|
259
270
|
Connector usage guides are configuration too. `usageGuide` accepts the
|
|
260
271
|
historical markdown string or `{ content, summary?, required? }`; the latter
|
|
261
272
|
lets discovery explain what the guide covers without loading it. The summary
|
|
262
|
-
is
|
|
273
|
+
is a 120-character routing hint; a longer configured value refuses construction
|
|
274
|
+
instead of being silently shortened. Mark a guide `required` only when no complete
|
|
263
275
|
tool schema can describe correct use, such as a generic operation wrapper or a
|
|
264
276
|
mandatory cross-tool sequence. Mutations and truncated compact schemas already
|
|
265
277
|
produce automatic review requirements. Two deployments may reuse the same
|
|
@@ -60,6 +60,14 @@ their smallest successful one-tool shapes:
|
|
|
60
60
|
|
|
61
61
|
## Discovery context
|
|
62
62
|
|
|
63
|
+
The deployment-derived `execute_code` description includes a live connector
|
|
64
|
+
inventory before any catalog search. It preserves registry order and uses each
|
|
65
|
+
canonical id, adding `shortcut <name>` only when the program namespace differs.
|
|
66
|
+
The complete inventory line is capped at 256 UTF-8 bytes. Entries stay whole,
|
|
67
|
+
and a truncated line ends with the exact `+N more` count. This reads only the
|
|
68
|
+
configured registry: it loads no catalog, probes no credential, grants no
|
|
69
|
+
capability, and does not replace canonical discovery or addressing.
|
|
70
|
+
|
|
63
71
|
Start an unknown-address lookup with two to four distinctive action/object
|
|
64
72
|
terms, not the full request, and omit `limit` so the default eight-result page
|
|
65
73
|
stays small. When the integration is obvious, set `connector` to its id: a
|
|
@@ -89,14 +97,16 @@ Compact search is deliberately a routing view, not a second copy of connector
|
|
|
89
97
|
documentation. Tool purposes are capped at 160 characters, connector
|
|
90
98
|
descriptions and property prose are omitted, required input fields render
|
|
91
99
|
before optional ones, and each input or output shape is capped at 1,024 UTF-8
|
|
92
|
-
bytes. Within that unchanged total, each enum node
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
bytes. Within that unchanged total, each enum node and each constraint
|
|
101
|
+
annotation may spend at most 256 UTF-8 bytes. Numeric bounds, string length
|
|
102
|
+
bounds, patterns, and formats render beside their type. A constraint that does
|
|
103
|
+
not fit is dropped whole. If constraints push the full shape over 1,024 bytes,
|
|
104
|
+
search retries the shape without them. Compact describe keeps all declared
|
|
105
|
+
constraints. A large enum keeps the longest whole-value prefix that fits, then
|
|
106
|
+
adds `unknown` and a comment with the exact omitted-value count. An empty enum
|
|
107
|
+
renders as the valid `never` type. A capped object becomes a valid
|
|
108
|
+
required-first shape with `unknown` types; other shapes become
|
|
109
|
+
`unknown /* truncated */`. Any cap marks the match with
|
|
100
110
|
`inputSchemaTruncated` or `outputSchemaTruncated`; repeat the search with
|
|
101
111
|
`includeSchemas: "json"` or use the existing describe path when exact
|
|
102
112
|
constraints matter. Small enums and both exact paths remain complete.
|
|
@@ -107,12 +117,13 @@ A connector may attach a deployment-owned guide as markdown, preserving the
|
|
|
107
117
|
original `usageGuide: string` configuration, or as
|
|
108
118
|
`{ content, summary?, required? }`. The structured form does not register a
|
|
109
119
|
connector or create a shared runtime template. `content` remains the markdown
|
|
110
|
-
returned verbatim by `skills`; `summary` is normalized and
|
|
111
|
-
|
|
112
|
-
fallback used by the skills listing: the first meaningful body
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
returned verbatim by `skills`; `summary` is normalized and refuses construction
|
|
121
|
+
when it exceeds 120 characters. When it is absent, Connecta derives the same
|
|
122
|
+
bounded fallback used by the skills listing: the first meaningful body
|
|
123
|
+
paragraph, joined across physical Markdown line wraps and shortened at a
|
|
124
|
+
sentence, clause, or word boundary, with a heading used only when the guide has
|
|
125
|
+
no body. `required: true` is reserved for generic API wrappers and
|
|
126
|
+
cross-operation conventions a complete downstream schema cannot express.
|
|
116
127
|
|
|
117
128
|
Search and describe results keep the existing `guide: "connector:<id>"`
|
|
118
129
|
pointer and add `guideSummary`. A matching tool also carries
|
|
@@ -143,8 +154,12 @@ the zero-tool page.
|
|
|
143
154
|
The built-in `usage` skill is byte-identical across deployments and says to
|
|
144
155
|
read it at most once per task. Connector guides remain scoped to the deployment
|
|
145
156
|
that listed them, even when two deployments happen to use identical content.
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
The always-loaded instructions and seven tool definitions own route selection,
|
|
158
|
+
the fail-closed boundary, and the minimum guest syntax. The usage skill owns
|
|
159
|
+
program selection detail, examples, runtime differences, and repair guidance.
|
|
160
|
+
This split avoids two normative copies while preserving a valid first program
|
|
161
|
+
for clients that never fetch the skill. Deployments without connector guides
|
|
162
|
+
receive none of the short conditional guide pointers in their definitions.
|
|
148
163
|
|
|
149
164
|
## Result representation
|
|
150
165
|
|
|
@@ -170,12 +185,18 @@ can shrink anything before it returns.
|
|
|
170
185
|
|
|
171
186
|
`fields` keeps its historical flat `{ "<path>": value }` result when every
|
|
172
187
|
requested dot-path resolves. Dot notation traverses objects; append `[]` to an
|
|
173
|
-
array field before continuing, as in `results[].id`.
|
|
188
|
+
array field before continuing, as in `results[].id`. Empty arrays resolve to
|
|
189
|
+
empty arrays. An exact downstream
|
|
174
190
|
`$connecta` field is always escaped under `data`. If any path misses—or that
|
|
175
191
|
reserved name is selected—the result carries matches under `data` and reserves `$connecta` for a
|
|
176
192
|
`type: "field_projection"` recovery record naming each `unmatchedFields`
|
|
177
|
-
entry.
|
|
178
|
-
|
|
193
|
+
entry. A path that resolves for only some array elements stays in `data` and
|
|
194
|
+
appears in `partialFields`; its unresolved positions serialize as `null`, while
|
|
195
|
+
the recovery record distinguishes them from genuine downstream nulls. A path
|
|
196
|
+
that misses every element appears in `unmatchedFields` and is omitted from
|
|
197
|
+
`data`. Both lists scale with requested paths, never with array length. When a
|
|
198
|
+
miss matches a declared array path except for `[]`, the record also carries the
|
|
199
|
+
traversal hint. The discriminator means downstream fields
|
|
179
200
|
named `data`, `projection`,
|
|
180
201
|
or `$connecta` remain ordinary values nested under `data`, never apparent
|
|
181
202
|
metadata. A declared output schema contributes a bounded `availableFields`
|
|
@@ -307,6 +328,16 @@ a tool. A read path that reaches an unannotated, write-capable, or destructive
|
|
|
307
328
|
tool returns `nextAction` for `call_destructive_tool` with the canonical
|
|
308
329
|
address. Nothing is executed by these records.
|
|
309
330
|
|
|
331
|
+
`connecta.describe` keeps failures inline so one miss cannot discard the other
|
|
332
|
+
schemas. Each failed entry keeps its human `error` and adds `errorDetails` with
|
|
333
|
+
the equivalent invocation `code` and `retryable`. Address and tool misses use
|
|
334
|
+
the same route-aware discovery action above. A close tool-name miss on a known
|
|
335
|
+
connector may also carry `suggestions`: at most three deterministically ranked
|
|
336
|
+
canonical addresses, with no scores or descriptions. An unknown connector
|
|
337
|
+
stays unscoped and has no suggestions. A catalog-load failure carries only
|
|
338
|
+
`code`, bounded `message`, `retryable`, and any `retryAfterMs`; discovery does
|
|
339
|
+
not inherit later additions to the call-failure envelope.
|
|
340
|
+
|
|
310
341
|
That route echoes the caller's own arguments back only while they fit a
|
|
311
342
|
512-byte budget, and then whole — never clipped. An error envelope is not
|
|
312
343
|
size-guarded the way a result is, so an unbounded echo would let a large
|
|
@@ -384,3 +415,6 @@ duplicate `additionalProperties` branches never reach the caller. A schema the l
|
|
|
384
415
|
validator cannot evaluate passes through to the provider. Provider error prose
|
|
385
416
|
is not parsed or guessed, so an unknown format remains
|
|
386
417
|
`connector_call_failed`.
|
|
418
|
+
|
|
419
|
+
Describe's nearby-address list uses the same three-item recovery bound. It
|
|
420
|
+
contains addresses only; it never serializes ranking scores or result prose.
|
|
@@ -50,6 +50,26 @@ password, not ordinary configuration. Mixpanel currently labels service-account
|
|
|
50
50
|
MCP authentication beta. Prefer OAuth unless the deployment is intentionally
|
|
51
51
|
headless.
|
|
52
52
|
|
|
53
|
+
## Conditional input contracts
|
|
54
|
+
|
|
55
|
+
Mixpanel's hosted descriptions enforce three cross-field conditions that its
|
|
56
|
+
input schemas do not encode. Connecta preserves those schemas unchanged under
|
|
57
|
+
[P1](./provider-conventions.md#p1--normalize-by-adding-never-by-rewriting), so
|
|
58
|
+
the maintained guide carries the missing call guidance:
|
|
59
|
+
|
|
60
|
+
- `Get-Business-Context` requires `project_id` or `organization_id`.
|
|
61
|
+
- `Get-Property-Values` requires `properties` or the deprecated `property`
|
|
62
|
+
alias. Event property values also require `event`.
|
|
63
|
+
- `List-Properties` accepts `names` or `query`, never both.
|
|
64
|
+
|
|
65
|
+
A read-only live audit on 2026-08-13 confirmed all three refusals against the
|
|
66
|
+
US hosted endpoint. They are reported upstream as
|
|
67
|
+
[`mixpanel/mixpanel-headless#202`](https://github.com/mixpanel/mixpanel-headless/issues/202).
|
|
68
|
+
The vetted catalog records the same audit's schema digests for all 63 tools,
|
|
69
|
+
so a later schema correction or regression appears by tool name in the
|
|
70
|
+
maintainer drift check. The guide can then shrink when the downstream schema
|
|
71
|
+
becomes complete; Connecta does not absorb the defect permanently.
|
|
72
|
+
|
|
53
73
|
The wrapper classifies the documented observational tools as reads and the
|
|
54
74
|
documented create, update, edit, merge, dismiss, duplicate, and delete tools as
|
|
55
75
|
writes. An unfamiliar tool the downstream leaves unannotated fails closed onto
|
package/documentation/notion.md
CHANGED
|
@@ -237,6 +237,23 @@ and all deliberately absent: this is a deliberate tool surface, not a mirror of
|
|
|
237
237
|
the API. Anything missing is reachable through a custom `api()` connector
|
|
238
238
|
beside this one, which remains a first-class path.
|
|
239
239
|
|
|
240
|
+
The 2026-03-11 contract also offers more fields on create and update. They were
|
|
241
|
+
reviewed after the 0.17.0 drift check and remain deliberately absent:
|
|
242
|
+
|
|
243
|
+
- `create_page` does not create workspace-private pages, apply templates,
|
|
244
|
+
choose page placement, or accept expanded icon and cover forms. Those change
|
|
245
|
+
ownership, start asynchronous content work, control ordering, or depend on
|
|
246
|
+
file surfaces. They are not extensions of the maintained page/row authoring
|
|
247
|
+
contract (#408).
|
|
248
|
+
- `update_page_properties` does not lock pages, apply templates, or erase page
|
|
249
|
+
content. Locking is coordination state, templates finish asynchronously, and
|
|
250
|
+
`erase_content` permanently deletes every child block through the API. None
|
|
251
|
+
belongs under an approval named for property replacement (#409).
|
|
252
|
+
|
|
253
|
+
`trash_page` stays separate and reversible. The current `create_page`,
|
|
254
|
+
`update_page_properties`, and `trash_page` request subsets remain valid against
|
|
255
|
+
the expanded published contract.
|
|
256
|
+
|
|
240
257
|
There is also **no guarded raw-REST escape hatch** — no `notion_api_get`, no
|
|
241
258
|
`notion_api_mutate`. The convention that permits one
|
|
242
259
|
([H14](./provider-conventions.md#h14--a-named-tool-must-beat-the-escape-hatch-and-the-escape-hatch-splits-by-safety))
|