@zackbart/connecta 0.24.2 → 0.24.4
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/AGENTS.md +18 -20
- package/CHANGELOG.md +205 -1
- package/README.md +5 -6
- package/dist/auth/bearer.js +2 -0
- package/dist/auth/downstream-oauth.d.ts +12 -1
- package/dist/auth/downstream-oauth.js +147 -35
- package/dist/branding.d.ts +31 -2
- package/dist/branding.js +116 -8
- package/dist/call-admission.d.ts +4 -0
- package/dist/call-admission.js +26 -0
- package/dist/catalog-drift.js +9 -4
- package/dist/catalog-service.d.ts +2 -0
- package/dist/catalog-service.js +25 -8
- package/dist/catalog.d.ts +2 -0
- package/dist/catalog.js +246 -121
- package/dist/connectors/api.d.ts +1 -1
- package/dist/connectors/api.js +21 -3
- package/dist/connectors/guarded-fetch.d.ts +6 -2
- package/dist/connectors/guarded-fetch.js +61 -24
- package/dist/connectors/remote-mcp.js +92 -57
- package/dist/errors.d.ts +28 -3
- package/dist/errors.js +60 -1
- package/dist/execute.js +85 -23
- package/dist/executor-result.js +3 -1
- package/dist/executors/quickjs-child.js +5 -1
- package/dist/executors/quickjs-protocol.d.ts +4 -0
- package/dist/executors/quickjs-runtime.d.ts +1 -1
- package/dist/executors/quickjs-runtime.js +38 -21
- package/dist/executors/quickjs.js +68 -27
- package/dist/index.d.ts +15 -1
- package/dist/index.js +36 -4
- package/dist/invocation.js +134 -93
- package/dist/mcp-result.js +3 -2
- package/dist/meta-tools.js +191 -36
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/view.d.ts +38 -1
- package/dist/operator-ui/view.js +71 -0
- package/dist/providers/cloudflare.d.ts +14 -2
- package/dist/providers/cloudflare.js +107 -16
- package/dist/providers/linear.d.ts +26 -4
- package/dist/providers/linear.js +19 -4
- package/dist/providers/mixpanel.d.ts +16 -3
- package/dist/providers/mixpanel.js +13 -2
- package/dist/providers/notion.d.ts +8 -1
- package/dist/providers/notion.js +83 -10
- package/dist/providers/revenuecat.d.ts +30 -4
- package/dist/providers/revenuecat.js +42 -4
- package/dist/providers/stripe.d.ts +7 -1
- package/dist/providers/stripe.js +30 -4
- package/dist/providers/vercel.js +11 -1
- package/dist/registry.d.ts +22 -2
- package/dist/registry.js +101 -13
- package/dist/routes/mcp.d.ts +4 -1
- package/dist/routes/mcp.js +84 -13
- package/dist/routes/oauth.js +4 -0
- package/dist/routes/shared.d.ts +1 -0
- package/dist/routes/shared.js +4 -4
- package/dist/server.js +15 -3
- package/dist/skills.js +6 -5
- package/dist/storage/file.d.ts +6 -2
- package/dist/storage/file.js +312 -34
- package/dist/storage/memory.js +12 -1
- package/dist/types.d.ts +37 -0
- package/dist/ui.js +18 -10
- package/dist/validate.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +193 -165
- package/documentation/auth.md +199 -145
- package/documentation/code-mode.md +433 -316
- package/documentation/meta-tools.md +363 -355
- package/examples/worker/AGENTS.md +2 -1
- package/examples/worker/README.md +12 -10
- package/examples/worker/src/index.ts +12 -15
- package/package.json +1 -2
- package/templates/node/.env.example +3 -3
- package/templates/node/AGENTS.md +5 -4
- package/templates/node/README.md +2 -1
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +23 -22
- package/documentation/call-admission.md +0 -142
- package/documentation/cloudflare.md +0 -471
- package/documentation/connector-guides.md +0 -176
- package/documentation/connectors.md +0 -389
- package/documentation/linear.md +0 -193
- package/documentation/mixpanel.md +0 -160
- package/documentation/notion.md +0 -308
- package/documentation/operations.md +0 -359
- package/documentation/operator-ui.md +0 -135
- package/documentation/optional-modules-upgrade.md +0 -243
- package/documentation/provider-conventions.md +0 -722
- package/documentation/request-admission.md +0 -170
- package/documentation/revenuecat.md +0 -305
- package/documentation/storage-and-credentials.md +0 -201
- package/documentation/stripe.md +0 -262
- package/documentation/upgrading.md +0 -754
- package/documentation/vercel.md +0 -241
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
# Code mode — the guest API contract
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
The [code-first exploration](https://github.com/zackbart/connecta/blob/main/records/code-first-exploration.md) is the evidence behind
|
|
16
|
-
the direction; [`ethos.md`](../ethos.md) carries the verdicts. Where its prototype
|
|
17
|
-
and this document disagree, this document wins. Clause identifiers (`A1`, `E3`, …)
|
|
18
|
-
are stable and cited by the tests in [Verification](#verification).
|
|
3
|
+
The normative description of what a program written for `execute_code` is
|
|
4
|
+
promised: what it can reach, what it gets back, how failures look, what it may
|
|
5
|
+
retry, what bounds it runs under, and what it leaves behind in activity. It is
|
|
6
|
+
specified in prose first and implemented second.
|
|
7
|
+
|
|
8
|
+
Two executors implement it: QuickJS in a child process on Node, and
|
|
9
|
+
`DynamicWorkerExecutor` from `@cloudflare/codemode` on Workers. Divergence is a
|
|
10
|
+
bug unless it appears in [Executor exceptions](#executor-exceptions), which
|
|
11
|
+
names the reason; a third executor is implementable from this document alone.
|
|
12
|
+
[`ethos.md`](../ethos.md) carries the verdicts behind the shape,
|
|
13
|
+
[`meta-tools.md`](./meta-tools.md) owns the top-level tool contract, and clause
|
|
14
|
+
identifiers (`A1`, `E3`, …) are stable and cited by [Verification](#verification).
|
|
19
15
|
|
|
20
16
|
## Deploy-time capability
|
|
21
17
|
|
|
@@ -32,10 +28,7 @@ QuickJS subpath:
|
|
|
32
28
|
import { createConnecta } from "@zackbart/connecta";
|
|
33
29
|
import { quickJsExecutor } from "@zackbart/connecta/quickjs";
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
executor: quickJsExecutor(),
|
|
37
|
-
// connectors, auth, storage…
|
|
38
|
-
});
|
|
31
|
+
createConnecta({ executor: quickJsExecutor() /* connectors, auth, storage… */ });
|
|
39
32
|
```
|
|
40
33
|
|
|
41
34
|
`quickJsExecutor()` runs each program in a disposable child-process sandbox; its
|
|
@@ -44,17 +37,16 @@ on the executor. Server bundlers must keep the `@zackbart/connecta/quickjs`
|
|
|
44
37
|
package files external so the child entry stays on disk. The
|
|
45
38
|
[Node template](../templates/node/README.md) carries the complete setup.
|
|
46
39
|
|
|
47
|
-
On Cloudflare Workers
|
|
40
|
+
On Cloudflare Workers the Worker Loader binding is the sandbox, and Dynamic
|
|
41
|
+
Workers require the Workers Paid plan:
|
|
48
42
|
|
|
49
43
|
```ts
|
|
50
|
-
createConnecta({
|
|
51
|
-
executor: new DynamicWorkerExecutor({ loader: env.LOADER }),
|
|
52
|
-
// connectors, auth, storage…
|
|
53
|
-
});
|
|
44
|
+
createConnecta({ executor: new DynamicWorkerExecutor({ loader: env.LOADER }) });
|
|
54
45
|
```
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
`globalOutbound` grant ambient guest authority and violate `P2`. The
|
|
47
|
+
The supported constructor passes only `loader`; `bindings`, `modules`, or
|
|
48
|
+
`globalOutbound` grant ambient guest authority and violate `P2`. The
|
|
49
|
+
[Worker example](../examples/worker/README.md#code-mode) has the full setup.
|
|
58
50
|
|
|
59
51
|
## What an executor must implement
|
|
60
52
|
|
|
@@ -86,19 +78,19 @@ Connecta passes exactly one provider, named `connecta`. An executor must:
|
|
|
86
78
|
called with the program's arguments and awaited. Connecta supplies `search`,
|
|
87
79
|
`describe`, `call`, and `emit`.
|
|
88
80
|
2. **Evaluate `prelude` after the provider globals exist and before the
|
|
89
|
-
program.**
|
|
90
|
-
errors
|
|
91
|
-
3. **Marshal values as JSON**
|
|
81
|
+
program.** It is trusted host code; connecta uses it to restore typed host
|
|
82
|
+
errors without exposing the private error frame.
|
|
83
|
+
3. **Marshal values as JSON** both directions (`P3`), and reject a host call
|
|
92
84
|
whose function is not an own property of `fns` — the guest can ask for
|
|
93
|
-
|
|
85
|
+
inherited members too.
|
|
94
86
|
4. **Return, never throw, for a failed program**: set `error` to the guest's
|
|
95
87
|
message, leave `result` undefined. `createExecuteTool` reads `error` first and
|
|
96
88
|
matches it back to the failures recorded during the run, which is how an
|
|
97
89
|
uncaught tool failure keeps its type (`E1`).
|
|
98
90
|
5. **Capture `console.log`, `console.warn`, and `console.error`** into `logs` in
|
|
99
91
|
call order (`R5`), bounding what it retains.
|
|
100
|
-
6. **Bound the guest**: wall clock, memory, stack, and CPU (`L3`, `L5`)
|
|
101
|
-
|
|
92
|
+
6. **Bound the guest**: wall clock, memory, stack, and CPU (`L3`, `L5`), staying
|
|
93
|
+
inside the tested `P2`/`X5` boundary.
|
|
102
94
|
7. **Grant no ambient authority of its own.** Never back this with `eval` or
|
|
103
95
|
`node:vm`: the sandbox is a containment layer on top of connecta's boundary,
|
|
104
96
|
not a replacement for it, and every capability arrives through `fns`.
|
|
@@ -107,63 +99,60 @@ Optionally implement `AdmittingExecutor` (`acquire()` returning a lease whose
|
|
|
107
99
|
`execute` runs once) for bounded admission (`L7`) and `close()` for shutdown;
|
|
108
100
|
connecta wraps a plain `Executor` with `withExecutorAdmission` otherwise. The
|
|
109
101
|
optional `name` — else a class's constructor name, which a minifier may rewrite
|
|
110
|
-
— is
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
of an executor — `connecta.emit` is just another provider function (`M8`).
|
|
102
|
+
— is the executor name `/health` and `connecta doctor` report.
|
|
103
|
+
[Emitted output](#emitted-output) asks nothing of an executor: `connecta.emit`
|
|
104
|
+
is just another provider function (`M8`).
|
|
114
105
|
|
|
115
106
|
## The program
|
|
116
107
|
|
|
117
|
-
**P1.** A program is one JavaScript `async` arrow-function expression
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
**P3.** Values cross the host bridge as JSON. Arguments must be
|
|
135
|
-
JSON-serializable and results arrive as plain JSON values. A value outside JSON —
|
|
136
|
-
a cycle, a `BigInt`, a function, a class instance — never round-trips: it either
|
|
108
|
+
**P1.** A program is one JavaScript `async` arrow-function expression, evaluated
|
|
109
|
+
once; its resolved value is the result. Both executors also accept
|
|
110
|
+
markdown-fenced code and a bare statement body, normalizing those differently —
|
|
111
|
+
a courtesy to model output, not contract, so anything else may be accepted,
|
|
112
|
+
rejected, or reinterpreted.
|
|
113
|
+
|
|
114
|
+
**P2.** The only capabilities in the contract are `connecta.search`,
|
|
115
|
+
`connecta.describe`, `connecta.call`, `connecta.emit`, and `console.log` /
|
|
116
|
+
`console.warn` / `console.error`, captured and returned. Anything else a runtime
|
|
117
|
+
exposes is outside the portable contract and must not be used. QuickJS grants
|
|
118
|
+
none of it. A loader-only Dynamic Worker denies external egress and filesystem
|
|
119
|
+
access and keeps its environment maps empty, but exposes the globals and runtime
|
|
120
|
+
builtins in `X5`.
|
|
121
|
+
|
|
122
|
+
**P3.** Values cross the host bridge as JSON: arguments must be
|
|
123
|
+
JSON-serializable and results arrive as plain JSON. A value outside JSON — a
|
|
124
|
+
cycle, a `BigInt`, a function, a class instance — never round-trips; it either
|
|
137
125
|
ends the run with an error or is converted lossily, executor's choice (`X9`).
|
|
138
|
-
Return JSON-shaped data and the question does not arise.
|
|
139
126
|
|
|
140
|
-
**P4.** Nothing survives an execution
|
|
127
|
+
**P4.** Nothing survives an execution: no module scope, cache, or scratch
|
|
128
|
+
storage carried to the next program, and no request-bound object outliving its
|
|
129
|
+
request. Within one execution, host calls share one downstream request scope.
|
|
130
|
+
`S9`'s output observation is host-owned catalog metadata, not guest memory — a
|
|
131
|
+
later program receives a labeled field/type schema through discovery, never a
|
|
132
|
+
prior value or object.
|
|
141
133
|
|
|
142
|
-
**P5.** Plain JavaScript only
|
|
143
|
-
does not import: QuickJS blocks imports,
|
|
144
|
-
runtime modules
|
|
134
|
+
**P5.** Plain JavaScript only; TypeScript syntax is a syntax error. Portable
|
|
135
|
+
code does not import: QuickJS blocks imports, Dynamic Workers expose the `X5`
|
|
136
|
+
runtime modules, and neither exposes `require`.
|
|
145
137
|
|
|
146
138
|
## Addressing
|
|
147
139
|
|
|
148
|
-
**A1.** A tool has one canonical address, `<connectorId>.<toolName>`, exactly
|
|
149
|
-
|
|
140
|
+
**A1.** A tool has one canonical address, `<connectorId>.<toolName>`, exactly as
|
|
141
|
+
discovery returns it. Call it with `connecta.call(address, args)`. Punctuation
|
|
150
142
|
is preserved; no JavaScript identifier conversion takes place.
|
|
151
143
|
|
|
152
|
-
**A2.** Connectors create no guest globals
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
Clauses A3–A5 belonged to shortcut dispatch and are retired. Clients and stored
|
|
158
|
-
programs should follow the [migration guide](./upgrading.md#0230-program-api-pruning).
|
|
144
|
+
**A2.** Connectors create no guest globals, so an id resembling a JavaScript
|
|
145
|
+
builtin, or one that would collide after sanitization, stays usable through its
|
|
146
|
+
canonical address. The bounded connector inventory in the tool description shows
|
|
147
|
+
canonical ids with bounded configured titles when present. Clauses A3–A5
|
|
148
|
+
belonged to shortcut dispatch and stay retired; nothing reuses those ids.
|
|
159
149
|
|
|
160
150
|
## The surface
|
|
161
151
|
|
|
162
|
-
Four functions, all `async`: `search`, `describe`, `call`, and `emit`. Nothing
|
|
163
|
-
property yields a function — the guest namespace
|
|
164
|
-
connecta.toString` is `"function"` — but *calling* it
|
|
165
|
-
resolves only own members of the provider's `fns`.
|
|
166
|
-
documented functions as the whole surface.
|
|
152
|
+
Four functions, all `async`: `search`, `describe`, `call`, and `emit`. Nothing
|
|
153
|
+
else works. Reading any other property yields a function — the guest namespace
|
|
154
|
+
is a Proxy, so `typeof connecta.toString` is `"function"` — but *calling* it
|
|
155
|
+
fails, because the host resolves only own members of the provider's `fns`.
|
|
167
156
|
|
|
168
157
|
### connecta.search
|
|
169
158
|
|
|
@@ -180,49 +169,104 @@ const page = await connecta.search({
|
|
|
180
169
|
});
|
|
181
170
|
```
|
|
182
171
|
|
|
183
|
-
**S1.** Returns one flat page: `{ tools, total, offset, limit, hasMore }`, plus
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
carries
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
172
|
+
**S1.** Returns one flat page: `{ tools, total, offset, limit, hasMore }`, plus
|
|
173
|
+
`nextOffset` when more remains and `matchMode: "partial"` when no tool matched
|
|
174
|
+
every term. Top-level `search_tools` is different — it returns
|
|
175
|
+
`{ connectors: [{ id, tools }], total, offset, limit, hasMore }`. Ranking is
|
|
176
|
+
[lexical discovery](./meta-tools.md#lexical-discovery)'s, and rows expose
|
|
177
|
+
neither lexical scores nor per-result coverage. An empty or whitespace-only
|
|
178
|
+
query browses; non-empty input with no ASCII lexical terms returns no tools plus
|
|
179
|
+
bounded no-match analysis in `queryAnalysis`; mixed input searches with its
|
|
180
|
+
ASCII terms.
|
|
181
|
+
|
|
182
|
+
Each entry carries `address`, `name`, the configured `connectorTitle` when
|
|
183
|
+
present (normalized whitespace, at most 120 UTF-8 bytes), and — when requested —
|
|
184
|
+
`description`, `inputSchema`, `outputSchema`, and `annotations`. An entry whose
|
|
185
|
+
connector carries a usage guide also carries `guide` (the skill name) and a
|
|
186
|
+
bounded `guideSummary`, requested or not. An output shape learned under `S9`
|
|
187
|
+
also carries `outputSchemaSource: "observed"`; provider declarations carry no
|
|
188
|
+
source marker.
|
|
189
|
+
|
|
190
|
+
Compact shapes omit property prose and put required fields first, under these
|
|
191
|
+
bounds:
|
|
192
|
+
|
|
193
|
+
| Bound | Value |
|
|
194
|
+
| --- | --- |
|
|
195
|
+
| Rendered shape | 1,024 UTF-8 bytes each |
|
|
196
|
+
| Work per shape | 2,000 visits across schema nodes, property and required names, literal values, any constraint-free retry, and its key-only fallback |
|
|
197
|
+
| Each enum node | 256 of those bytes |
|
|
198
|
+
| Resolved `$ref` text | reused within one walk |
|
|
199
|
+
|
|
200
|
+
About three near-cap enum nodes therefore coexist while the final quarter stays
|
|
201
|
+
for surrounding syntax, and above 1,024 bytes the global fallback applies.
|
|
202
|
+
Exhausted work yields `unknown /* truncated */`; a capped enum preserves whole
|
|
203
|
+
values before `unknown` plus an exact omitted-value count; an empty enum renders
|
|
204
|
+
as `never` and small enums stay complete. Either cap sets `inputSchemaTruncated`
|
|
205
|
+
or `outputSchemaTruncated`, and a shape-wide cap stays structurally valid with
|
|
206
|
+
`unknown` types plus `/* truncated */`. Compound forms share those bounds:
|
|
207
|
+
`prefixItems` renders as a tuple with the declared `items` rest, an `unknown[]`
|
|
208
|
+
rest when open, or no rest when `items` is false; `dependentSchemas` and
|
|
209
|
+
`if`/`then`/`else` keep the base shape plus `/* conditional */` and set the
|
|
210
|
+
truncation flag; `$dynamicRef` resolves a same-named definition like `$ref`, and
|
|
211
|
+
an unresolved one becomes `unknown` with the flag. For omitted exact constraints
|
|
212
|
+
use `format: "json"` or JSON search.
|
|
213
|
+
|
|
214
|
+
**S1a.** `connector` loads only the named catalog; omit it only when the
|
|
215
|
+
integration is ambiguous, because an unscoped search fans out across every
|
|
216
|
+
configured connector. `safety: "readOnly"` returns exactly the tools available
|
|
217
|
+
through `connecta.call`; `"approvalRequired"` returns the complementary
|
|
218
|
+
fail-closed class, including false, missing, and contradictory annotations;
|
|
219
|
+
omitted or `"all"` preserves the complete catalog. These filters grant no
|
|
220
|
+
authority and change no admission decision.
|
|
221
|
+
|
|
222
|
+
**S2.** A requested object schema carries `inputKeys`, `requiredInputKeys`
|
|
223
|
+
(declared properties only), and `outputKeys`: the names the rendered schema
|
|
224
|
+
shows, ready to check before building arguments. Select on inputs, truncation,
|
|
225
|
+
safety, and outputs rather than lexical rank, and use `outputKeys` instead of
|
|
226
|
+
guessed roots. A non-object schema — a union, an array, an unresolvable `$ref` —
|
|
227
|
+
carries no lists rather than empty ones, because absent means "read the schema"
|
|
228
|
+
where `[]` would claim the tool takes no fields. The lists come from the walk
|
|
229
|
+
that renders the compact schema, so a top-level `$ref` resolves and an `allOf`
|
|
230
|
+
composes. A zero-input object keeps `inputKeys: []` and `requiredInputKeys: []`;
|
|
231
|
+
an output object with no declared properties omits `outputKeys`; a truncated
|
|
232
|
+
schema omits the corresponding list. `search_tools` carries the same metadata
|
|
233
|
+
whenever schemas are requested, and `includeSchemaKeys: false` buys the bytes
|
|
234
|
+
back.
|
|
235
|
+
|
|
236
|
+
**S3.** Discovery bounds throw rather than silently shrink. `invalid_args`
|
|
237
|
+
covers a `limit` outside 1–100, an `offset` that is not a non-negative integer,
|
|
238
|
+
a supplied `query` that is not a string, and a `connector` over 512 UTF-8 bytes
|
|
239
|
+
— refused whole, since a clipped scope names a different connector. Omitted
|
|
240
|
+
`offset` starts at 0; omitted `query` browses. A page whose serialized form
|
|
241
|
+
exceeds 256,000 bytes is `result_too_large`; a program's page is measured as its
|
|
242
|
+
serialized value, while top-level `search_tools` measures the complete tool
|
|
243
|
+
result including both copies and JSON escaping. Each error carries a correction
|
|
244
|
+
hint and the stable `code`, `retryable`, and `details` fields (`E1`).
|
|
206
245
|
|
|
207
246
|
### connecta.describe
|
|
208
247
|
|
|
209
248
|
```js
|
|
210
249
|
const one = await connecta.describe({ address: "ci.get_run" });
|
|
211
|
-
|
|
212
|
-
const { tools } = await connecta.describe({
|
|
250
|
+
const many = await connecta.describe({
|
|
213
251
|
addresses: ["ci.get_run", "ci.get_job_logs"], // ≤ 100
|
|
214
252
|
format: "compact", // or "json"
|
|
215
253
|
fullDescriptions: false,
|
|
216
254
|
});
|
|
217
255
|
```
|
|
218
256
|
|
|
219
|
-
**S4.** Returns `{ tools }` in order, one entry per address
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
257
|
+
**S4.** Returns `{ tools }` in order, one entry per address; one bad address
|
|
258
|
+
never fails the whole call. An unknown address or failed catalog returns `error`
|
|
259
|
+
plus typed `errorDetails` (`code`, `message`, `retryable`). Misses carry a
|
|
260
|
+
route-aware `nextAction`, a close miss may add three canonical `suggestions`,
|
|
261
|
+
and catalog failures add only `retryAfterMs` when known. Each failed entry
|
|
262
|
+
clamps its caller-authored `address` to 512 UTF-8 bytes with an `…` marker, and
|
|
263
|
+
entry order correlates a clipped address with its request; successes keep
|
|
264
|
+
canonical addresses. More than 100 addresses is `invalid_args`, and the same
|
|
265
|
+
256,000-byte ceiling applies. Compact describe keeps property prose within a
|
|
266
|
+
separate 8,192-byte UTF-8 shape cap and shares search's 2,000-visit budget; a
|
|
267
|
+
capped shape sets `inputSchemaTruncated` or `outputSchemaTruncated`, and
|
|
268
|
+
`format: "json"` gives the exact schema. A success whose output shape came from
|
|
269
|
+
`S9` carries `outputSchemaSource: "observed"`.
|
|
226
270
|
|
|
227
271
|
### connecta.call
|
|
228
272
|
|
|
@@ -236,12 +280,11 @@ when present, otherwise text content JSON-parsed when it parses and the raw text
|
|
|
236
280
|
when it does not; a downstream result flagged `isError` throws. Omitted `args`
|
|
237
281
|
is treated as `{}`.
|
|
238
282
|
|
|
239
|
-
**S6.** Every call goes through the same catalog,
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
reach.
|
|
283
|
+
**S6.** Every call goes through the same catalog, fail-closed read-only
|
|
284
|
+
predicate, admission, credential containment, timeout classification, health
|
|
285
|
+
accounting, and activity recording as an ordinary meta-tool call. The sandbox is
|
|
286
|
+
an additional containment layer, not a second implementation of the boundary,
|
|
287
|
+
and nothing a program does widens what it can reach.
|
|
245
288
|
|
|
246
289
|
### Parallel calls
|
|
247
290
|
|
|
@@ -250,21 +293,28 @@ program, or `Promise.allSettled` to retain every outcome in input order. Both
|
|
|
250
293
|
use the same per-call admission, host-call budget, deadlines, and activity path
|
|
251
294
|
as sequential calls. There is no separate batch size or result contract.
|
|
252
295
|
|
|
253
|
-
```js
|
|
254
|
-
const outcomes = await Promise.allSettled([
|
|
255
|
-
connecta.call("ci.get_run", { runId: 42 }),
|
|
256
|
-
connecta.call("ci.list_jobs", { runId: 42 }),
|
|
257
|
-
]);
|
|
258
|
-
return outcomes.map((outcome) => outcome.status === "fulfilled"
|
|
259
|
-
? { ok: true, data: outcome.value }
|
|
260
|
-
: { ok: false, code: outcome.reason.code, message: outcome.reason.message });
|
|
261
|
-
```
|
|
262
|
-
|
|
263
296
|
**S8.** A rejected promise retains the caught error's `code`, `retryable`, and
|
|
264
|
-
`details`. Project those fields before returning
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
**S9.** A successful explicitly read-only call whose provider declared no
|
|
297
|
+
`details`. Project those fields before returning — an `Error` object is not a
|
|
298
|
+
JSON result contract.
|
|
299
|
+
|
|
300
|
+
**S9.** A successful explicitly read-only call whose provider declared no
|
|
301
|
+
`outputSchema` passively learns one from the unwrapped result. The observation
|
|
302
|
+
keeps field names and broad JSON types only — no arguments, scalar values, raw
|
|
303
|
+
results, code, credentials, or errors — and property names may be user-authored.
|
|
304
|
+
Objects stay open, every field stays optional, and discovery labels the shape
|
|
305
|
+
`outputSchemaSource: "observed"` so a model cannot mistake runtime evidence for
|
|
306
|
+
a provider contract. Later observations merge fields and types in a
|
|
307
|
+
process-local 256-entry LRU; a provider declaration always wins. Inference stops
|
|
308
|
+
at depth 6, 128 schema nodes, 48 properties per object, 32 inspected array
|
|
309
|
+
items, and 128 UTF-8 bytes per property name; `__proto__`, `constructor`, and
|
|
310
|
+
`prototype` names are discarded. A tool definition over 64 KiB or an observed
|
|
311
|
+
schema over 16 KiB is ignored. An entry expires after 24 hours and carries the
|
|
312
|
+
exact serialized tool definition, so a changed catalog entry, process restart,
|
|
313
|
+
or isolate eviction starts cold. A failed call or failed result-processing step
|
|
314
|
+
learns nothing, and any observation failure is discarded without changing a
|
|
315
|
+
successful call. No discovery read, timer, refresh, background job, or storage
|
|
316
|
+
adapter executes or persists work for this cache: the result-sampling refusal in
|
|
317
|
+
[#282](https://github.com/zackbart/connecta/issues/282) stands.
|
|
268
318
|
|
|
269
319
|
### connecta.emit
|
|
270
320
|
|
|
@@ -277,7 +327,8 @@ clauses are [Emitted output](#emitted-output) (`M1`–`M10`).
|
|
|
277
327
|
|
|
278
328
|
## Errors
|
|
279
329
|
|
|
280
|
-
**E1.** There are three error channels. Connecta failures are typed whether
|
|
330
|
+
**E1.** There are three error channels. Connecta failures are typed whether
|
|
331
|
+
caught or uncaught.
|
|
281
332
|
|
|
282
333
|
| Channel | Shape | Typed? |
|
|
283
334
|
| --- | --- | --- |
|
|
@@ -285,11 +336,24 @@ clauses are [Emitted output](#emitted-output) (`M1`–`M10`).
|
|
|
285
336
|
| An uncaught **tool or discovery** failure, as the model sees it | `{ error: { code, message, retryable, … } }` with `isError` | yes |
|
|
286
337
|
| Program or execution failure (`E5`, `E6`, a bridge bound in `L6`) | error text | no |
|
|
287
338
|
|
|
288
|
-
Both executor bridges reduce a rejected host call to `new Error(message)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
339
|
+
Both executor bridges reduce a rejected host call to `new Error(message)`, and
|
|
340
|
+
connecta restores the typed failure in a trusted prelude with a per-execution
|
|
341
|
+
authenticated frame (`X11`). `message` stays human text, capped at 2,000
|
|
342
|
+
JSON-serialized characters including quotes and an `…` marker when clipped.
|
|
343
|
+
`code` and `retryable` are the stable branch fields; `details` carries the host
|
|
344
|
+
classification and fits 3,700 serialized characters. Optional recovery metadata
|
|
345
|
+
that would exceed that bound is omitted whole, preserving `code`, `message`,
|
|
346
|
+
`retryable`, and `retryAfterMs`, because a clipped recovery address or argument
|
|
347
|
+
describes a different call. This covers `call`, `search`, `describe`, `emit`, and
|
|
348
|
+
the host-call budget. Program-authored errors stay untyped, and code must never
|
|
349
|
+
parse error prose. An `unavailable` classification may add `details.host`, an
|
|
350
|
+
HTTP(S) origin of at most 253 UTF-8 bytes, and `details.code`, a validated
|
|
351
|
+
network errno, undici transport code, or `timeout` of at most 32 bytes; neither
|
|
352
|
+
enters activity.
|
|
353
|
+
|
|
354
|
+
**E2.** The taxonomy: `retryable` is what connecta reports, `Y3` what a program
|
|
355
|
+
may do. A provider maps each downstream failure to the code that tells the
|
|
356
|
+
caller what to do next, and never invents a cause it was not told.
|
|
293
357
|
|
|
294
358
|
| Code | Raised when | `retryable` |
|
|
295
359
|
| --- | --- | --- |
|
|
@@ -298,10 +362,10 @@ Program-authored errors stay untyped, and code must never parse error prose.
|
|
|
298
362
|
| `destructive_tool_requires_approval` | the tool is not explicitly read-only | false |
|
|
299
363
|
| `auth_required` | the credential is missing, expired, or rejected | false |
|
|
300
364
|
| `invalid_args` | arguments or discovery bounds were rejected | false |
|
|
301
|
-
| `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
|
|
365
|
+
| `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 | false |
|
|
302
366
|
| `input_required_unsupported` | a downstream asked for mid-call input | false |
|
|
303
367
|
| `rate_limited` | the downstream reported a rate limit | true |
|
|
304
|
-
| `unavailable` | the downstream is down or unreachable | true |
|
|
368
|
+
| `unavailable` | the downstream is down or unreachable; optional sanitized `details.host` and `details.code` describe the transport failure without paths, queries, credentials, or provider prose | true |
|
|
305
369
|
| `timeout` | the per-call deadline (`execute.hostCallTimeoutMs`, default 15 s) expired | true |
|
|
306
370
|
| `cancelled` | the run ended while this call was in flight (`E5`) | false |
|
|
307
371
|
| `connector_call_failed` | anything else the connector threw | per message |
|
|
@@ -313,8 +377,8 @@ Program-authored errors stay untyped, and code must never parse error prose.
|
|
|
313
377
|
**E3.** `auth_required` carries the same recovery envelope as `call_tool`:
|
|
314
378
|
`connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
|
|
315
379
|
`unavailable`), `nextAction` naming `authorize_connector`, and a `retry`
|
|
316
|
-
sentence. A program cannot recover credentials — only an operator can — so
|
|
317
|
-
|
|
380
|
+
sentence. A program cannot recover credentials — only an operator can — so stop
|
|
381
|
+
and let the failure reach the model.
|
|
318
382
|
|
|
319
383
|
**E4.** An unannotated, write-capable, or destructive tool stays refused with
|
|
320
384
|
`destructive_tool_requires_approval`; `nextAction` carries its canonical address
|
|
@@ -326,34 +390,55 @@ never goes downstream, and generated code cannot mint the capability.
|
|
|
326
390
|
**E5.** Failures of the *execution*, not of a call, never appear inside the
|
|
327
391
|
guest: admission rejection (`executor_overloaded`, retryable, with
|
|
328
392
|
`retryAfterMs`), cancellation (`executor_cancelled`), shutdown
|
|
329
|
-
(`executor_closed`), deadline expiry, and sandbox crashes end the run and
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
393
|
+
(`executor_closed`), deadline expiry, and sandbox crashes end the run and reach
|
|
394
|
+
the model as an error result. One seam: a host call still in flight when the run
|
|
395
|
+
is cancelled fails with `cancelled`, catchable on the way out but never worth
|
|
396
|
+
acting on (`Y3`). When shutdown tears down a program that had already started,
|
|
397
|
+
accepted blocks are reported as discarded under `M4`; a failure before execution
|
|
398
|
+
started carries no discard fields. A returned `error` field is a failure even
|
|
399
|
+
when empty — an empty string reports `executor_failed` with
|
|
400
|
+
`Error: Execution failed without an error message.`
|
|
334
401
|
|
|
335
402
|
**E6.** An error the program raises itself — a `TypeError`, a call to a
|
|
336
|
-
`connecta` member that is not a provider function (
|
|
337
|
-
|
|
338
|
-
carrying that message.
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
message
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
**
|
|
403
|
+
`connecta` member that is not a provider function (an inherited one like
|
|
404
|
+
`toString` included), a `throw` of its own — ends the run with an untyped error
|
|
405
|
+
result carrying that message. One precedence rule: connecta recognizes an
|
|
406
|
+
escaped tool failure by its message, exactly first and by containment second for
|
|
407
|
+
messages of at least eight characters, so a program that *wraps* a failure's
|
|
408
|
+
message still reports the underlying typed failure — keeping the type beats
|
|
409
|
+
keeping the prose. Matching retains only the most recent 64 failures per
|
|
410
|
+
execution, caught refusals included; an older escaped message stays an untyped
|
|
411
|
+
execution failure. An empty terminal error uses the fixed message in `E5`.
|
|
412
|
+
|
|
413
|
+
**E7.** `retryable` for `unknown_address`, `unknown_tool`, and
|
|
414
|
+
`destructive_tool_requires_approval` is pinned false, never inferred from an
|
|
415
|
+
address containing `503`, `429`, or `temporar`. The first two carry
|
|
416
|
+
`nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }`
|
|
417
|
+
— scoped discovery keyed to the surface the caller has, since a program cannot
|
|
418
|
+
call `search_tools`. The message, the derived `query`, and a failed describe
|
|
419
|
+
entry's `address` clamp caller-authored text to 512 UTF-8 bytes with an `…`
|
|
420
|
+
marker, because those values land in both the text content and
|
|
421
|
+
`structuredContent`, where an invented 50 KB address would produce a refusal far
|
|
422
|
+
past the deployment's result cap. A clipped address still identifies the mistake
|
|
423
|
+
by its position; a short one is exact and untagged.
|
|
424
|
+
|
|
425
|
+
**E8.** A remote MCP tool whose advertised schema rejects the call fails before
|
|
426
|
+
provider dispatch with `invalid_args`, carrying bounded, value-free
|
|
427
|
+
`{ path, code, expected }` findings and the same scoped
|
|
428
|
+
`function: "connecta.search"` recovery every other in-program miss gets. A
|
|
429
|
+
declared property reports the schema keyword that failed, never the validator's
|
|
430
|
+
duplicate `additionalProperties` branch; a truly undeclared property still
|
|
431
|
+
reports `additionalProperties`. Unsupported schemas pass through; unrecognized
|
|
432
|
+
provider prose remains `connector_call_failed`.
|
|
347
433
|
|
|
348
434
|
## Results and projection
|
|
349
435
|
|
|
350
436
|
**R1 (verdict: projection stays explicit).** A program's return value reaches
|
|
351
437
|
the model unchanged except for the size guard in `R2`. Connecta does not
|
|
352
438
|
summarize, reshape, or field-select it, and there is no automatic projection
|
|
353
|
-
mode
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
earn their way in only if [#222](https://github.com/zackbart/connecta/issues/222)
|
|
439
|
+
mode: the measured byte win came from *program-authored* projection, while a
|
|
440
|
+
host heuristic would drop deliberately returned fields invisibly. Helpers earn
|
|
441
|
+
their way in only if [#222](https://github.com/zackbart/connecta/issues/222)
|
|
357
442
|
shows programs failing to project on their own.
|
|
358
443
|
|
|
359
444
|
**R2.** The boundary is 24,000 serialized characters (~6k tokens). A value over
|
|
@@ -373,30 +458,45 @@ size of what the program returned and truncation happens exactly once no matter
|
|
|
373
458
|
how many hops the value takes.
|
|
374
459
|
|
|
375
460
|
**R3.** Truncation is a *successful* result, not an error: the program ran, and
|
|
376
|
-
|
|
377
|
-
|
|
461
|
+
the honest report is that its answer was too large. The fix is a program that
|
|
462
|
+
returns less, which is why the envelope says so.
|
|
378
463
|
|
|
379
464
|
**R4 (verdict: no result paging for programs).** A truncated program result
|
|
380
|
-
carries no `get_result` handle, unlike `call_tool`. `get_result` exists so a
|
|
381
|
-
can page a *downstream payload* it could not shrink; a program can shrink
|
|
382
|
-
anything, so paging its result would reward the
|
|
383
|
-
remove
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
465
|
+
carries no `get_result` handle, unlike `call_tool`. `get_result` exists so a
|
|
466
|
+
model can page a *downstream payload* it could not shrink; a program can shrink
|
|
467
|
+
anything, so paging its result would reward the behavior code mode exists to
|
|
468
|
+
remove.
|
|
469
|
+
|
|
470
|
+
**R5.** `console.log`, `console.warn`, and `console.error` are captured in call
|
|
471
|
+
order and returned as one `logs` string, capped at 4,000 characters with a
|
|
472
|
+
truncation marker. Logs survive program failure through either a returned error
|
|
473
|
+
result or a thrown error carrying `logs: string[]`. QuickJS streams captured
|
|
474
|
+
entries to its parent and preserves the received prefix on cancellation,
|
|
475
|
+
shutdown, deadline termination, child crashes, and IPC failures (`X4`). How a
|
|
476
|
+
non-string argument renders is not contract (`X4`).
|
|
477
|
+
|
|
478
|
+
**R6.** Nothing else is added to a normal program result. `diagnostics: true`
|
|
479
|
+
adds one request-local, payload-free `diagnostics` block; a program that emitted
|
|
480
|
+
adds `emitted: N` and its blocks (`M2`). Omitted, `false`, and emit-free are
|
|
481
|
+
byte-for-byte the ordinary response path
|
|
482
|
+
([#247](https://github.com/zackbart/connecta/issues/247)).
|
|
483
|
+
|
|
484
|
+
**R7.** Diagnostic timing separates admission, provider setup, total executor
|
|
485
|
+
wall time, catalog work, and connector work. Catalog and connector values are
|
|
486
|
+
cumulative, so parallel work can exceed executor wall time. Each used operation
|
|
487
|
+
kind (`search`, `describe`, `call`) gets one aggregate with count, failures,
|
|
488
|
+
duration, returned serialized bytes, and catalog/connector time.
|
|
489
|
+
|
|
490
|
+
**R8.** Diagnostics contain measurements and fixed operation names only: no
|
|
491
|
+
addresses, arguments, results, code, credentials, logs, or raw errors. Result
|
|
492
|
+
sizes are numbers, never previews. The collector exists only for the opted-in
|
|
493
|
+
request; it is not activity, a session, or a stream.
|
|
393
494
|
|
|
394
495
|
## Emitted output
|
|
395
496
|
|
|
396
497
|
MCP-native output a return value cannot carry: base64 is not projectable, so a
|
|
397
|
-
block that survives intake uncapped (`S5`) must not die at the `R2` exit
|
|
398
|
-
|
|
399
|
-
[design record](https://github.com/zackbart/connecta/blob/main/records/rich-output-design.md) and `ethos.md`
|
|
498
|
+
block that survives intake uncapped (`S5`) must not die at the `R2` exit guard.
|
|
499
|
+
The refused alternatives are in `ethos.md`
|
|
400
500
|
([#267](https://github.com/zackbart/connecta/issues/267),
|
|
401
501
|
[#270](https://github.com/zackbart/connecta/issues/270)).
|
|
402
502
|
|
|
@@ -404,18 +504,18 @@ guard. The argument and the refused alternatives live in the
|
|
|
404
504
|
text }` or `{ type: "image" | "audio", data /* base64 */, mimeType }`, every
|
|
405
505
|
field a string, no extra fields, no `annotations`, no `_meta`, no sugar forms.
|
|
406
506
|
An invalid block throws catchably and nothing is accepted — rejected, not
|
|
407
|
-
stripped. The refused types are pointers: a guest-minted `resource_link` URI
|
|
408
|
-
|
|
507
|
+
stripped. The refused types are pointers: a guest-minted `resource_link` URI is
|
|
508
|
+
a lure a client may dereference.
|
|
409
509
|
|
|
410
|
-
**M2.** Blocks collect on the host in emission order and are delivered only
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
510
|
+
**M2.** Blocks collect on the host in emission order and are delivered only with
|
|
511
|
+
a successful result, appended to `content` after the JSON envelope, which gains
|
|
512
|
+
`emitted: N`. A program that never emits produces the byte-for-byte ordinary
|
|
513
|
+
response (`R6`). `structuredContent` stays the envelope alone — emission is
|
|
514
|
+
presentation, not a second data channel.
|
|
415
515
|
|
|
416
516
|
**M3.** Return value and emission are independent: `R2` never measures emitted
|
|
417
|
-
bytes, a truncated return does not suppress delivered blocks, and blocks do
|
|
418
|
-
|
|
517
|
+
bytes, a truncated return does not suppress delivered blocks, and blocks do not
|
|
518
|
+
shrink the return budget.
|
|
419
519
|
|
|
420
520
|
**M4.** A failed program delivers no blocks. The error result reports
|
|
421
521
|
`emittedDiscarded: N` when N > 0 — a field on the structured envelope, a
|
|
@@ -423,52 +523,55 @@ trailing line on the plain-text paths — never silently.
|
|
|
423
523
|
|
|
424
524
|
**M5.** Two budgets (`ConnectaConfig.execute.maxEmittedBytes` /
|
|
425
525
|
`.maxEmittedBlocks`, defaults 4,000,000 serialized bytes and 32 blocks) fail
|
|
426
|
-
loudly at the `emit` call, naming the budget and the room remaining; nothing
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
526
|
+
loudly at the `emit` call, naming the budget and the room remaining; nothing is
|
|
527
|
+
partially accepted and prior blocks stand. No `get_result` stash: the program
|
|
528
|
+
learns while it can still choose differently. The byte default is a transport
|
|
529
|
+
bound, not a context bound — emitted media reaches the model as media, not
|
|
530
|
+
base64 text.
|
|
431
531
|
|
|
432
|
-
**M6.** No provenance is claimed: every emitted block is program output,
|
|
433
|
-
|
|
434
|
-
|
|
532
|
+
**M6.** No provenance is claimed: every emitted block is program output, trusted
|
|
533
|
+
exactly as much as the return value. Preservation is re-emission of the raw
|
|
534
|
+
downstream block, so `S5`'s uncapped fallthrough is contract.
|
|
435
535
|
|
|
436
|
-
**M7.** `emit` spends no host-call budget (`L4`); `
|
|
437
|
-
bounds.
|
|
536
|
+
**M7.** `emit` alone spends no host-call budget (`L4`); `search`, `describe`,
|
|
537
|
+
and `call` share it. `M5`'s bounds are emission's only bounds.
|
|
438
538
|
|
|
439
539
|
**M8.** Emission asks nothing of an executor: `emit` is a provider function,
|
|
440
540
|
blocks cross the guest boundary once as an argument, and `ExecuteResult` is
|
|
441
541
|
unchanged — `Executor` stays assignable from `@cloudflare/codemode`'s
|
|
442
|
-
`DynamicWorkerExecutor`,
|
|
542
|
+
`DynamicWorkerExecutor`, so any executor that bridges provider calls gets
|
|
443
543
|
emission for free.
|
|
444
544
|
|
|
445
545
|
**M9.** Request-local and unstreamed: blocks exist only in the finished
|
|
446
546
|
response, and `emit` resolving means "accepted," never "delivered."
|
|
447
547
|
|
|
448
548
|
**M10.** Activity stays payload-free. `diagnostics: true` adds one `emitted`
|
|
449
|
-
aggregate — count and serialized bytes, numbers only (`R8`), present only
|
|
450
|
-
|
|
549
|
+
aggregate — count and serialized bytes, numbers only (`R8`), present only when
|
|
550
|
+
something was emitted.
|
|
451
551
|
|
|
452
552
|
## Retry semantics
|
|
453
553
|
|
|
454
554
|
**Y1.** Connecta makes one downstream attempt per admitted call, both inside a
|
|
455
555
|
program and through either direct-call tool. It never waits and retries on the
|
|
456
|
-
caller's behalf
|
|
556
|
+
caller's behalf, and an admission refusal may prevent even that attempt.
|
|
457
557
|
|
|
458
|
-
**Y2.** A program may retry a caught failure whose `retryable` is true, or a
|
|
558
|
+
**Y2.** A program may retry a caught failure whose `retryable` is true, or a
|
|
559
|
+
rejected promise whose `reason.retryable` is true (`S8`). Every attempt spends
|
|
560
|
+
host-call budget, so an unchecked loop converts a transient failure into
|
|
561
|
+
`budget_exceeded`.
|
|
459
562
|
|
|
460
563
|
**Y3.** What must never be retried automatically:
|
|
461
564
|
|
|
462
565
|
- anything with `retryable: false` — a policy refusal, a missing credential, a
|
|
463
566
|
bad address, or malformed arguments will fail identically forever;
|
|
464
567
|
- `rate_limited`, immediately. A portable program has no timer, and a
|
|
465
|
-
Dynamic-Worker-only wait would spend the run's wall
|
|
466
|
-
|
|
467
|
-
|
|
568
|
+
Dynamic-Worker-only wait would spend the run's wall clock on code that fails
|
|
569
|
+
on QuickJS. Return the failure and let the model, which can wait, re-issue
|
|
570
|
+
with `retryAfterMs` in hand.
|
|
468
571
|
- a cancelled or timed-out *execution*: it is already over (`L1`).
|
|
469
572
|
|
|
470
|
-
**Y4.** A provider's `retryAfterMs` is returned unchanged
|
|
471
|
-
whether and when to reissue
|
|
573
|
+
**Y4.** A provider's `retryAfterMs` is returned unchanged; the caller decides
|
|
574
|
+
whether and when to reissue, and a later call gets its own deadline and
|
|
472
575
|
admission decision.
|
|
473
576
|
|
|
474
577
|
## Cancellation and limits
|
|
@@ -478,12 +581,12 @@ poll, no cancellation exception to catch, and no guarantee that a `finally`
|
|
|
478
581
|
block runs — a cancelled QuickJS child is terminated outright. Write programs
|
|
479
582
|
that need no cleanup.
|
|
480
583
|
|
|
481
|
-
**L2.** What cancellation guarantees: in-flight host calls abort, no further
|
|
482
|
-
call is admitted, the admission lease is released, and nothing
|
|
483
|
-
survives the request.
|
|
584
|
+
**L2.** What cancellation guarantees: in-flight host calls abort, no further
|
|
585
|
+
host call is admitted, the admission lease is released, and nothing
|
|
586
|
+
request-bound survives the request.
|
|
484
587
|
|
|
485
|
-
**L3.** Every execution runs under a wall-clock deadline that includes time
|
|
486
|
-
waiting on host calls. Expiry ends the run with an execution error and no
|
|
588
|
+
**L3.** Every execution runs under a wall-clock deadline that includes time
|
|
589
|
+
spent waiting on host calls. Expiry ends the run with an execution error and no
|
|
487
590
|
partial result; the deadline's length is executor configuration (`X1`).
|
|
488
591
|
|
|
489
592
|
**L4.** Per-execution bounds that are contract, identical in both executors
|
|
@@ -491,8 +594,8 @@ because connecta enforces them above the sandbox:
|
|
|
491
594
|
|
|
492
595
|
| Bound | Value |
|
|
493
596
|
| --- | --- |
|
|
494
|
-
| Host calls per execution | 20 |
|
|
495
|
-
| Deadline per host call | 15 s, `execute.hostCallTimeoutMs
|
|
597
|
+
| Host calls per execution, shared by `search`, `describe`, and `call` | 20 by default, `execute.maxHostCalls` |
|
|
598
|
+
| Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
|
|
496
599
|
| Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
|
|
497
600
|
| `describe` addresses | ≤ 100 |
|
|
498
601
|
| `describe` nearby suggestions | ≤ 3 canonical addresses per failed entry |
|
|
@@ -500,7 +603,13 @@ because connecta enforces them above the sandbox:
|
|
|
500
603
|
| Result | 24,000 serialized characters |
|
|
501
604
|
| Logs presented to the model | 4,000 characters |
|
|
502
605
|
|
|
503
|
-
|
|
606
|
+
Every `call` attempt spends one host call on entry, before address resolution,
|
|
607
|
+
catalog lookup, safety checks, validation, or dispatch, so unknown addresses,
|
|
608
|
+
unknown tools, catalog failures, and other pre-dispatch refusals cost what a
|
|
609
|
+
successful call costs; catching a refusal does not refund it. `search` and
|
|
610
|
+
`describe` likewise spend on entry. Exhausting the budget fails that call with
|
|
611
|
+
non-retryable `budget_exceeded` (`E2`) and a message naming the budget. No
|
|
612
|
+
connector is reached, and the budget does not refill inside one execution.
|
|
504
613
|
|
|
505
614
|
**L5.** The guest is memory-, stack-, and CPU-bounded, and a program that
|
|
506
615
|
exhausts a bound ends the run with an error instead of degrading the host. The
|
|
@@ -513,30 +622,33 @@ code safe to run at all.
|
|
|
513
622
|
**L6.** A host call's serialized arguments and its serialized result are each
|
|
514
623
|
bounded — QuickJS caps both at 256 KiB (`X10`) — and exceeding either fails that
|
|
515
624
|
call, not the execution, so a program can catch it and ask for less. The failure
|
|
516
|
-
is executor-owned untyped text, not a Connecta host failure (`E1`). An
|
|
517
|
-
|
|
518
|
-
*argument* payload is refused before it
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
**L8.** Bounds are deployment configuration, not program inputs: a program
|
|
528
|
-
raise one by asking. `execute_code`'s description states the host-call
|
|
529
|
-
|
|
530
|
-
|
|
625
|
+
is executor-owned untyped text, not a Connecta host failure (`E1`). An
|
|
626
|
+
over-bound *result* names the address the program called rather than only the
|
|
627
|
+
generic bridge function; an over-bound *argument* payload is refused before it
|
|
628
|
+
is parsed, so it names no address at all.
|
|
629
|
+
|
|
630
|
+
**L7.** Executions are admitted, not queued indefinitely: bounded concurrency
|
|
631
|
+
plus a bounded queue with a wait timeout. Overload is a retryable
|
|
632
|
+
`executor_overloaded` carrying `retryAfterMs`; cancellation and shutdown are
|
|
633
|
+
terminal. Admission happens *before* any catalog or provider is built, so a
|
|
634
|
+
queued request holds no state.
|
|
635
|
+
|
|
636
|
+
**L8.** Bounds are deployment configuration, not program inputs: a program
|
|
637
|
+
cannot raise one by asking. `execute_code`'s description states the host-call
|
|
638
|
+
budget and the per-call deadline — the ones a program must plan around before it
|
|
639
|
+
runs. The result and log caps live here and in the truncation notice itself
|
|
640
|
+
(`R2`, `R5`).
|
|
531
641
|
|
|
532
642
|
## Activity
|
|
533
643
|
|
|
534
644
|
**V1.** One payload-free activity event per attempted call, with
|
|
535
645
|
`source: "execute_code"` — every dispatched call plus every local refusal: a
|
|
536
|
-
read-only refusal, an unknown tool, an unloadable
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
646
|
+
read-only refusal, an unknown tool, an unloadable catalog, a missing credential,
|
|
647
|
+
an address no connector owns. Ten tools called is ten events, as legible as ten
|
|
648
|
+
`call_tool` calls, which makes moving work into the sandbox an optimization
|
|
649
|
+
rather than a blindfold. An exhausted host-call budget is the exception: `L4`
|
|
650
|
+
refuses that attempt above the invocation path, so it is charged but records no
|
|
651
|
+
event.
|
|
540
652
|
|
|
541
653
|
**V2.** Each event carries `connectorId`, `toolName`, `address`, `source`,
|
|
542
654
|
`outcome` (`success`, `error`, `timeout`, `cancelled`), `durationMs`,
|
|
@@ -544,25 +656,23 @@ calls — which makes moving work into the sandbox an optimization, not a blindf
|
|
|
544
656
|
server identity. Typed codes derive an optional `friction`: `tool_not_found`,
|
|
545
657
|
`schema_retry`, `destructive_reroute`, or `auth_required`. The fifth class,
|
|
546
658
|
`result_too_large`, cannot reach an `execute_code` event: it belongs to a
|
|
547
|
-
`call_tool` result too large to return inline, and a program's own
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
the honest record of what was attempted.
|
|
659
|
+
`call_tool` result too large to return inline, and a program's own return is
|
|
660
|
+
refused paging by design rather than truncated into friction. There is nowhere
|
|
661
|
+
to put arguments, results, program source, or raw error text; a caught failure
|
|
662
|
+
is still recorded. `address` is canonical (`A1`) where a tool resolved,
|
|
663
|
+
otherwise the name the program used — the honest record of what was attempted.
|
|
553
664
|
|
|
554
665
|
**V3.** A call whose connector does not exist is recorded at the address as
|
|
555
666
|
written, *provided* it split into the two fields activity keeps — one with no
|
|
556
667
|
interior dot records nothing. An invented id is the address mistake an operator
|
|
557
|
-
most needs to see
|
|
668
|
+
most needs to see, but recording it as written puts caller-authored text in
|
|
558
669
|
fields that are otherwise operator- and connector-authored, so `connectorId`
|
|
559
670
|
and `toolName` clamp at 128 UTF-8 bytes (`address` at 257) with a `…` marker:
|
|
560
|
-
payload-free *by construction* means the event has nowhere to put a payload
|
|
561
|
-
not merely that connecta declines to.
|
|
671
|
+
payload-free *by construction* means the event has nowhere to put a payload.
|
|
562
672
|
|
|
563
673
|
**V4.** The execution itself emits no event. It has no address, and its one
|
|
564
|
-
distinctive artifact is the program source
|
|
565
|
-
|
|
674
|
+
distinctive artifact is the program source — exactly what a payload-free history
|
|
675
|
+
must never keep.
|
|
566
676
|
|
|
567
677
|
## Executor exceptions
|
|
568
678
|
|
|
@@ -578,30 +688,50 @@ and guest-CPU limits (`L5`); the Dynamic Worker has no such knobs, so workerd's
|
|
|
578
688
|
isolate limits apply untuned. A specific heap ceiling is a Node-only option.
|
|
579
689
|
|
|
580
690
|
**X3. Mid-flight cancellation.** The QuickJS pool receives the request's
|
|
581
|
-
`AbortSignal` and kills the child. The Dynamic Worker executor's `execute()`
|
|
582
|
-
no signal, so a cancelled request's program runs on until its host calls
|
|
583
|
-
the deadline expires. `L2` holds either way — the calls abort, the
|
|
584
|
-
not wait — but "the run ends" is best-effort on Workers.
|
|
585
|
-
|
|
586
|
-
**X4. Log rendering and capture.** QuickJS JSON-stringifies non-string
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
691
|
+
`AbortSignal` and kills the child. The Dynamic Worker executor's `execute()`
|
|
692
|
+
takes no signal, so a cancelled request's program runs on until its host calls
|
|
693
|
+
fail or the deadline expires. `L2` holds either way — the calls abort, the
|
|
694
|
+
response does not wait — but "the run ends" is best-effort on Workers.
|
|
695
|
+
|
|
696
|
+
**X4. Log rendering and capture.** QuickJS JSON-stringifies non-string arguments
|
|
697
|
+
and captures `log`, `info`, `warn`, `error`, and `debug`; the Dynamic Worker
|
|
698
|
+
renders arguments with `String()` (so an object logs as `[object Object]`) and
|
|
699
|
+
captures only `log`, `warn`, and `error`, prefixing the latter two. Only the
|
|
700
|
+
three captured everywhere are contract (`R5`); rendering is not. QuickJS streams
|
|
701
|
+
each accepted entry within the existing IPC envelope bound while retaining its
|
|
702
|
+
per-entry and cumulative child caps, and the parent keeps at most 4,001 joined
|
|
703
|
+
characters for failure recovery — one beyond the presentation cap, so truncation
|
|
704
|
+
stays visible. On a normal reply the child's complete log array wins and the two
|
|
705
|
+
copies are never joined; on termination or IPC failure the parent attaches its
|
|
706
|
+
retained prefix to the thrown error. Admission rejection before the program
|
|
707
|
+
starts has no guest logs to recover.
|
|
708
|
+
|
|
709
|
+
**X5. Leftover authority.** QuickJS blocks imports and has no `fetch`,
|
|
710
|
+
`process`, timers, `crypto`, or `WebSocket`, and its Node child starts with an
|
|
711
|
+
explicitly empty process environment rather than inheriting deployment variables
|
|
712
|
+
or `NODE_OPTIONS`. A Dynamic Worker has those globals plus a non-contract set of
|
|
713
|
+
runtime builtins through `import()` and `process.getBuiltinModule()`, including
|
|
714
|
+
`node:path`, `node:crypto`, `node:net`, `node:tls`, `node:dns`, `node:module`,
|
|
715
|
+
and `cloudflare:workers`; the upstream set drifts, so that is not an allowlist.
|
|
716
|
+
The supported construction is exactly `new DynamicWorkerExecutor({ loader })` —
|
|
717
|
+
`bindings`, `modules`, and `globalOutbound` each grant ambient configuration,
|
|
718
|
+
code, or egress. Under it, `process.env`, lexical `this.env`, and
|
|
719
|
+
`cloudflare:workers.env` are empty; `node:fs`, `node:http`, and `node:https` are
|
|
720
|
+
unavailable through either access route; external `fetch`, `WebSocket`,
|
|
721
|
+
`node:net`, and `node:tls` fail with workerd's outbound-denial error; DNS lookup
|
|
722
|
+
ends unresolved; and `fetch("data:...")` resolves locally. `P2` is the portable
|
|
723
|
+
contract: programs use none of this runtime-only authority, timers and `crypto`
|
|
724
|
+
included, because the same code fails on QuickJS, and the `execute_code`
|
|
725
|
+
description and served `usage` skill say so before an agent writes code.
|
|
596
726
|
|
|
597
727
|
**X6. Stall detection.** QuickJS notices a program awaiting something that can
|
|
598
|
-
never settle and fails fast; the Dynamic Worker waits for its deadline.
|
|
599
|
-
|
|
728
|
+
never settle and fails fast; the Dynamic Worker waits for its deadline.
|
|
729
|
+
Requiring the fast failure would require a host-driven job loop — not a
|
|
600
730
|
reasonable demand on a platform sandbox.
|
|
601
731
|
|
|
602
732
|
**X7. Value codec.** QuickJS is JSON-only; `@cloudflare/codemode` tunnels binary
|
|
603
|
-
values through a tagged envelope, so a `Uint8Array` may survive there. `P3` is
|
|
604
|
-
contract: JSON-serializable values, or the program is Workers-only.
|
|
733
|
+
values through a tagged envelope, so a `Uint8Array` may survive there. `P3` is
|
|
734
|
+
the contract: JSON-serializable values, or the program is Workers-only.
|
|
605
735
|
|
|
606
736
|
**X8. Unknown-property message.** An unknown `connecta` property throws
|
|
607
737
|
`Unknown function connecta.x` on QuickJS and `Tool "x" not found` on the Dynamic
|
|
@@ -611,40 +741,38 @@ Worker. Both satisfy `E6`; the text is not contract.
|
|
|
611
741
|
error when a program returns something its codec cannot carry. QuickJS converts
|
|
612
742
|
lossily instead — a cyclic object comes back as the string `"[object Object]"`,
|
|
613
743
|
because the guest-to-host dump happens before any serializer can object.
|
|
614
|
-
Normalizing
|
|
615
|
-
JSON-representability, spending real CPU on every program to improve the error
|
|
744
|
+
Normalizing that would spend real CPU on every program to improve the error
|
|
616
745
|
message of a program that is already wrong. `P3` is the contract: neither
|
|
617
746
|
behavior returns the value.
|
|
618
747
|
|
|
619
748
|
**X10. Per-host-call payload bound.** `L6`'s 256 KiB ceiling on a host call's
|
|
620
749
|
arguments and result is QuickJS's, enforced at its IPC boundary. The Dynamic
|
|
621
|
-
Worker has no documented equivalent; Workers RPC limits apply and connecta
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
**X11. Typed host rejection.** Both executors rebuild Connecta's authenticated
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
[migration guide](./upgrading.md#0230-program-api-pruning).
|
|
750
|
+
Worker has no documented equivalent; Workers RPC limits apply and connecta adds
|
|
751
|
+
none, because that boundary is an isolate-to-isolate call rather than a
|
|
752
|
+
`process.send` with a hard ceiling. A program returning a quarter-megabyte from
|
|
753
|
+
one tool call therefore fails on Node and may succeed on Workers — reduce inside
|
|
754
|
+
the program either way (`R1`).
|
|
755
|
+
|
|
756
|
+
**X11. Typed host rejection.** Both executors rebuild Connecta's authenticated
|
|
757
|
+
host-failure frame as a thrown guest `Error` (`E1`). The per-run secret stays in
|
|
758
|
+
the trusted prelude closure and the prelude locks `globalThis.Error`, so guest
|
|
759
|
+
code and connector prose cannot forge the frame. The host bounds details before
|
|
760
|
+
framing (`E1`), JSON escapes included; QuickJS refuses an oversized frame whole
|
|
761
|
+
rather than slicing through its JSON, hides one whose JSON is malformed, and
|
|
762
|
+
keeps the raw bridge and its JSON decoder in a private closure so guest code
|
|
763
|
+
cannot intercept a frame first. A mismatched frame is ordinary untyped prose.
|
|
636
764
|
|
|
637
765
|
## Verification
|
|
638
766
|
|
|
639
767
|
Every clause has a test. `test/guest-contract-cases.ts` holds the case table,
|
|
640
768
|
written once and run twice: `test/guest-api-contract-quickjs.test.ts` runs it on
|
|
641
|
-
the Node QuickJS executor, and `test/guest-api-contract.test.ts` runs it on a
|
|
642
|
-
`DynamicWorkerExecutor` in workerd — a Miniflare Worker Loader binding
|
|
643
|
-
arm real rather than simulated — alongside the clauses connecta
|
|
644
|
-
executor. Rows naming `test/guest-api-contract.test.ts` are
|
|
645
|
-
and each case's title carries its clauses. Two arms
|
|
646
|
-
check on the executor duties above, with
|
|
647
|
-
the upstream `Executor` shape assignable.
|
|
769
|
+
the Node QuickJS executor, and `test/guest-api-contract.test.ts` runs it on a
|
|
770
|
+
real `DynamicWorkerExecutor` in workerd — a Miniflare Worker Loader binding
|
|
771
|
+
makes that arm real rather than simulated — alongside the clauses connecta
|
|
772
|
+
enforces above any executor. Rows naming `test/guest-api-contract.test.ts` are
|
|
773
|
+
covered by both arms, and each case's title carries its clauses. Two arms
|
|
774
|
+
passing one table is also the check on the executor duties above, with
|
|
775
|
+
`test/codemode-compat.test.ts` holding the upstream `Executor` shape assignable.
|
|
648
776
|
|
|
649
777
|
| Clauses | Test |
|
|
650
778
|
| --- | --- |
|
|
@@ -653,52 +781,41 @@ the upstream `Executor` shape assignable.
|
|
|
653
781
|
| `P3`, `X9` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` |
|
|
654
782
|
| `P4` | `test/guest-api-contract.test.ts` (no cross-run leakage), `test/execute.test.ts` (one catalog load per connector per execution) |
|
|
655
783
|
| `A1`, `A2` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (canonical addressing), `test/server.test.ts` (bounded live connector inventory) |
|
|
656
|
-
| `S1`, `S2` | `test/guest-api-contract.test.ts` (flat page, connector guides, schema keys,
|
|
784
|
+
| `S1`, `S1a`, `S2` | `test/guest-api-contract.test.ts` (flat page, connector guides, schema keys, unfiltered browse), `test/execute.test.ts` (guide pagination/partial/no-match behavior and `$ref`/`allOf`), `test/meta-tools-search.test.ts` (mixed complete/partial ranking, stable pagination, the two safety classes) |
|
|
657
785
|
| `S3` | `test/guest-api-contract.test.ts` (typed uncaught bound), `test/execute.test.ts` (count limits, fan-out bound) |
|
|
658
|
-
| `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) |
|
|
659
|
-
| `S5` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`unwrapMcpResult
|
|
660
|
-
| `S6` | `test/execute.test.ts` (fail-closed annotations, activity parity) |
|
|
786
|
+
| `S4` | both guest-contract executors (ordered mixed describe results with unknown-address, unknown-tool suggestion, and catalog-failure details), `test/meta-tools-search.test.ts` (top-level routing, no-suggestion, catalog-failure, and hostile-input bounds) |
|
|
787
|
+
| `S5`, `S6` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`unwrapMcpResult`, fail-closed annotations, activity parity) |
|
|
661
788
|
| `S7` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (parallel calls and shared admission) |
|
|
662
|
-
| `S8`, `E1`, `X11` | both guest-contract executors (caught call, discovery, utility, budget, removed-function, and forgery cases; typed promise rejections) |
|
|
663
|
-
| `S9` | `test/result-shapes.test.ts` (value exclusion, bounds, merging, LRU and time expiry, runtime isolation, read-only admission, declared precedence, definition invalidation, unwrapped MCP results, discovery provenance, copy isolation,
|
|
664
|
-
| `E2`, `E8` | `test/guest-api-contract.test.ts` (code → `retryable`, caught, parallel, and uncaught validation recovery), `test/meta-tools.test.ts` (direct, destructive, provider fallback), `test/validate.test.ts` (bounded payload-free findings), `test/errors.test.ts` |
|
|
665
|
-
| `E3` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`auth_required
|
|
666
|
-
| `E4` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (destructive) |
|
|
789
|
+
| `S8`, `E1`, `X11` | both guest-contract executors (caught call, discovery, utility, budget, removed-function, and forgery cases; typed promise rejections), `test/quickjs-executor.test.ts` (oversized messages, private transport, forged frames) |
|
|
790
|
+
| `S9` | `test/result-shapes.test.ts` (value exclusion, bounds, merging, LRU and time expiry, runtime isolation, read-only admission, declared precedence, definition invalidation, unwrapped MCP results, discovery provenance, copy isolation, failure isolation) |
|
|
791
|
+
| `E2`, `E8` | `test/guest-api-contract.test.ts` (code → `retryable`, caught, parallel, and uncaught validation recovery), `test/meta-tools-call.test.ts` (direct, destructive, provider fallback), `test/validate.test.ts` (bounded payload-free findings), `test/errors.test.ts` |
|
|
792
|
+
| `E3`, `E4` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`auth_required`, destructive reroute) |
|
|
667
793
|
| `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) |
|
|
668
794
|
| `E6`, `X8` | `test/guest-api-contract.test.ts` (unknown and inherited members, wrapped-message precedence), `test/quickjs-executor.test.ts` |
|
|
669
795
|
| `E7` | `test/guest-api-contract.test.ts` (refusals about a `503`-named connector), `test/errors.test.ts` |
|
|
670
|
-
| `R1`, `R3` | `test/guest-api-contract.test.ts` (pass-through, truncation is success) |
|
|
671
|
-
| `
|
|
672
|
-
| `R4` | verdict; `R2` is its enforcement |
|
|
796
|
+
| `R1`, `R2`, `R3` | `test/guest-api-contract.test.ts` (pass-through, truncation is success, envelope fits the cap and is idempotent) |
|
|
797
|
+
| `R4`, `M6`, `M9` | verdicts; `R2`'s guard, `M1`'s strict typing, and `M2`'s collect-then-deliver are their enforcement |
|
|
673
798
|
| `R5` | `test/guest-api-contract.test.ts`, `test/quickjs-log-limits.test.ts` |
|
|
674
799
|
| `R6`–`R8` | `test/guest-api-contract.test.ts` (normal result keys), `test/execute.test.ts` (opt-in operation aggregates, failure paths, payload exclusion) |
|
|
675
|
-
| `Y1` | `test/guest-api-contract.test.ts` (one attempt per call) |
|
|
676
|
-
| `Y2`, `Y3` | `test/guest-api-contract.test.ts` (retryable flags by code) |
|
|
800
|
+
| `Y1`, `Y2`, `Y3` | `test/guest-api-contract.test.ts` (one attempt per call, retryable flags by code) |
|
|
677
801
|
| `Y4` | `test/meta-tools-call.test.ts`, `test/call-admission.test.ts` (one attempt, retry hints, caller reissue) |
|
|
678
802
|
| `L1`, `L2` | `test/guest-api-contract.test.ts` (in-flight call fails `cancelled`), `test/execute.test.ts` (cancels outstanding host calls) |
|
|
679
803
|
| `L3`, `X1` | `test/guest-api-contract.test.ts` (short-deadline executors) |
|
|
680
|
-
| `L4`, `L8` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (budgets) |
|
|
681
|
-
| `L5`, `X2` | `test/quickjs-executor.test.ts` (CPU, heap) |
|
|
682
|
-
| `L6`, `X10` | `test/quickjs-executor.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message) |
|
|
683
|
-
| `
|
|
684
|
-
| `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, and the identity clamp) |
|
|
804
|
+
| `L4`, `L8` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (shared discovery/call budgets) |
|
|
805
|
+
| `L5`, `L7`, `X2` | `test/quickjs-executor.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue) |
|
|
806
|
+
| `L6`, `X10` | `test/quickjs-executor.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.test.ts` (outer reply serialization failure settles the call) |
|
|
807
|
+
| `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, the identity clamp) |
|
|
685
808
|
| `M1` | `test/guest-api-contract.test.ts` (invalid emits throw catchably, accept nothing), `test/execute-emit.test.ts` (every rejected shape) |
|
|
686
809
|
| `M2`, `M3` | `test/guest-api-contract.test.ts` (delivery order, truncated return plus delivered blocks), `test/execute-emit.test.ts` (envelope, `structuredContent`, byte-for-byte no-emit path) |
|
|
687
810
|
| `M4` | `test/guest-api-contract.test.ts` (discard is visible), `test/execute-emit.test.ts` (structured and plain paths), `test/quickjs-executor.test.ts` (mid-run shutdown) |
|
|
688
811
|
| `M5`, `M7` | `test/execute-emit.test.ts` (both budgets fail the crossing block; host-call budget untouched) |
|
|
689
|
-
| `M6`, `M9` | verdicts; `M1`'s strict typing and `M2`'s collect-then-deliver are their enforcement |
|
|
690
812
|
| `M8` | two arms passing one case table, `test/codemode-compat.test.ts` |
|
|
691
813
|
| `M10` | `test/execute-emit.test.ts` (aggregate present, numbers only, absent when nothing emitted) |
|
|
692
|
-
| `
|
|
693
|
-
| `
|
|
694
|
-
| `X6` | `test/quickjs-executor.test.ts` (never-settling await) |
|
|
814
|
+
| `X4` | `test/guest-api-contract.test.ts` (string logs only), `test/quickjs-executor.test.ts` (logs before cancellation), `test/quickjs-child-stderr.test.ts` (crash, shutdown, deadline, IPC failure, bounded parent retention), `test/quickjs-log-limits.test.ts` (unchanged successful logs) |
|
|
815
|
+
| `X3`, `X6` | `test/quickjs-executor.test.ts` (cancels a running child, never-settling await) |
|
|
695
816
|
| `X7` | `P3`'s tests; the Workers superset is deliberately unused |
|
|
696
817
|
|
|
697
|
-
The surface itself is checked by `test/server.test.ts` (the exact seven-tool
|
|
698
|
-
and `test/code-first-surface.test.ts` (the fold's construction rules, the
|
|
818
|
+
The surface itself is checked by `test/server.test.ts` (the exact seven-tool
|
|
819
|
+
list) and `test/code-first-surface.test.ts` (the fold's construction rules, the
|
|
699
820
|
required executor, the refusals a removed top-level tool now gets, copy, and
|
|
700
|
-
measured size).
|
|
701
|
-
|
|
702
|
-
```sh
|
|
703
|
-
npm --prefix eval/current-version run benchmark
|
|
704
|
-
```
|
|
821
|
+
measured size).
|