@zackbart/connecta 0.41.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/bin/connecta.mjs +26 -17
- package/bin/meta-tool-names.d.mts +3 -1
- package/bin/meta-tool-names.mjs +4 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +2 -1
- package/dist/activity.js +5 -5
- package/dist/advertised-schema.js +15 -0
- package/dist/agent-routes.js +31 -5
- package/dist/auth/downstream-oauth/refresh-coordinator.js +4 -1
- package/dist/auth/downstream-oauth.js +3 -0
- package/dist/auth/static-oauth.js +26 -18
- package/dist/auth-elicitation.js +118 -61
- package/dist/authorization-handoff.js +36 -3
- package/dist/catalog-cache.js +292 -24
- package/dist/catalog-freshness.js +12 -0
- package/dist/catalog-service.js +373 -70
- package/dist/catalog.js +147 -58
- package/dist/config-defaults.d.ts +2 -1
- package/dist/config-defaults.js +4 -1
- package/dist/config.d.ts +43 -17
- package/dist/config.js +26 -10
- package/dist/connectors/remote-mcp/catalog.js +52 -28
- package/dist/connectors/remote-mcp/connection.js +62 -5
- package/dist/connectors/remote-mcp.js +10 -1
- package/dist/describe-config.d.ts +3 -2
- package/dist/discovery-health.js +26 -11
- package/dist/errors.d.ts +52 -3
- package/dist/errors.js +86 -3
- package/dist/execute.js +476 -200
- package/dist/executor-admission.js +2 -0
- package/dist/executor-contract.js +6 -0
- package/dist/executor-result.js +2 -2
- package/dist/executors/quickjs-child.js +13 -4
- package/dist/executors/quickjs-runtime.d.ts +2 -0
- package/dist/executors/quickjs-runtime.js +36 -19
- package/dist/executors/quickjs.js +23 -6
- package/dist/guest-functions.js +5 -4
- package/dist/guest-runtime.js +37 -1
- package/dist/guest-types.d.ts +18 -0
- package/dist/http/client.js +7 -2
- package/dist/http/errors.js +9 -4
- package/dist/http/fields.js +17 -5
- package/dist/http/hatch.js +9 -3
- package/dist/http/paging.js +1 -1
- package/dist/http/provider.js +2 -6
- package/dist/http/vendor.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -0
- package/dist/invocation.js +98 -34
- package/dist/meta-output.js +24 -18
- package/dist/meta-tool-names.d.ts +1 -1
- package/dist/meta-tool-names.js +1 -1
- package/dist/meta-tools.js +259 -60
- package/dist/native-recovery.js +4 -1
- package/dist/native-tools.js +166 -51
- package/dist/node.js +3 -1
- package/dist/oauth-handoff.js +53 -43
- package/dist/operation-activity.js +2 -1
- package/dist/operation-vocabulary.js +6 -0
- package/dist/operator-record.d.ts +1 -1
- package/dist/operator-record.js +1 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/program-result.js +2 -0
- package/dist/program-source.js +1 -1
- package/dist/program-writes.js +112 -19
- package/dist/provider.d.ts +0 -13
- package/dist/provider.js +0 -3
- package/dist/providers/_shared/google/kit.js +2 -2
- package/dist/providers/breeze/api.js +2 -2
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/ccb/api.js +2 -2
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +2 -2
- package/dist/providers/cloudflare/index.js +2 -0
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/forms/index.js +1 -0
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/github/api.js +98 -6
- package/dist/providers/github/index.js +7 -2
- package/dist/providers/github/lib/auth.d.ts +3 -1
- package/dist/providers/github/lib/auth.js +43 -12
- package/dist/providers/github/lib/discovery.js +11 -1
- package/dist/providers/github/lib/operations.js +8 -0
- package/dist/providers/github/safety.js +39 -0
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +12 -5
- package/dist/providers/gmail/index.js +1 -0
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/index.js +2 -0
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/index.js +11 -5
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/skill.generated.js +1 -1
- package/dist/providers/quickbooks/index.js +2 -5
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/index.js +1 -0
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/slides/index.js +2 -2
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/index.js +1 -0
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +2 -2
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/registry.d.ts +36 -4
- package/dist/registry.js +127 -27
- package/dist/result-preview.js +210 -0
- package/dist/result-seal.js +168 -0
- package/dist/route-names.js +13 -4
- package/dist/routes/connect.js +21 -13
- package/dist/routes/mcp.js +55 -26
- package/dist/routes/shared.d.ts +6 -0
- package/dist/runtime/call.js +15 -10
- package/dist/runtime/request-scope.js +36 -1
- package/dist/runtime/run.js +4 -1
- package/dist/sent-secrets.d.ts +20 -0
- package/dist/sent-secrets.js +49 -1
- package/dist/skills.js +100 -33
- package/dist/storage/keys.js +9 -6
- package/dist/storage/sql-native.js +1 -1
- package/dist/tool-safety.d.ts +33 -0
- package/dist/tool-safety.js +51 -15
- package/dist/types.d.ts +31 -0
- package/dist/usage-guide.js +14 -13
- package/dist/validate.js +29 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +28 -8
- package/documentation/architecture.md +104 -42
- package/documentation/auth.md +46 -19
- package/documentation/code-mode.md +378 -116
- package/documentation/deploying.md +10 -9
- package/documentation/hosted-mcp-migration.md +9 -3
- package/documentation/integrating.md +27 -5
- package/documentation/meta-tools.md +655 -153
- package/documentation/operating.md +5 -3
- package/documentation/providers.md +18 -6
- package/examples/worker/README.md +3 -3
- package/guides/how-it-works.md +2 -1
- package/guides/safety-and-trust.md +1 -3
- package/package.json +1 -1
- package/templates/node/AGENTS.md +3 -3
- package/templates/node/README.md +1 -1
- package/templates/node/package.json +1 -1
|
@@ -34,7 +34,7 @@ new behavior.
|
|
|
34
34
|
## Deploy-time capability
|
|
35
35
|
|
|
36
36
|
The `executor` passed to `createConnecta()` is required. The
|
|
37
|
-
[
|
|
37
|
+
[meta-tool surface](./meta-tools.md#the-meta-tools) is defined in the meta-tool
|
|
38
38
|
reference. Construction fails when the
|
|
39
39
|
executor is missing, and the removed deployment-level `surface` option is rejected rather than
|
|
40
40
|
ignored ([#273](https://github.com/zackbart/connecta/issues/273)), as are the
|
|
@@ -64,9 +64,9 @@ import { workerExecutor } from "@zackbart/connecta/worker";
|
|
|
64
64
|
createConnecta({ executor: workerExecutor({ loader: env.LOADER }), readOnly: false /* … */ });
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
The adapter uses only `loader` and an optional `timeout
|
|
68
|
-
are two active runs, eight queued runs, a
|
|
69
|
-
|
|
67
|
+
The adapter uses only `loader` and an optional `timeout` (default 50 s, `L3`). Its admission defaults
|
|
68
|
+
are two active runs, eight queued runs, a 40-second queue deadline (which leaves a
|
|
69
|
+
queued run time inside the response ceiling, `L3`) and a one-second `retryAfterMs` hint. `admission` replaces all
|
|
70
70
|
four: `{ concurrency, maxQueueSize, queueTimeoutMs, retryAfterMs? }`, where an
|
|
71
71
|
omitted `retryAfterMs` equals `queueTimeoutMs`. The pool belongs to the executor
|
|
72
72
|
instance, so a Worker that builds one per isolate bounds each isolate, not the
|
|
@@ -90,8 +90,10 @@ also need the lifecycle wrapper above; structural compatibility alone is insuffi
|
|
|
90
90
|
```ts
|
|
91
91
|
interface Executor {
|
|
92
92
|
readonly name?: string; // what /health and doctor report
|
|
93
|
+
readonly deadlineMs?: number; // the run's wall-clock deadline (`L3`)
|
|
93
94
|
execute(code: string, providers: ExecutorProvider[]): Promise<ExecuteResult>;
|
|
94
95
|
close?(): void | Promise<void>;
|
|
96
|
+
readonly maxReadBytes?: number; // whole-read ceiling, default 1.5 MiB (`S11`)
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
interface ExecutorProvider {
|
|
@@ -115,8 +117,8 @@ Connecta passes exactly one provider, named `connecta`. An executor must:
|
|
|
115
117
|
called with the program's arguments and awaited. Connecta supplies `search`,
|
|
116
118
|
`describe`, `call`, `read`, `result`, `skill`, and `emit`; the prelude also
|
|
117
119
|
binds the guest's `skills` alias to `skill`. Connecta also supplies
|
|
118
|
-
`unawaited`, which the built-in prelude calls at most once, after a
|
|
119
|
-
program read a result field off an unawaited call. It spends no host call;
|
|
120
|
+
`unawaited`, which the built-in prelude calls at most once, after a
|
|
121
|
+
program read a result field off an unawaited call, whether it then failed or finished. It spends no host call;
|
|
120
122
|
the host admits one notice per run and ends the run with `budget_exceeded`
|
|
121
123
|
on any further call, so a bridge that exposes it directly gains no
|
|
122
124
|
uncounted round trips.
|
|
@@ -142,7 +144,11 @@ Connecta passes exactly one provider, named `connecta`. An executor must:
|
|
|
142
144
|
call order (`R5`), bounding what it retains.
|
|
143
145
|
6. **Bound the guest**: wall clock, memory, stack, and CPU (`L3`, `L5`), within
|
|
144
146
|
`P2`/`X5`. Settle `execute()` on your own deadline; connecta stops waiting
|
|
145
|
-
at `execute.watchdogMs` and reports the sandbox unresponsive (`L3`).
|
|
147
|
+
at `execute.watchdogMs` and reports the sandbox unresponsive (`L3`). Expose
|
|
148
|
+
that deadline as `deadlineMs`, a positive whole number of milliseconds, below
|
|
149
|
+
`execute.watchdogMs`, so connecta can end reads before it and state it in the
|
|
150
|
+
tool description. Without it, reads end only just before `execute.watchdogMs`,
|
|
151
|
+
so a slow one can leave a slower program no time to finish.
|
|
146
152
|
7. **Grant no ambient authority.** Never use `eval` or `node:vm`: sandboxing
|
|
147
153
|
supplements connecta's boundary; every capability arrives through `fns`.
|
|
148
154
|
|
|
@@ -170,7 +176,9 @@ wrap a bare body when driven directly; that is outside the portable contract.
|
|
|
170
176
|
exposes is outside the portable contract and must not be used. QuickJS grants
|
|
171
177
|
none of it. A loader-only Dynamic Worker denies external egress and filesystem
|
|
172
178
|
access and keeps its environment maps empty, but exposes the globals and runtime
|
|
173
|
-
builtins in `X5`.
|
|
179
|
+
builtins in `X5`. Denied egress does not stop a read tool's own arguments from
|
|
180
|
+
carrying data. [`execute_read_code`](./meta-tools.md#read-programs) runs under this
|
|
181
|
+
same contract and refuses calls that can write.
|
|
174
182
|
|
|
175
183
|
**P3.** Values cross the host bridge as JSON: arguments must be
|
|
176
184
|
JSON-serializable and results arrive as plain JSON. A value outside JSON — a
|
|
@@ -248,7 +256,9 @@ unchanged as `connecta.call(entry.address, entry.args)`
|
|
|
248
256
|
([fully determined operations](./meta-tools.md#discovery-context) only).
|
|
249
257
|
|
|
250
258
|
Each entry carries `address`, `name`, and — when requested — `description`,
|
|
251
|
-
the rendered schemas, and `annotations`.
|
|
259
|
+
the rendered schemas, and `annotations`. `description` is the one-sentence
|
|
260
|
+
[summary](./meta-tools.md#lexical-discovery), with `descriptionTruncated: true`
|
|
261
|
+
when text was left out; `fullDescriptions: true` returns the original text. The page's `connectors` map holds each
|
|
252
262
|
row's connector facts once, keyed by the id before the address's first dot: the
|
|
253
263
|
configured `title` when present (normalized whitespace, at most 120 UTF-8
|
|
254
264
|
bytes), and `guide` (the skill name) with a bounded `guideSummary` when the
|
|
@@ -354,7 +364,14 @@ and catalog failures add only `retryAfterMs` when known. Each failed entry
|
|
|
354
364
|
clamps its caller-authored `address` to 512 UTF-8 bytes with an `…` marker, and
|
|
355
365
|
entry order correlates a clipped address with its request; successes keep
|
|
356
366
|
canonical addresses. More than 100 addresses is `invalid_args`, and the same
|
|
357
|
-
256,000-byte ceiling applies.
|
|
367
|
+
256,000-byte ceiling applies. `description` is the tool's full text. One call
|
|
368
|
+
returns at most 32 KiB of UTF-8 full description text in address order; an entry
|
|
369
|
+
that does not fit in what remains gets the search summary, with
|
|
370
|
+
`descriptionTruncated: true` when that summary leaves text out, and a later,
|
|
371
|
+
shorter entry may still fit. Summaries have their own allowance and spend none of
|
|
372
|
+
that budget: each is at most 160 UTF-16 code units, at most 480 UTF-8 bytes, so
|
|
373
|
+
100 addresses return at most 80,288 bytes of description text.
|
|
374
|
+
`fullDescriptions: true` lifts the budget, leaving the ceiling. Compact describe keeps property prose within a
|
|
358
375
|
separate 8,192-byte UTF-8 shape cap and shares search's 2,000-visit budget; a
|
|
359
376
|
capped shape sets `inputSchemaTruncated` or `outputSchemaTruncated`, and
|
|
360
377
|
`format: "json"` gives the exact schema. `format: "typescript"` returns a
|
|
@@ -370,9 +387,12 @@ The `execute_code` description declares a compact subset of it inline: argument
|
|
|
370
387
|
shapes that match what `GuestApi` accepts, result fields it returns, and no
|
|
371
388
|
others. Authored emit blocks are the exact variants `M1` accepts;
|
|
372
389
|
forwarded blocks are the native variants from `result.content`. It also
|
|
373
|
-
states that search and describe return objects rather than arrays, that a
|
|
374
|
-
|
|
375
|
-
|
|
390
|
+
states that search and describe return objects rather than arrays, that a row with
|
|
391
|
+
`guideRequired` needs its connector guide first (only when a visible connector has
|
|
392
|
+
a guide), that a call result over 1 MiB returns a paged handle to loop with
|
|
393
|
+
`connecta.result`, and that every function except `emit` spends a host call and
|
|
394
|
+
that passing the limit ends the program with nothing returned, even under
|
|
395
|
+
`Promise.allSettled` (`M7`). `GuestApi`'s `emit` takes no paged handle, which the
|
|
376
396
|
host refuses. Programs still use plain JavaScript.
|
|
377
397
|
|
|
378
398
|
```js
|
|
@@ -414,6 +434,31 @@ it can reach.
|
|
|
414
434
|
|
|
415
435
|
### connecta.result and connecta.skill
|
|
416
436
|
|
|
437
|
+
**S11.** One rule covers size in a program: a program sees a whole value up to
|
|
438
|
+
the read ceiling; what it returns is capped at `maxResultChars` (`R2`); anything
|
|
439
|
+
larger carries a `resultId`.
|
|
440
|
+
|
|
441
|
+
`await connecta.result(handleOrId, { all: true })` returns the whole stored value
|
|
442
|
+
as `{ data, format }`, for one host call however many transfer pages it takes
|
|
443
|
+
(`L6`). A handle's `valueFormat` decides the format. JSON that won't parse, as when
|
|
444
|
+
redaction replaced a number, throws and says to page instead.
|
|
445
|
+
A bare id parses as JSON when it can, and is text otherwise, so pass the handle when
|
|
446
|
+
the difference matters.
|
|
447
|
+
`all` takes no other option; `{ all: false }` is ordinary paging. Each transfer
|
|
448
|
+
page takes the same bindings, rechecks and deadline as an explicit page: a result
|
|
449
|
+
its run did not publish (`R4a`) fails with its usual code, and a slow page is a
|
|
450
|
+
catchable `timeout`.
|
|
451
|
+
|
|
452
|
+
The read ceiling is 1.5 MiB of the stored UTF-8. `quickJsExecutor()` scales it by
|
|
453
|
+
`cpuTimeMs / 250`, at most 2 MiB and at most a sixteenth of `memoryLimitBytes`:
|
|
454
|
+
parsing a value and using it costs CPU and heap in proportion to its size. The Dynamic Worker
|
|
455
|
+
uses 1.5 MiB; Miniflare read 16 MB without failing, but that says nothing about
|
|
456
|
+
production isolate limits. A custom executor may set `maxReadBytes` lower. Over it the call throws
|
|
457
|
+
`result_too_large`, catchable, naming the size and the ceiling, after one host call
|
|
458
|
+
and no transfer. Page with `{ offset }` and keep only what you need from each page.
|
|
459
|
+
|
|
460
|
+
Reading whole leaves the stash in place: read it again, page it, or return the id.
|
|
461
|
+
|
|
417
462
|
`await connecta.result(handleOrId, { page?, offset?, maxBytes? })` reads a stashed direct-call
|
|
418
463
|
or program-call result bound to the admitted subject, principal, endpoint/pool, request origin,
|
|
419
464
|
connector, and tool. It returns
|
|
@@ -437,8 +482,9 @@ connector id as top-level `skills`, within the admitted connector view; built-in
|
|
|
437
482
|
names stay reserved (see
|
|
438
483
|
[meta-tools](./meta-tools.md#connector-guides-and-skills)). It reads the
|
|
439
484
|
registry behind native `skills/get` and `resources/read`, including opted-in
|
|
440
|
-
downstream skills. It returns
|
|
441
|
-
`
|
|
485
|
+
downstream skills. It returns `{ name, format: "text", text }`, where a local
|
|
486
|
+
guide's `text` omits the frontmatter native readers receive; an unknown name is
|
|
487
|
+
`not_found`. It also
|
|
442
488
|
accepts the top-level tool's `{ name }` object, and `connecta.skills` is an
|
|
443
489
|
undeclared alias of `connecta.skill` with the same host-call accounting, because
|
|
444
490
|
models copy the `skills` tool's shape into programs. Any other argument is
|
|
@@ -562,7 +608,8 @@ access (`L4`); Workers guest computation may continue briefly until teardown (`X
|
|
|
562
608
|
|
|
563
609
|
Both executor bridges rebuild typed host rejections as guest Errors and retain
|
|
564
610
|
their identity in executor-owned state (`X11`). `message` stays human text, capped at 2,000
|
|
565
|
-
JSON-serialized characters including quotes and
|
|
611
|
+
JSON-serialized characters including quotes and a `… [truncated]` marker when clipped,
|
|
612
|
+
and cut only between tokens.
|
|
566
613
|
`code` and `retryable` are the stable branch fields; `data` and `details` carry the same host
|
|
567
614
|
classification, which fits 3,700 serialized characters. Optional recovery metadata
|
|
568
615
|
that would exceed that bound is omitted whole, preserving `code`, `message`,
|
|
@@ -610,20 +657,22 @@ direct calls; and `input_required_invalid`, `input_required_limit`,
|
|
|
610
657
|
| `input_required_unsupported` | a downstream asked for input; a read gets `nextAction` for the equivalent direct call, a write is an unknown outcome (`writeOutcome: "unknown"`) to reconcile before any fresh call | false |
|
|
611
658
|
| `rate_limited` | the downstream reported a rate limit | true |
|
|
612
659
|
| `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 |
|
|
613
|
-
| `timeout` | a call or sandbox deadline expired; details name operation, stage, elapsedMs, and deadlineMs
|
|
660
|
+
| `timeout` | a call or sandbox deadline expired; details name operation, stage, elapsedMs, and deadlineMs; at `write gate` or `dispatch gate`, it was not sent (`L3`) | per operation |
|
|
614
661
|
| `program_error` | guest JavaScript failed; `details.name`, `line`, and a fixed repair hint describe it | false |
|
|
615
662
|
| `cancelled` | the run ended while this call was in flight (`E5`) | false |
|
|
616
663
|
| `connector_call_failed` | anything else the connector threw | per message |
|
|
617
664
|
| `catalog_lookup_failed` | the connector's catalog could not be loaded | per cause |
|
|
618
665
|
| `result_processing_failed` | the result could not be prepared | per message |
|
|
619
|
-
| `result_too_large` | a discovery response exceeded its byte bound
|
|
666
|
+
| `result_too_large` | a discovery response or a whole result read exceeded its byte bound | false |
|
|
667
|
+
| `result_not_retained` | paging a result its run did not publish (`R4a`): rerun a read, never repeat a write | false |
|
|
620
668
|
| `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
|
|
621
|
-
| `write_outcome_unknown` | a program write was sent and no answer
|
|
669
|
+
| `write_outcome_unknown` | a program write was sent and no answer proves whether it applied; `unknownWrites` says how each failed, and none is sent again (`W9`) | false |
|
|
622
670
|
| `executor_failed` | the executor returned a malformed result or failed outside the guest program | false |
|
|
623
671
|
|
|
624
672
|
**E3.** `auth_required` carries the same recovery envelope as `call_tool`:
|
|
625
673
|
`connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
|
|
626
|
-
`unavailable`), `nextAction`
|
|
674
|
+
`unavailable`), a `nextAction` carrying the handoff as
|
|
675
|
+
[authorization recovery](./meta-tools.md#authorization-recovery) describes, and a `retry`
|
|
627
676
|
sentence. A program cannot recover credentials — only an operator can — so stop
|
|
628
677
|
and let the failure reach the model.
|
|
629
678
|
|
|
@@ -660,11 +709,16 @@ non-enumerable getters for result fields (`data`, `format`, `content`, `tools`,
|
|
|
660
709
|
`catalogErrors`, `total`, `text`, `resultId`, `hasMore`, `nextOffset`). Reading
|
|
661
710
|
one before `await`, as in `connecta.call(...).data` or
|
|
662
711
|
`const { tools } = connecta.search(...)`, returns undefined as before and sets
|
|
663
|
-
a per-run flag.
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
712
|
+
a per-run flag. The prelude reports the flag through the uncounted `unawaited`
|
|
713
|
+
host function before the program's outcome crosses. A failed program's hint is
|
|
714
|
+
`connecta.* returns a Promise; await it`; a program that still finished carries
|
|
715
|
+
the same words in `note` (`R1`). Awaiting, `then`, `Promise.all`, and
|
|
716
|
+
`Promise.allSettled` never read those fields. A `TypeError` saying a value is not a
|
|
717
|
+
function or not iterable, in a program that uses `connecta.search`, `describe` or
|
|
718
|
+
`call`, names the result shape: `search and describe return { tools }; call returns
|
|
719
|
+
{ data, format }.` A syntax error in source that does not start
|
|
720
|
+
as a program says `Pass one expression: async () => { ...; return answer; }.`
|
|
721
|
+
Hints are fixed text; no program source, field name, or value enters them.
|
|
668
722
|
|
|
669
723
|
An unchanged uncaught host error retains its classification through Error
|
|
670
724
|
identity. A new error containing its message is a new program error. Copied
|
|
@@ -715,6 +769,15 @@ fields. Program-authored projection saved bytes; host heuristics could drop
|
|
|
715
769
|
deliberately returned fields. Helpers need [#222](https://github.com/zackbart/connecta/issues/222)
|
|
716
770
|
evidence that programs fail to project.
|
|
717
771
|
|
|
772
|
+
A program that finished with nothing to read looks like nothing happened, and a
|
|
773
|
+
write inside it must not run again. When `result` is absent and nothing was
|
|
774
|
+
emitted, the response carries a fixed `note`. After a write:
|
|
775
|
+
`Program returned nothing. Its writes were already sent. Do not rerun it, even with a fix: read their targets in a new program.`
|
|
776
|
+
After reads only: `Program returned nothing, though its calls ran. Add a return.`
|
|
777
|
+
With no calls: `Program returned nothing and made no host calls. Add a return.`
|
|
778
|
+
A returned `null` is a return. An unawaited read (`E6`) puts its await hint in
|
|
779
|
+
`note` even beside a value, followed by the write sentences when a write was sent.
|
|
780
|
+
|
|
718
781
|
**R2.** The boundary is `execute.maxResultChars` serialized characters, shared
|
|
719
782
|
with emitted text: 24,000 (~6k tokens) by default, configurable from 4,000 to
|
|
720
783
|
1,000,000. Raise it when agents need large results whole and the model's
|
|
@@ -727,14 +790,25 @@ with no content (see [truncated direct-call results](./meta-tools.md#truncated-d
|
|
|
727
790
|
so keep the cap below what every client of the deployment accepts. Redaction scans the whole envelope once, so its cost grows with the cap.
|
|
728
791
|
The cap does not change stash paging: pages still obey the stashed call's
|
|
729
792
|
`maxResultBytes` and the 32 KiB bridge ceiling.
|
|
793
|
+
A direct read over its cap may answer with a [preview](./meta-tools.md#previews-of-reads-over-the-cap)
|
|
794
|
+
whose `nextAction` is a program: it loops `connecta.result` over the retained
|
|
795
|
+
result, parses it, and ends in a line the agent edits to reduce it. Editing that
|
|
796
|
+
line and running it costs one host call per page, and a result it could not walk
|
|
797
|
+
within `maxHostCalls` gets no preview. A program never sees a preview: its calls
|
|
798
|
+
and its pages return the whole value. A read at or under its cap returns whole.
|
|
799
|
+
If the host refuses the program, the rest of that result can't be read, and the
|
|
800
|
+
notice says to narrow the read or say what was not read.
|
|
730
801
|
An oversized return is stashed as compact JSON through the same
|
|
731
802
|
result store as direct calls. The returned notice carries `truncated`, `resultId`,
|
|
732
|
-
`totalBytes`, `hint`, `nextOffset: 0`, and an executable `nextAction
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
803
|
+
`totalBytes`, `hint`, `nextOffset: 0`, and an executable `nextAction`. Within the
|
|
804
|
+
read ceiling (`S11`), that program reads the return whole with
|
|
805
|
+
`connecta.result(id, { all: true })` and marks where to reduce it; as written it
|
|
806
|
+
returns a prefix that fits, so running it unedited cannot overflow again. Past the
|
|
807
|
+
ceiling it pages. Both use a size of `floor((maxResultChars - 1024) / 6)` bytes, so
|
|
808
|
+
the output fits the next program's return even when every byte escapes to six
|
|
809
|
+
characters (3,829 bytes by default), at most the stashed call's `maxResultBytes`
|
|
810
|
+
and the 32 KiB bridge ceiling; direct-call notices page at the same size. It also
|
|
811
|
+
retains `totalChars` and a bounded `preview`.
|
|
738
812
|
The recovery action and hint precede the preview, so clients that spill results
|
|
739
813
|
after the first 2,000 characters retain the handle and the instruction.
|
|
740
814
|
Small returns pass through unchanged; Connecta does not select fields.
|
|
@@ -768,6 +842,47 @@ its dependencies forward to any new return stash. The page cap is the smallest
|
|
|
768
842
|
of the deployment cap and contributing connectors' effective `maxResultBytes`.
|
|
769
843
|
Read-only endpoints cannot page write results. A UUID is a handle, never a grant.
|
|
770
844
|
|
|
845
|
+
**R4a.** A run's retained results are unreadable from other requests by default.
|
|
846
|
+
One becomes readable only after its bytes pass the run's final secret set, including
|
|
847
|
+
any credential or private argument registered after they were stored. This covers
|
|
848
|
+
every result the run stashes: oversized `connecta.call` reads and writes, those
|
|
849
|
+
transferred without showing a handle, and an oversized return. An id the program
|
|
850
|
+
places in its return value, its error or a later call's arguments is covered alike.
|
|
851
|
+
Executors do not take part, so QuickJS, Worker and custom executors behave the same.
|
|
852
|
+
|
|
853
|
+
Each entry's first chunk, which a reader needs, stays in memory until the run ends,
|
|
854
|
+
and paging inside the run reads it from there. Every way a run answers then seals.
|
|
855
|
+
Sealing starts no further host calls, then waits for the run's host calls and the
|
|
856
|
+
request's credential reads to finish, so its secret set is final. Only then does it
|
|
857
|
+
write first chunks. An entry the set has not grown past is written with no other
|
|
858
|
+
storage work. After growth, its stored bytes are read back and checked, in stored
|
|
859
|
+
form and structurally, so JSON-escaped, URL-encoded and base64 forms and a form split
|
|
860
|
+
across text blocks are found. An entry with no match is written unchanged. An entry
|
|
861
|
+
whose JSON was serialized from a typed array, `Buffer`, `DataView` or `ArrayBuffer`,
|
|
862
|
+
including one a `toJSON` holds or returns, is not read back and stays unreadable
|
|
863
|
+
after growth, since those bytes are checked only while still binary. Stored bytes are
|
|
864
|
+
never rewritten, so
|
|
865
|
+
`totalBytes` and any offset issued during the run stay valid in later runs.
|
|
866
|
+
|
|
867
|
+
An entry that was not written stays unreadable: it matched the final set, a read-back
|
|
868
|
+
or write failed, or work was still pending at the response's answer-by time, which
|
|
869
|
+
is `execute.watchdogMs` plus a margin of at most 2 seconds. None of these publishes
|
|
870
|
+
anything first and withdraws it later. The run's response carries a `note` that
|
|
871
|
+
paging such an id may fail and to rerun the read, and with a write's output among
|
|
872
|
+
them, not to repeat that write. Paging it from a later request fails with
|
|
873
|
+
`result_not_retained`, `retryable: false`: a read's message says to rerun, and a
|
|
874
|
+
write's says its output is not retrievable and not to repeat it. That answer comes
|
|
875
|
+
from a marker holding the entry's binding and no bytes. If storing the marker fails,
|
|
876
|
+
the id answers `not_found`. Cancellation starts no marker or cleanup; storage expiry
|
|
877
|
+
reclaims the chunks. A write's output is always retrievable inside the run that wrote
|
|
878
|
+
it, so a program that needs it can read it before it ends.
|
|
879
|
+
|
|
880
|
+
A direct `call_tool`, `call_destructive_tool` or native tool's stash has no later
|
|
881
|
+
call in its request to wait for, so its bytes pass that request's set when stored,
|
|
882
|
+
and no seal applies. A direct call in a different request shares no secret set
|
|
883
|
+
with it: a credential first used there never checks an earlier direct entry, just
|
|
884
|
+
as it never checks that earlier call's inline output.
|
|
885
|
+
|
|
771
886
|
If paging is unavailable because the stash is full, storage fails, or text
|
|
772
887
|
cannot round-trip as UTF-8, the successful return keeps the existing bounded
|
|
773
888
|
`{ truncated, preview, totalChars, hint }` fallback. Its hint explains that
|
|
@@ -943,7 +1058,9 @@ use `execute.hostCallTimeoutMs`. The `execute_code` description advertises the
|
|
|
943
1058
|
`connecta.call` default as `<n>s/host call`, or, when `calls.defaultTimeoutMs`
|
|
944
1059
|
differs, both deadlines, for example `25s/call (15s result/skill/read)`.
|
|
945
1060
|
A guest may request a longer individual call, but the sandbox
|
|
946
|
-
and request ceilings still bound the whole run.
|
|
1061
|
+
and request ceilings still bound the whole run (`L3`). A direct call's deadline, however
|
|
1062
|
+
it was set, ends at the response ceiling, `execute.watchdogMs` from the
|
|
1063
|
+
request's arrival, and states the deadline it ran under (`L3`). Timeout details are agent-facing;
|
|
947
1064
|
operator records retain only checked classifications and numeric facts.
|
|
948
1065
|
Result processing, including stash reservation, uses the same remaining call
|
|
949
1066
|
budget and request cancellation. If it cannot finish after a successful call,
|
|
@@ -961,15 +1078,95 @@ appear in `timeouts`, bounded to ten entries.
|
|
|
961
1078
|
|
|
962
1079
|
**L3.** Every execution runs under a wall-clock deadline that includes time
|
|
963
1080
|
spent waiting on host calls. Expiry ends the run with an execution error and no
|
|
964
|
-
partial result; the deadline's length is executor configuration (`X1`)
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
the
|
|
971
|
-
|
|
972
|
-
|
|
1081
|
+
partial result; the deadline's length is executor configuration (`X1`), which
|
|
1082
|
+
an executor publishes as `deadlineMs` (QuickJS 30 s, Dynamic Worker 50 s by
|
|
1083
|
+
default).
|
|
1084
|
+
|
|
1085
|
+
Reads stop before that deadline so one slow read costs only itself. Each
|
|
1086
|
+
`connecta.call` read, `read`, `result` and `skill` runs under the smaller of its
|
|
1087
|
+
own deadline (`L4`) and the time left until the run deadline minus a margin of
|
|
1088
|
+
1 s or a tenth of the deadline, whichever is less, and never past the same margin
|
|
1089
|
+
before the response ceiling below; scoped `search` and `describe`
|
|
1090
|
+
are bounded the same way. Those clocks start before the executor's, so they end
|
|
1091
|
+
early rather than late. A read that reaches its cap fails with a retryable
|
|
1092
|
+
`timeout` the program can catch, so `Promise.allSettled` keeps every result that
|
|
1093
|
+
finished. Caught or not, it is the read's failure, not a sandbox timeout. A
|
|
1094
|
+
later read gets the one-tick deadline that is left, and fails the same way.
|
|
1095
|
+
The cap ends the work as well as the wait: the read's downstream signal aborts
|
|
1096
|
+
before the program hears `timeout`, and a listing other callers still wait on is
|
|
1097
|
+
cancelled once the last of them leaves. Storage operations cannot be withdrawn
|
|
1098
|
+
once sent, so one in flight runs to its end. A discovery-health routine already
|
|
1099
|
+
under way cannot be interrupted either, and it may start its next operation,
|
|
1100
|
+
such as reading the connector's generation key, after the program hears
|
|
1101
|
+
`timeout`. No deadline bounds that storage latency.
|
|
1102
|
+
|
|
1103
|
+
Writes are exempt, because a write ended early is a write with an unknown
|
|
1104
|
+
outcome. A write-like call (`W9`), a source write overridden to read included,
|
|
1105
|
+
keeps its own deadline up to `execute.watchdogMs`, and when it outlasts the run
|
|
1106
|
+
the drain still learns its outcome. Classification comes from the catalog, so
|
|
1107
|
+
resolution uses the read's cap for every call: nothing has been sent if it
|
|
1108
|
+
expires. A write that reaches its gate after reads have stopped is refused
|
|
1109
|
+
unsent with a retryable `timeout` at `details.stage: "write gate"`: its answer
|
|
1110
|
+
could not reach the program, so a known "not sent" is worth more than a write
|
|
1111
|
+
only the final counts could report. It spends no write budget and adds nothing
|
|
1112
|
+
to `writes`. Send it again in a new program. If an earlier write in the run landed,
|
|
1113
|
+
a program that lets this refusal end it is not retryable as a whole (below): send
|
|
1114
|
+
only what is left.
|
|
1115
|
+
|
|
1116
|
+
The same time is checked once more as the last step before a call reaches its
|
|
1117
|
+
connector, for reads and writes alike, because resolution, admission or auth can
|
|
1118
|
+
spend it after the write gate, and synchronous work can cross it before a timer
|
|
1119
|
+
runs. A call refused there is a retryable `timeout` at
|
|
1120
|
+
`details.stage: "dispatch gate"` and was not sent. A write refused there had
|
|
1121
|
+
already passed its gate, so it spends write budget and counts in `writes.failed`.
|
|
1122
|
+
|
|
1123
|
+
Above the run deadline sits the response ceiling, `execute.watchdogMs` (55 s by
|
|
1124
|
+
default, above both executors' own deadlines). Connecta enforces it outside the
|
|
1125
|
+
sandbox, because an executor's deadline may live inside the sandbox it is meant
|
|
1126
|
+
to stop: a wedged Dynamic Worker never fires its in-isolate timer. The ceiling
|
|
1127
|
+
counts from the inbound request's arrival, so upload and time in the request and
|
|
1128
|
+
code queues spend it, and it also caps every direct call. On Node, arrival is when
|
|
1129
|
+
`listen` received the request's headers, before it buffered the body. On Workers it
|
|
1130
|
+
is the route's start, which reads the body afterwards. A queue wait still running at
|
|
1131
|
+
the ceiling is refused as a retryable `executor_overloaded`. A run with no time left
|
|
1132
|
+
when it would queue or start is refused as a retryable `timeout` at
|
|
1133
|
+
`details.stage: "response ceiling"`: it did not run and sent nothing.
|
|
1134
|
+
|
|
1135
|
+
A run whose executor has passed its own deadline by the ceiling, or published none and never queued, ends as a
|
|
1136
|
+
non-retryable `timeout` whose message calls the sandbox unresponsive, and its
|
|
1137
|
+
admission lease is released, so wedged runs cannot fill the code pool. A run that
|
|
1138
|
+
reaches the ceiling first, because it queued or because its executor's deadline
|
|
1139
|
+
is set past the ceiling, ends as a retryable `timeout` at
|
|
1140
|
+
`details.stage: "response ceiling"`. Neither has a partial result. Each still
|
|
1141
|
+
carries `hostCalls`, and `writes` when the program sent any. A write still on the
|
|
1142
|
+
wire reaches its deadline at the ceiling and makes the result
|
|
1143
|
+
`write_outcome_unknown`, once, with no replay (`W9`).
|
|
1144
|
+
|
|
1145
|
+
`retryable` on a program's final error answers whether the whole program can run
|
|
1146
|
+
again. Once a write in the run has landed, it is `false` however the run ended, a
|
|
1147
|
+
retryable read, the write gate and the ceiling included, and `retryAfterMs` is
|
|
1148
|
+
dropped, because running the program again would repeat that write. Writes that
|
|
1149
|
+
failed, which are proven not applied, leave the failure's own verdict standing.
|
|
1150
|
+
|
|
1151
|
+
Work after the run that can hold its response, such as storing an oversized return
|
|
1152
|
+
for paging, stops at most 2 s past the ceiling, or a tenth of it if that is smaller
|
|
1153
|
+
(57 s by default). Storage still stalled then costs the paging: the program's return
|
|
1154
|
+
comes back as its preview with no `resultId`.
|
|
1155
|
+
|
|
1156
|
+
The ceiling exists because MCP clients abandon a request at about 60 s, and a client's
|
|
1157
|
+
timeout tells the agent nothing: no typed error, no counts, no write outcome.
|
|
1158
|
+
Connecta answers each `tools/call` as one JSON response, so nothing reaches the
|
|
1159
|
+
client until the run ends, and the client's per-request timer runs for all of it. Claude
|
|
1160
|
+
Code's HTTP transport times each request at 60 s unless `MCP_TOOL_TIMEOUT` or a
|
|
1161
|
+
server's `timeout` is higher; Codex's `tool_timeout_sec` defaults to 60 s, and
|
|
1162
|
+
the MCP TypeScript SDK's request timeout defaults to 60 s. The default leaves 5 s of that
|
|
1163
|
+
for storage after the run and to reach the client. Raise it only when every client
|
|
1164
|
+
of the deployment allows longer. Then set the client's limit first, keep
|
|
1165
|
+
`execute.watchdogMs` a margin below it, and raise any executor deadline
|
|
1166
|
+
(`quickJsExecutor({ timeoutMs })`, `workerExecutor({ timeout })`) to stay below
|
|
1167
|
+
`execute.watchdogMs`. Construction warns when an executor's deadline does not.
|
|
1168
|
+
Cloudflare sets no wall-clock limit on an HTTP Worker while its client stays
|
|
1169
|
+
connected, so neither figure is a platform limit.
|
|
973
1170
|
|
|
974
1171
|
**L4.** Per-execution bounds that are contract, identical in both executors
|
|
975
1172
|
because connecta enforces them above the sandbox:
|
|
@@ -995,9 +1192,9 @@ current page, then pass that cursor to the tool in the next program.
|
|
|
995
1192
|
|
|
996
1193
|
Every `call`, `read`, `search`, `describe`, `result`, and `skill` spends one host
|
|
997
1194
|
call on entry, before resolution, validation, or dispatch; automatic transfer
|
|
998
|
-
pages of a paged result do not. Catching a local refusal refunds nothing.
|
|
1195
|
+
pages of a paged result and of `result(id, { all: true })` do not. Catching a local refusal refunds nothing.
|
|
999
1196
|
The first call beyond the budget ends the host run with one non-retryable
|
|
1000
|
-
`budget_exceeded` (`E2`). Its bridge stays pending until lease disposal so guest
|
|
1197
|
+
`budget_exceeded` (`E2`), whose message says the run ended with nothing returned, to split the remaining work across programs within the limit, and not to resend any write already sent. Its bridge stays pending until lease disposal so guest
|
|
1001
1198
|
`try/catch` cannot swallow it. Later host access, including `emit`, stops; pending
|
|
1002
1199
|
replies are withheld. The refusal reaches no connector, returns no partial result,
|
|
1003
1200
|
and discards accepted emits (`M4`). Calls still resolving or awaiting admission
|
|
@@ -1032,10 +1229,14 @@ bridge. The trusted guest initializer reconstructs values up to 1 MiB from
|
|
|
1032
1229
|
bounded pages, retaining JSON versus text format and every payload code point,
|
|
1033
1230
|
including U+FEFF at page starts. Larger values return a
|
|
1034
1231
|
`{ format: "paged", valueFormat, resultId, totalBytes, truncated: true, hint }`
|
|
1035
|
-
handle.
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1232
|
+
handle. Within the read ceiling its `hint` says to read it with
|
|
1233
|
+
`connecta.result(handle, { all: true })` (`S11`). Past the ceiling, pass the handle or
|
|
1234
|
+
its `resultId` to `connecta.result` and reduce the pages before returning them.
|
|
1235
|
+
Automatic transfers spend no additional host-call budget; explicit paging does, so
|
|
1236
|
+
such a read's `hint` offers the whole loop only when it fits the calls left and
|
|
1237
|
+
otherwise bounds the pages per program
|
|
1238
|
+
([paging](./meta-tools.md#paging-with-connectaresult)). This policy is shared by
|
|
1239
|
+
QuickJS, Worker and custom executors that run provider preludes.
|
|
1039
1240
|
Oversized raw text containing unpaired UTF-16 surrogates returns a handle without
|
|
1040
1241
|
`resultId`, with a notice that it cannot be paged as text. This preserves the
|
|
1041
1242
|
UTF-8 byte-offset contract without silently replacing code units. The completed
|
|
@@ -1044,9 +1245,18 @@ round-trip losslessly.
|
|
|
1044
1245
|
|
|
1045
1246
|
Stashes retain their existing bindings, 15-minute expiry, per-connector page
|
|
1046
1247
|
caps and aggregate capacity charges on memory, SQLite and D1. Capacity exhaustion
|
|
1047
|
-
or a storage failure returns a successful notice without a `resultId`. A
|
|
1248
|
+
or a storage failure returns a successful notice without a `resultId`. A read's
|
|
1249
|
+
value reconstructed through transfer pages gives its stash back once it is whole: the
|
|
1250
|
+
guest never saw a handle for it, so nothing can name those bytes. Giving back removes
|
|
1251
|
+
the entry from the run's memory ledger first, so sealing (`R4a`) has nothing to publish
|
|
1252
|
+
for it; once sealing has begun the entry stays until it expires. A run gives back at
|
|
1253
|
+
most three stashes within a shared one-second budget, and the rest of its entries stay
|
|
1254
|
+
until they expire, so neither slow storage nor a long run of reads costs the answer. A write's stash, and a
|
|
1255
|
+
handle the guest received, keep their charge until they expire, including after
|
|
1256
|
+
`{ all: true }`. A failed
|
|
1048
1257
|
automatic reconstruction retains the handle. Neither result path reissues the
|
|
1049
1258
|
call or changes a completed write into a failed or unknown write. Guest returns use the same store when they exceed the presentation cap (`R2`–`R4`).
|
|
1259
|
+
Pages from a later run see these results only after the run publishes them (`R4a`).
|
|
1050
1260
|
|
|
1051
1261
|
Over-bound arguments and other unpaged bridge results still fail catchably with
|
|
1052
1262
|
executor-owned text. An over-bound result names the called address; an argument
|
|
@@ -1073,10 +1283,11 @@ on arrival. A queued run also holds its MCP request permit
|
|
|
1073
1283
|
([request admission](./architecture.md#admission-in-two-places)) while it waits.
|
|
1074
1284
|
|
|
1075
1285
|
**L8.** Result, log, host-call, and emission budgets are deployment configuration.
|
|
1076
|
-
A call may request `timeoutMs` just as a direct call does
|
|
1077
|
-
capped by the time
|
|
1078
|
-
|
|
1079
|
-
|
|
1286
|
+
A call may request `timeoutMs` just as a direct call does. A read's deadline is
|
|
1287
|
+
capped by the time left before reads stop, a write's by the time left before the
|
|
1288
|
+
program watchdog (`L3`). `execute_code`'s description states the host-call
|
|
1289
|
+
budget, the per-call deadline and, when the executor publishes one, the run
|
|
1290
|
+
deadline as `<n>s/run` — the ones a program must plan around before it runs. The result and log caps live here and in the truncation notice itself
|
|
1080
1291
|
(`R2`, `R5`).
|
|
1081
1292
|
|
|
1082
1293
|
## Activity
|
|
@@ -1155,7 +1366,8 @@ createConnecta({
|
|
|
1155
1366
|
reporting: { tools: ["posthog"], readOnly: true, members: analysts },
|
|
1156
1367
|
},
|
|
1157
1368
|
classification: {
|
|
1158
|
-
posthog: { exec: "read" }, // only
|
|
1369
|
+
posthog: { exec: "read" }, // only if the server is silent about exec and exec is safe to repeat
|
|
1370
|
+
crm: { save_view: "read-repeatable" }, // declared a write; checked, and a repeat is accepted
|
|
1159
1371
|
vendor: { mislabeled_update: "write" },
|
|
1160
1372
|
},
|
|
1161
1373
|
});
|
|
@@ -1172,7 +1384,7 @@ all calls this deployment can make through it.
|
|
|
1172
1384
|
|
|
1173
1385
|
| Precedence | Evidence | Result |
|
|
1174
1386
|
| ---------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1175
|
-
| 1 | Exact deployment `classification[connectorId][toolName]` | `read`, `write`, or `hidden
|
|
1387
|
+
| 1 | Exact deployment `classification[connectorId][toolName]` | `read` where the source is silent, `read-repeatable`, `write`, or `hidden`; only `read-repeatable` can serve a source's declared write as a read |
|
|
1176
1388
|
| 2 | `Connector.classification` provider review | Reviewed writes remain writes; a stale schema digest makes a read a write; an explicit downstream contradiction also invalidates a reviewed read |
|
|
1177
1389
|
| 3 | Downstream annotations | Read only when `readOnlyHint === true && destructiveHint !== true`; otherwise write |
|
|
1178
1390
|
|
|
@@ -1181,8 +1393,14 @@ fail construction. An absent runtime-discovered tool leaves its override
|
|
|
1181
1393
|
dormant and emits an operator warning once per catalog change; the override applies if the name returns.
|
|
1182
1394
|
Hidden tools are absent from search and describe and refuse every call, including
|
|
1183
1395
|
a guessed name. `"write"` selects the host approval path. MCP recovery sends a
|
|
1184
|
-
tool only once when either its effective or source class is write,
|
|
1185
|
-
override
|
|
1396
|
+
tool only once when either its effective or source class is write, including a
|
|
1397
|
+
tool the server is silent about and an override sets to read. A `read` override on a
|
|
1398
|
+
tool whose source declares a write (`readOnlyHint: false`, `destructiveHint: true`,
|
|
1399
|
+
or contradictory hints) has no effect beyond an operator warning: a host may resend
|
|
1400
|
+
a read-annotated call, so that tool stays a write. Where the source is silent, the
|
|
1401
|
+
override asserts that the call is safe to repeat. `read-repeatable` serves a declared
|
|
1402
|
+
write as a read in programs and everywhere else, and keeps these single-send rules; the
|
|
1403
|
+
deployment accepts that a host may repeat the call. The source class is the verified provider review
|
|
1186
1404
|
where present during migration, otherwise the server annotations. It also governs
|
|
1187
1405
|
authorization recovery, program re-runs and ambiguous failure outcomes: after
|
|
1188
1406
|
dispatch, an unknown outcome has no retry or wait advice, including on timeout or
|
|
@@ -1193,8 +1411,49 @@ They are never inferred from descriptions or arguments.
|
|
|
1193
1411
|
if the program does not await it, bounded by its host-call deadline. A write
|
|
1194
1412
|
that reaches accounting after the program settles is not sent. An unanswered
|
|
1195
1413
|
write makes the run fail `write_outcome_unknown` with
|
|
1196
|
-
`writes: { succeeded, failed, unknown }`, regardless of its return value.
|
|
1197
|
-
|
|
1414
|
+
`writes: { succeeded, failed, unknown }`, regardless of its return value. Its
|
|
1415
|
+
`unknownWrites` lists at most ten such writes, each with `address` when resolved,
|
|
1416
|
+
`code`, the redacted `message`, and `toolAnswered: true` when the downstream tool
|
|
1417
|
+
itself answered `isError`. `unknownWritesTruncated` marks a write left out or a
|
|
1418
|
+
message dropped. When a tool answered, the error's `message` leads with
|
|
1419
|
+
`<address> answered: <the tool's words>`, and that entry carries no second copy.
|
|
1420
|
+
An authorization verdict keeps its own lead, with the words in its entry. The
|
|
1421
|
+
words say why the tool answered as it did, not that nothing applied, so read them
|
|
1422
|
+
and check the target before acting. Give the user any approval link or request id
|
|
1423
|
+
they hold. Never rerun the program: writes it already sent would repeat. If the
|
|
1424
|
+
tool's words call for a further call, such as one carrying a provider's approval
|
|
1425
|
+
token, send only that operation, as a new call. Connecta does not block it.
|
|
1426
|
+
|
|
1427
|
+
A tool's words reach any agent up to 4,096 UTF-8 bytes, on direct and native calls
|
|
1428
|
+
too. A longer text is cut between tokens and ends `… [truncated]`, so a link
|
|
1429
|
+
arrives whole or not at all. A tighter result cap on a direct call cuts it further.
|
|
1430
|
+
A program's report is redacted against the run's final credentials before any cut,
|
|
1431
|
+
holds at most 4,096 serialized bytes per message and 8,192 in all, and stays inside
|
|
1432
|
+
`execute.maxResultChars`. Activity and logs never receive these words.
|
|
1433
|
+
|
|
1434
|
+
Program arguments pass through the credentials this request has already sent. The
|
|
1435
|
+
check matches a whole credential or eight consecutive characters of what follows
|
|
1436
|
+
its public framing: an auth scheme, a vendor prefix such as `sk_test_`, or a JWT's
|
|
1437
|
+
header segment. Naming a prefix is no match, an identifier sent beside a credential
|
|
1438
|
+
(a confidential client's ID, a Basic username, or a maintained provider's declared
|
|
1439
|
+
header: Cloudflare's `X-Auth-Email`, Overflow's `x-client-id`) is no match, and a
|
|
1440
|
+
private `writeOnly` value is no match. A read that matches is sent with the match replaced. A write that matches
|
|
1441
|
+
is refused before dispatch with `invalid_args`, because it is never sent altered:
|
|
1442
|
+
nothing leaves, and a value that is genuine can go as its own direct call. A
|
|
1443
|
+
program's first call has no such credentials yet, so this can first appear on a
|
|
1444
|
+
later call. Otherwise a refusal needs one of these: a value that shares eight
|
|
1445
|
+
characters past the prefix with a credential, such as a publishable key beside its
|
|
1446
|
+
secret key; a non-secret value configured as a credential, such as a field not
|
|
1447
|
+
named as an identifier, a `remoteMcp()` or `api()` auth header under any name, or a
|
|
1448
|
+
query parameter with a sensitive-looking name; or text the check cannot finish (escaping
|
|
1449
|
+
nested past eight layers, or past its work budget). The check is a backstop: fewer
|
|
1450
|
+
than eight characters past a prefix pass it, and a program never receives what
|
|
1451
|
+
output redaction withheld.
|
|
1452
|
+
|
|
1453
|
+
A failed program that already wrote carries those counts on its error. The
|
|
1454
|
+
write-budget refusal is `execute_code write budget exceeded (10 writes per run); <address> was not sent.`,
|
|
1455
|
+
followed by checking the run's writes count and sending only the remaining writes
|
|
1456
|
+
in a new program. No
|
|
1198
1457
|
write is automatically retried, including an ambiguous timeout. A downstream
|
|
1199
1458
|
refusal can establish failure only when it covers the whole call: a request
|
|
1200
1459
|
connecta never sent, or an HTTP 4xx refusal (not 408) of the only request the
|
|
@@ -1224,7 +1483,7 @@ omit write tools from `search_tools`, `connecta.search`, and
|
|
|
1224
1483
|
`connecta.describe`, and refuse every write call through `call_tool`,
|
|
1225
1484
|
`call_destructive_tool`, and programs with `read_only_endpoint` before
|
|
1226
1485
|
validation or dispatch. Guides and skills are documentation and may still name
|
|
1227
|
-
those tools.
|
|
1486
|
+
those tools. Both modes keep `execute_code` and every other meta tool; [`execute_read_code`](./meta-tools.md#read-programs) is listed only on writable human endpoints.
|
|
1228
1487
|
[Native pools](./meta-tools.md#native-tool-surface) list individual downstream
|
|
1229
1488
|
tools and omit writes when read-only. They retain `execute_code` by default;
|
|
1230
1489
|
`executeCode: false` replaces it with `read_result`. Approval belongs to the client.
|
|
@@ -1238,9 +1497,9 @@ removed. `W1`–`W8` and `W11` remain retired.
|
|
|
1238
1497
|
Documented divergences, with reasons. Everything else must match.
|
|
1239
1498
|
|
|
1240
1499
|
**X1. Deadline default.** QuickJS defaults to 30 s wall clock and terminates the
|
|
1241
|
-
child; the Dynamic Worker defaults to
|
|
1500
|
+
child; the Dynamic Worker defaults to 50 s and races the program against an
|
|
1242
1501
|
in-isolate timer. Both satisfy `L3`; the numbers are each executor's
|
|
1243
|
-
configuration and the error text differs.
|
|
1502
|
+
configuration, published as `deadlineMs`, and the error text differs.
|
|
1244
1503
|
|
|
1245
1504
|
**X2. Memory, stack, and CPU mechanism.** QuickJS exposes explicit heap, stack,
|
|
1246
1505
|
and guest-CPU limits (`L5`); the Dynamic Worker has no such knobs, so workerd's
|
|
@@ -1257,6 +1516,8 @@ promise independently of upstream evaluation. Guest computation may continue
|
|
|
1257
1516
|
briefly until platform teardown, but no late return, callback, `finally`, call,
|
|
1258
1517
|
or emit changes the finished host result. Disposal releases request resources;
|
|
1259
1518
|
it does not guarantee a synchronous guest-CPU kill.
|
|
1519
|
+
If freeing a finished run's runtime fails, the run keeps its answer, a warning is
|
|
1520
|
+
logged and the child is replaced before it can take another run.
|
|
1260
1521
|
|
|
1261
1522
|
**X4. Log rendering and capture.** QuickJS JSON-stringifies non-string arguments
|
|
1262
1523
|
and captures `log`, `info`, `warn`, `error`, and `debug`; the Dynamic Worker
|
|
@@ -1365,51 +1626,52 @@ rejection, and branded adapter acceptance across module copies.
|
|
|
1365
1626
|
declaration against `GuestApi`, checks arguments in both directions, and rejects
|
|
1366
1627
|
invented, mistyped, and missing members and authored blocks the host refuses.
|
|
1367
1628
|
|
|
1368
|
-
| Clauses | Test
|
|
1369
|
-
| ---------------------- |
|
|
1370
|
-
| `P1`, `P5` | both guest-contract executors (TypeScript syntax, trailing terminators, wrapper recovery), `test/program-source.test.ts` (which semicolons and wrappers are recovered), `test/quickjs-executor.node.test.ts` (`normalizeCode`)
|
|
1371
|
-
| `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.node.test.ts` (exact absent globals and blocked imports), `test/deployment-shapes.node.test.ts` (loader-only Worker construction)
|
|
1372
|
-
| `P3`, `X9` | `test/guest-api-contract.test.ts`, `test/execute.test.ts`
|
|
1373
|
-
| `P4` | `test/guest-api-contract.test.ts` (no cross-run leakage), `test/execute.test.ts` (one catalog load per connector per execution)
|
|
1374
|
-
| `A1`, `A2` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (canonical addressing), `test/server.test.ts` (bounded live connector inventory)
|
|
1375
|
-
| `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)
|
|
1376
|
-
| `S3` | `test/guest-api-contract.test.ts` (typed uncaught bound), `test/execute.test.ts` (count limits, fan-out bound)
|
|
1377
|
-
| `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)
|
|
1378
|
-
| `
|
|
1379
|
-
| `
|
|
1380
|
-
| `
|
|
1381
|
-
| `
|
|
1382
|
-
| `
|
|
1383
|
-
| `
|
|
1384
|
-
| `
|
|
1385
|
-
| `
|
|
1386
|
-
| `
|
|
1387
|
-
| `
|
|
1388
|
-
| `
|
|
1389
|
-
| `
|
|
1390
|
-
| `
|
|
1391
|
-
| `
|
|
1392
|
-
| `
|
|
1393
|
-
| `
|
|
1394
|
-
| `
|
|
1395
|
-
| `
|
|
1396
|
-
| `
|
|
1397
|
-
| `
|
|
1398
|
-
| `
|
|
1399
|
-
| `
|
|
1400
|
-
| `
|
|
1401
|
-
| `
|
|
1402
|
-
| `
|
|
1403
|
-
| `
|
|
1404
|
-
| `
|
|
1405
|
-
| `
|
|
1406
|
-
| `
|
|
1407
|
-
| `
|
|
1408
|
-
| `
|
|
1409
|
-
| `
|
|
1410
|
-
| `
|
|
1411
|
-
|
|
1412
|
-
|
|
1629
|
+
| Clauses | Test |
|
|
1630
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1631
|
+
| `P1`, `P5` | both guest-contract executors (TypeScript syntax, trailing terminators, wrapper recovery), `test/program-source.test.ts` (which semicolons and wrappers are recovered), `test/quickjs-executor.node.test.ts` (`normalizeCode`) |
|
|
1632
|
+
| `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.node.test.ts` (exact absent globals and blocked imports), `test/deployment-shapes.node.test.ts` (loader-only Worker construction) |
|
|
1633
|
+
| `P3`, `X9` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` |
|
|
1634
|
+
| `P4` | `test/guest-api-contract.test.ts` (no cross-run leakage), `test/execute.test.ts` (one catalog load per connector per execution) |
|
|
1635
|
+
| `A1`, `A2` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (canonical addressing), `test/server.test.ts` (bounded live connector inventory) |
|
|
1636
|
+
| `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) |
|
|
1637
|
+
| `S3` | `test/guest-api-contract.test.ts` (typed uncaught bound), `test/execute.test.ts` (count limits, fan-out bound) |
|
|
1638
|
+
| `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) |
|
|
1639
|
+
| `S11` | both guest-contract executors (whole reads, ceiling refusal, stash reclaim), `test/guest-api-contract-quickjs.node.test.ts` (ceiling scaling, default-limits proof), `test/program-result-cases.ts` (sealing composition) |
|
|
1640
|
+
| `S5`, `S6` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`unwrapMcpResult`, fail-closed annotations, activity parity) |
|
|
1641
|
+
| `S7` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (parallel calls and shared admission) |
|
|
1642
|
+
| `S8`, `E1`, `X11` | both guest-contract executors (caught call, discovery, utility, removed-function, and forgery cases; typed promise rejections), `test/quickjs-executor.node.test.ts` (oversized messages, private transport, forged outcomes) |
|
|
1643
|
+
| `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) |
|
|
1644
|
+
| `S10` | `test/resource-read.test.ts` (qualified routing, whole-connector grants, personal credentials, redaction, admission, timeout, cancellation and downstream MCP reads), both guest-contract executors (successful resource reads and caught/uncaught typed failures) |
|
|
1645
|
+
| `E2`, `E8` | `test/guest-api-contract.test.ts` (code → `retryable`, caught, parallel, and uncaught validation recovery, a conflict's bounded `current`), `test/meta-tools-call.test.ts` (direct, destructive, provider fallback), `test/validate.test.ts` (bounded payload-free findings), `test/errors.test.ts` |
|
|
1646
|
+
| `E3`, `E4` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`auth_required`, destructive reroute), `test/identity-write-journey.node.test.ts` (read-only refusals before validation or dispatch, actual QuickJS) |
|
|
1647
|
+
| `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.node.test.ts` (mid-run shutdown) |
|
|
1648
|
+
| `E6`, `X8` | `test/guest-api-contract.test.ts` (unknown and inherited members, retained error identities), `test/quickjs-executor.node.test.ts` |
|
|
1649
|
+
| `E7` | `test/guest-api-contract.test.ts` (refusals about a `503`-named connector), `test/errors.test.ts` |
|
|
1650
|
+
| `R1`, `R2`, `R3`, `R4` | `test/guest-api-contract.test.ts` and `test/guest-api-contract-quickjs.node.test.ts` (small pass-through, shared return paging and unavailable fallback, no repeated writes, live authority), `test/program-result-cases.ts` (reconstruction, redaction, notice survives a 2,000-character spill) |
|
|
1651
|
+
| `M6`, `M9` | verdicts; `R2`'s guard, `M1`'s strict typing, and `M2`'s collect-then-deliver are their enforcement |
|
|
1652
|
+
| `R5` | `test/guest-api-contract.test.ts` |
|
|
1653
|
+
| `R6`–`R8` | `test/guest-api-contract.test.ts` (normal result keys), `test/execute.test.ts` (opt-in operation aggregates, failure paths, payload exclusion) |
|
|
1654
|
+
| `Y1`, `Y2`, `Y3` | `test/guest-api-contract.test.ts` (one attempt per call, retryable flags by code) |
|
|
1655
|
+
| `Y4` | `test/meta-tools-call.test.ts`, `test/call-admission.test.ts` (one attempt, retry hints, caller reissue) |
|
|
1656
|
+
| `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) |
|
|
1657
|
+
| `L3`, `X1` | `test/guest-api-contract.test.ts` (short-deadline executors, read caps, write exemption, the write gate, the final dispatch gate, a write on the wire at the ceiling and a stall after a write), `test/node.node.test.ts` (upload counts against the ceiling, arrival comes only from the host), `test/program-writes.test.ts` (what makes a failed program retryable), `test/meta-tools-call.test.ts` (direct and native calls at the final gate), `test/request-admission.test.ts` (a 65 s downstream behind a 60 s client on a fake clock, with the old ceiling as its control), `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) |
|
|
1658
|
+
| `L4`, `L8` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (shared discovery/call budgets and terminal catch-and-continue loops and queued-write cancellation on both executors) |
|
|
1659
|
+
| `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue), `test/executor-requests.test.ts` (queued handoffs and refusal shape across separate workerd requests), `test/request-context.node.test.ts` (queued and QuickJS host-call request context) |
|
|
1660
|
+
| `L6`, `X10` | `test/guest-api-contract-quickjs.node.test.ts` and `test/guest-api-contract.test.ts` (whole oversized reads and one-write pageable handles), `test/sqlite-storage.node.test.ts` and `test/storage-sql-d1.test.ts` (SQL stash and native D1 fault probes), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message) |
|
|
1661
|
+
| `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no execution event by default), `test/identity-write-journey.node.test.ts` (opt-in real-program utility/program rows, payload exclusion and detached writes), `test/activity.test.ts` (the shared code → friction table, the identity clamp, the one-attempt floor), `test/browser/operator-ui.spec.ts` (identity changes), `test/sql-storage-contract.ts`, run by `test/sqlite-storage.node.test.ts` (historical activity rows still round-trip) |
|
|
1662
|
+
| `V5`, `W9` | `test/program-writes.test.ts` (unawaited program drain, failed-program accounting), `test/identity-write-journey.node.test.ts` (committed ambiguity, reconciliation and physical writes), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
|
|
1663
|
+
| `W10`, `W12` | `test/identity-write-journey.node.test.ts` (real HTTP/MCP and QuickJS: write budget, read-only discovery and refusal, named guide, pool inheritance/reopening and connector policy), `test/program-writes.test.ts` (construction refusals and classification drift), `test/browser/operator-ui.spec.ts` (the badge) |
|
|
1664
|
+
| `M1` | `test/guest-api-contract.test.ts` (invalid emits throw catchably, accept nothing), `test/execute-emit.test.ts` (rejected shapes, bounded native snapshots, paging before metadata traversal, and cyclic presentation refusal) |
|
|
1665
|
+
| `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) |
|
|
1666
|
+
| `M4` | `test/guest-api-contract.test.ts` (discard is visible), `test/execute-emit.test.ts` (structured and plain paths), `test/quickjs-executor.node.test.ts` (mid-run shutdown) |
|
|
1667
|
+
| `M5`, `M7` | `test/execute-emit.test.ts` (both budgets fail the crossing block; host-call budget untouched) |
|
|
1668
|
+
| `M8` | two arms passing one case table, `test/codemode-compat.test.ts` |
|
|
1669
|
+
| `M10` | `test/execute-emit.test.ts` (aggregate present, numbers only, absent when nothing emitted) |
|
|
1670
|
+
| `X4` | `test/guest-api-contract.test.ts` (string logs only), `test/quickjs-executor.node.test.ts` (logs before cancellation) |
|
|
1671
|
+
| `X3`, `X6` | `test/quickjs-executor.node.test.ts` (cancels a running child, never-settling await), `test/execute.test.ts` (a wedged executor stops being awaited) |
|
|
1672
|
+
| `X7` | `P3`'s tests; the Workers superset is deliberately unused |
|
|
1673
|
+
|
|
1674
|
+
The surface itself is checked by `test/server.test.ts` (the exact tool list)
|
|
1413
1675
|
and `test/program-writes.test.ts` (deployment policy and classification construction refusals).
|
|
1414
1676
|
|
|
1415
1677
|
### Adding a guest function
|
|
@@ -1422,16 +1684,16 @@ each bridge call by the entry's budget class and binds it by name to a host
|
|
|
1422
1684
|
implementation, and the `execute_code` description joins each entry's compact
|
|
1423
1685
|
declaration and names the functions that spend no host call.
|
|
1424
1686
|
|
|
1425
|
-
| Function | Budget | Purpose
|
|
1426
|
-
| ----------- | ---------- |
|
|
1427
|
-
| `search` | `hostCall` | Searches the catalog and returns one flat page of `{ tools }`.
|
|
1428
|
-
| `describe` | `hostCall` | Describes exact addresses, one entry per address in order.
|
|
1429
|
-
| `call` | `hostCall` | Calls one tool and inlines transfer pages and native content.
|
|
1430
|
-
| `read` | `hostCall` | Reads one advertised resource URI.
|
|
1431
|
-
| `result` | `hostCall` |
|
|
1432
|
-
| `skill` | `hostCall` | Fetches a skill's text by exact name.
|
|
1433
|
-
| `emit` | `emission` | Forwards a content block to the client; needs no await.
|
|
1434
|
-
| `unawaited` | `once` | Tells the host a
|
|
1687
|
+
| Function | Budget | Purpose |
|
|
1688
|
+
| ----------- | ---------- | ------------------------------------------------------------------------ |
|
|
1689
|
+
| `search` | `hostCall` | Searches the catalog and returns one flat page of `{ tools }`. |
|
|
1690
|
+
| `describe` | `hostCall` | Describes exact addresses, one entry per address in order. |
|
|
1691
|
+
| `call` | `hostCall` | Calls one tool and inlines transfer pages and native content. |
|
|
1692
|
+
| `read` | `hostCall` | Reads one advertised resource URI. |
|
|
1693
|
+
| `result` | `hostCall` | Reads a retained result whole, or pages it by page or UTF-8 byte offset. |
|
|
1694
|
+
| `skill` | `hostCall` | Fetches a skill's text by exact name. |
|
|
1695
|
+
| `emit` | `emission` | Forwards a content block to the client; needs no await. |
|
|
1696
|
+
| `unawaited` | `once` | Tells the host a program read a result field off an unawaited call. |
|
|
1435
1697
|
|
|
1436
1698
|
A `hostCall` function spends one host call on entry (`L4`, `M7`) and returns a
|
|
1437
1699
|
Promise whose result fields mark an unawaited read. The runtime wraps
|