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