@zackbart/connecta 0.31.0 → 0.33.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.
Files changed (169) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +30 -20
  3. package/bin/connecta.mjs +25 -6
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.d.ts +167 -23
  6. package/dist/access-tokens.js +676 -140
  7. package/dist/agent-identity.js +47 -0
  8. package/dist/auth/cloudflare-access.js +13 -5
  9. package/dist/auth-elicitation.js +1 -1
  10. package/dist/call-admission-context.js +15 -0
  11. package/dist/call-admission.js +30 -43
  12. package/dist/call-recovery.js +6 -2
  13. package/dist/catalog-service.js +7 -2
  14. package/dist/config-value-sources.js +14 -9
  15. package/dist/config.d.ts +58 -78
  16. package/dist/config.js +143 -47
  17. package/dist/connector-access.d.ts +4 -33
  18. package/dist/connector-access.js +15 -99
  19. package/dist/connector-caller.d.ts +7 -1
  20. package/dist/connector-caller.js +3 -2
  21. package/dist/connectors/api-connector.d.ts +27 -4
  22. package/dist/connectors/api-connector.js +2 -0
  23. package/dist/connectors/guarded-fetch.d.ts +9 -0
  24. package/dist/connectors/guarded-fetch.js +7 -0
  25. package/dist/connectors/remote-mcp.js +2 -0
  26. package/dist/describe-config.d.ts +32 -7
  27. package/dist/describe-config.js +9 -18
  28. package/dist/errors.d.ts +1 -1
  29. package/dist/errors.js +1 -0
  30. package/dist/execute.js +16 -14
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +30 -10
  33. package/dist/invocation.js +12 -7
  34. package/dist/membership.d.ts +51 -0
  35. package/dist/membership.js +121 -0
  36. package/dist/meta-tools.js +17 -19
  37. package/dist/module-contracts.d.ts +23 -6
  38. package/dist/operator-record.d.ts +2 -2
  39. package/dist/operator-record.js +2 -2
  40. package/dist/operator-ui/agents.d.ts +74 -0
  41. package/dist/operator-ui/agents.js +104 -0
  42. package/dist/operator-ui/contract.d.ts +151 -29
  43. package/dist/operator-ui/endpoints.js +192 -0
  44. package/dist/operator-ui/fix-prompts.js +4 -4
  45. package/dist/operator-ui/generated.js +5 -4
  46. package/dist/operator-ui/member-page.js +112 -0
  47. package/dist/operator-ui/model.d.ts +15 -7
  48. package/dist/operator-ui/view.d.ts +84 -19
  49. package/dist/operator-ui/view.js +83 -21
  50. package/dist/page-styles.js +1 -1
  51. package/dist/program-writes.js +2 -2
  52. package/dist/provider.d.ts +63 -11
  53. package/dist/provider.js +46 -7
  54. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  55. package/dist/providers/_shared/google/workspace.js +31 -9
  56. package/dist/providers/_shared/rest/tools.js +2 -2
  57. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  58. package/dist/providers/_shared/rest/value-safety.js +699 -7
  59. package/dist/providers/breeze/skill.generated.js +2 -2
  60. package/dist/providers/ccb/index.js +1 -1
  61. package/dist/providers/cloudflare/index.js +3 -3
  62. package/dist/providers/cloudflare/rest.js +25 -3
  63. package/dist/providers/cloudflare/skill.generated.js +2 -2
  64. package/dist/providers/cloudflare/value-safety.js +32 -0
  65. package/dist/providers/docs/index.d.ts +1 -1
  66. package/dist/providers/docs/index.js +5 -5
  67. package/dist/providers/docs/skill.generated.js +2 -2
  68. package/dist/providers/drive/index.d.ts +1 -1
  69. package/dist/providers/drive/index.js +4 -4
  70. package/dist/providers/drive/skill.generated.js +2 -2
  71. package/dist/providers/forms/index.d.ts +1 -1
  72. package/dist/providers/forms/index.js +5 -5
  73. package/dist/providers/forms/skill.generated.js +2 -2
  74. package/dist/providers/github/skill.generated.js +2 -2
  75. package/dist/providers/gmail/index.d.ts +1 -1
  76. package/dist/providers/gmail/index.js +279 -26
  77. package/dist/providers/gmail/skill.generated.js +2 -2
  78. package/dist/providers/linear/index.js +1 -1
  79. package/dist/providers/mixpanel/index.d.ts +27 -12
  80. package/dist/providers/mixpanel/index.js +58 -6
  81. package/dist/providers/mixpanel/operations.js +20 -0
  82. package/dist/providers/mixpanel/rest.js +603 -0
  83. package/dist/providers/mixpanel/skill.generated.js +3 -2
  84. package/dist/providers/mixpanel/value-safety.js +16 -0
  85. package/dist/providers/notion/index.d.ts +7 -0
  86. package/dist/providers/notion/index.js +116 -11
  87. package/dist/providers/notion/rest.js +183 -24
  88. package/dist/providers/notion/skill.generated.js +4 -4
  89. package/dist/providers/notion/value-safety.js +30 -5
  90. package/dist/providers/overflow/index.js +1 -1
  91. package/dist/providers/planning-center/index.d.ts +27 -0
  92. package/dist/providers/planning-center/index.js +287 -25
  93. package/dist/providers/planning-center/skill.generated.js +24 -4
  94. package/dist/providers/sheets/index.d.ts +4 -1
  95. package/dist/providers/sheets/index.js +9 -6
  96. package/dist/providers/sheets/skill.generated.js +2 -2
  97. package/dist/providers/slides/index.d.ts +1 -1
  98. package/dist/providers/slides/index.js +6 -6
  99. package/dist/providers/slides/skill.generated.js +2 -2
  100. package/dist/providers/stripe/rest.js +9 -2
  101. package/dist/providers/stripe/skill.generated.js +2 -2
  102. package/dist/providers/stripe/value-safety.js +22 -0
  103. package/dist/providers/vercel/index.js +2 -2
  104. package/dist/providers/vercel/rest.js +20 -2
  105. package/dist/providers/vercel/skill.generated.js +2 -2
  106. package/dist/providers/vercel/value-safety.js +22 -0
  107. package/dist/providers/wispr-flow/index.d.ts +14 -0
  108. package/dist/providers/wispr-flow/index.js +117 -0
  109. package/dist/providers/wispr-flow/skill.generated.js +8 -0
  110. package/dist/registry.d.ts +2 -3
  111. package/dist/registry.js +8 -9
  112. package/dist/routes/access-tokens.js +82 -75
  113. package/dist/routes/activity-disclosure.js +17 -18
  114. package/dist/routes/activity.js +34 -54
  115. package/dist/routes/actor-labels.js +33 -4
  116. package/dist/routes/agent-path.js +43 -0
  117. package/dist/routes/connect.js +16 -12
  118. package/dist/routes/credentials.js +16 -40
  119. package/dist/routes/mcp.js +155 -85
  120. package/dist/routes/oauth-management.js +11 -5
  121. package/dist/routes/oauth.js +3 -9
  122. package/dist/routes/operator.js +25 -17
  123. package/dist/routes/shared.d.ts +60 -20
  124. package/dist/routes/shared.js +187 -96
  125. package/dist/routes/ui-config.js +437 -97
  126. package/dist/routes/ui-data.js +2 -2
  127. package/dist/routes/ui.js +9 -16
  128. package/dist/runtime/call-admission.js +9 -0
  129. package/dist/server.js +8 -0
  130. package/dist/skills.js +5 -4
  131. package/dist/storage/keys.js +9 -1
  132. package/dist/tool-safety.js +13 -5
  133. package/dist/types.d.ts +12 -1
  134. package/dist/ui.d.ts +2 -2
  135. package/dist/ui.js +17 -15
  136. package/dist/usage-guide.js +3 -3
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/workspace-address.js +9 -0
  140. package/dist/workspace-subject.js +23 -0
  141. package/documentation/README.md +46 -22
  142. package/documentation/architecture.md +378 -58
  143. package/documentation/auth.md +806 -215
  144. package/documentation/code-mode.md +93 -81
  145. package/documentation/deploying.md +128 -26
  146. package/documentation/integrating.md +145 -22
  147. package/documentation/meta-tools.md +58 -46
  148. package/documentation/migration-0.33.md +344 -0
  149. package/documentation/operating.md +14 -9
  150. package/documentation/operator-tests.md +117 -31
  151. package/documentation/operator-ui.md +326 -55
  152. package/documentation/provider-auth-migration.md +42 -27
  153. package/documentation/provider-migration-0.29.md +3 -3
  154. package/examples/worker/AGENTS.md +35 -8
  155. package/examples/worker/README.md +92 -53
  156. package/examples/worker/src/connecta.config.ts +41 -20
  157. package/examples/worker/wrangler.jsonc +3 -0
  158. package/package.json +7 -2
  159. package/templates/node/.env.example +3 -0
  160. package/templates/node/AGENTS.md +33 -10
  161. package/templates/node/Dockerfile +2 -2
  162. package/templates/node/README.md +71 -44
  163. package/templates/node/docker-compose.yml +3 -2
  164. package/templates/node/package.json +4 -3
  165. package/templates/node/src/adopt-token.ts +23 -0
  166. package/templates/node/src/connecta.config.ts +33 -12
  167. package/templates/node/src/rotate-token.ts +17 -0
  168. package/dist/tool-safety.d.ts +0 -9
  169. package/templates/node/src/provision-token.ts +0 -17
@@ -48,7 +48,7 @@ QuickJS subpath:
48
48
  import { createConnecta } from "@zackbart/connecta";
49
49
  import { quickJsExecutor } from "@zackbart/connecta/quickjs";
50
50
 
51
- createConnecta({ executor: quickJsExecutor() /* connectors, auth, storage… */ });
51
+ createConnecta({ executor: quickJsExecutor(), readOnly: false /* connectors, auth, storage… */ });
52
52
  ```
53
53
 
54
54
  `quickJsExecutor()` runs each program in a disposable child process, with CPU,
@@ -61,7 +61,7 @@ On Workers the Worker Loader is the sandbox; Dynamic Workers require the Paid pl
61
61
  ```ts
62
62
  import { workerExecutor } from "@zackbart/connecta/worker";
63
63
 
64
- createConnecta({ executor: workerExecutor({ loader: env.LOADER }) });
64
+ createConnecta({ executor: workerExecutor({ loader: env.LOADER }), readOnly: false /* … */ });
65
65
  ```
66
66
 
67
67
  The adapter uses only `loader` and an optional `timeout`. Its admission defaults
@@ -80,7 +80,7 @@ and cleanup at budget exhaustion, cancellation, and deadlines; the wrapper only
80
80
 
81
81
  ## What an executor must implement
82
82
 
83
- The Promise provider/result seam lives in `src/types.ts`. Custom implementations
83
+ The Promise provider/result seam is declared in the installed `dist/types.d.ts`. Custom implementations
84
84
  also need the lifecycle wrapper above; structural compatibility alone is insufficient.
85
85
 
86
86
  ```ts
@@ -283,7 +283,10 @@ signature is byte-identical to the one `search_tools` returns.
283
283
  integration is ambiguous. `safety: "readOnly"` selects stored read verdicts;
284
284
  `"approvalRequired"` selects stored write verdicts; omitted or `"all"` selects
285
285
  both. Search and describe rows carry `classification`. These filters grant no
286
- authority and change no pool trust or call admission decision.
286
+ authority and change no call admission decision. On a read-only endpoint,
287
+ search and describe omit write tools (`W12`): a write is absent from every
288
+ filter, and describing one is `unknown_tool`. `connecta.skill` guides are not
289
+ filtered and may name such a write; calling it is still refused.
287
290
 
288
291
  **S2.** A requested object schema carries `inputKeys`, `requiredInputKeys`
289
292
  (declared properties only), and `outputKeys`: the names the rendered schema
@@ -394,10 +397,10 @@ and a 32 KiB bridge ceiling. `page` is zero-based and mutually exclusive with
394
397
  are at least four bytes wide and align both edges to UTF-8 boundaries.
395
398
  Follow `nextOffset`, reassemble inside the program, and return a reduced value.
396
399
  Before returning a page, the host rechecks current authentication, connector/tool
397
- grants, pool membership, and endpoint trust against both the original and current
398
- tool classification. A read-only endpoint cannot page a write result, so a
399
- direct write there returns an inline truncation notice without stashing the
400
- result or advertising a paging action. Binding
400
+ grants, pool membership, and whether the endpoint is read-only against both the
401
+ original and current tool classification. A read-only endpoint cannot page a
402
+ write result; it never runs a write, so any write result it is asked for came
403
+ from a writable endpoint. Binding
401
404
  mismatches, revoked access, and old entries without bindings are `not_found`, as
402
405
  are unknown or expired ids; a storage failure is `unavailable`. IDs are random UUIDs.
403
406
  The `get_result` meta-tool is removed.
@@ -477,10 +480,10 @@ Connecta passes an admitted decoded URI as an opaque MCP `resources/read`
477
480
  argument to the configured endpoint. It never fetches that
478
481
  URI or creates a connector from it.
479
482
 
480
- Resources are protocol reads in both `read-only` and `trusted` pools. A read
481
- requires a grant to the whole connector through the endpoint/pool and identity
482
- intersection. Exact-tool grants, including guarded grants, authorize no
483
- resources. An absent, hidden, tool-restricted or resource-incapable connector
483
+ Resources are protocol reads on read-only and writable endpoints alike. A read
484
+ requires the whole connector on the endpoint: plain `/mcp`, or a pool whose
485
+ `tools` list the connector id. A pool listing only exact `connector.tool`
486
+ addresses authorizes no resources. An absent, hidden, tool-restricted or resource-incapable connector
484
487
  fails with `unknown_address`. The URI and extra arguments cannot select a
485
488
  different credential owner or downstream subject. Personal credentials come
486
489
  from the admitted principal, just as they do for tool calls.
@@ -553,27 +556,27 @@ URL-elicitation hosts retain their existing flow.
553
556
  may do. A provider maps each downstream failure to the code that tells the
554
557
  caller what to do next, and never invents a cause it was not told.
555
558
 
556
- | Code | Raised when | `retryable` |
557
- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
558
- | `unknown_address` | no visible connector owns the tool address or grants the resource reader | false |
559
- | `unknown_tool` | the connector has no such tool | false |
560
- | `destructive_tool_requires_approval` | a program in a read-only pool attempted a write (`E4`) | false |
561
- | `auth_required` | the credential is missing, expired, or rejected | false |
562
- | `invalid_args` | arguments or discovery bounds were rejected | false |
563
- | `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 |
564
- | `conflict` | the write named a base version someone else already moved past; nothing changed. `details.current` says where things stand (at most 20 whole-number entries) — re-read, reapply, retry with that base | false |
565
- | `input_required_unsupported` | a downstream asked for input; use `nextAction` for the equivalent direct call | false |
566
- | `rate_limited` | the downstream reported a rate limit | true |
567
- | `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 |
568
- | `timeout` | a call or sandbox deadline expired; details name operation, stage, elapsedMs, and deadlineMs | per operation |
569
- | `program_error` | guest JavaScript failed; `details.name`, `line`, and a fixed repair hint describe it | false |
570
- | `cancelled` | the run ended while this call was in flight (`E5`) | false |
571
- | `connector_call_failed` | anything else the connector threw | per message |
572
- | `catalog_lookup_failed` | the connector's catalog could not be loaded | per cause |
573
- | `result_processing_failed` | the result could not be prepared | per message |
574
- | `result_too_large` | a discovery response exceeded its byte bound | false |
575
- | `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
576
- | `write_outcome_unknown` | a trusted-pool write was sent and no answer came back; it is never sent again (`W9`) | false |
559
+ | Code | Raised when | `retryable` |
560
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
561
+ | `unknown_address` | no visible connector owns the tool address or grants the resource reader | false |
562
+ | `unknown_tool` | the connector has no such tool | false |
563
+ | `read_only_endpoint` | a program on a read-only endpoint attempted a write (`E4`) | false |
564
+ | `auth_required` | the credential is missing, expired, or rejected | false |
565
+ | `invalid_args` | arguments or discovery bounds were rejected | false |
566
+ | `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 |
567
+ | `conflict` | the write named a base version someone else already moved past; nothing changed. `details.current` says where things stand (at most 20 whole-number entries) — re-read, reapply, retry with that base | false |
568
+ | `input_required_unsupported` | a downstream asked for input; use `nextAction` for the equivalent direct call | false |
569
+ | `rate_limited` | the downstream reported a rate limit | true |
570
+ | `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 |
571
+ | `timeout` | a call or sandbox deadline expired; details name operation, stage, elapsedMs, and deadlineMs | per operation |
572
+ | `program_error` | guest JavaScript failed; `details.name`, `line`, and a fixed repair hint describe it | false |
573
+ | `cancelled` | the run ended while this call was in flight (`E5`) | false |
574
+ | `connector_call_failed` | anything else the connector threw | per message |
575
+ | `catalog_lookup_failed` | the connector's catalog could not be loaded | per cause |
576
+ | `result_processing_failed` | the result could not be prepared | per message |
577
+ | `result_too_large` | a discovery response exceeded its byte bound | false |
578
+ | `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
579
+ | `write_outcome_unknown` | a program write was sent and no answer came back; it is never sent again (`W9`) | false |
577
580
 
578
581
  **E3.** `auth_required` carries the same recovery envelope as `call_tool`:
579
582
  `connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
@@ -581,16 +584,13 @@ caller what to do next, and never invents a cause it was not told.
581
584
  sentence. A program cannot recover credentials — only an operator can — so stop
582
585
  and let the failure reach the model.
583
586
 
584
- **E4.** A tool classified as a write never runs in a
585
- program unless the pool is trusted (`W12`). It is refused with
586
- `destructive_tool_requires_approval` before validation and before anything is
587
- sent, `nextAction` carrying its canonical address to `call_destructive_tool`
588
- and a schema-filtered snapshot within the 512-byte echo budget. `argsRedacted`
589
- marks partial or withheld arguments; use the original arguments if this flag is
590
- set or `args` is absent. Snapshots are never clipped. The host's prompt on that
591
- call is the approval, and the only one: generated code can neither mint the
592
- capability nor approve its own write, and the model's short `reason` grants no
593
- authority and never goes downstream.
587
+ **E4.** A tool classified as a write never runs on a read-only endpoint
588
+ (`W12`). A program's call is refused with `read_only_endpoint` before
589
+ validation and before anything is sent. The refusal carries no `nextAction` and
590
+ echoes no arguments: no route on that endpoint writes, so the program reports
591
+ it rather than retrying. Generated code can neither mint the capability nor
592
+ change the endpoint; on a writable endpoint, approval of the program as a write
593
+ belongs to the client.
594
594
 
595
595
  **E5.** Failures of the _execution_, not of a call, never appear inside the
596
596
  guest: admission rejection (`executor_overloaded`, retryable, with
@@ -631,7 +631,7 @@ Program diagnostic fields are bounded before transport, including escaped text;
631
631
  a large custom error name cannot turn a failure into a truncated success.
632
632
 
633
633
  **E7.** `retryable` for `unknown_address`, `unknown_tool`, and
634
- `destructive_tool_requires_approval` is pinned false, never inferred from an
634
+ `read_only_endpoint` is pinned false, never inferred from an
635
635
  address containing `503`, `429`, or `temporar`. The first two carry
636
636
  `nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }`
637
637
  — scoped discovery keyed to the surface the caller has, since a program cannot
@@ -686,7 +686,7 @@ Caught call failures retain that connector visibility when catalog loading or
686
686
  tool resolution fails. Recovery messages listing configured connectors or
687
687
  local guides retain visibility of those contributors too.
688
688
  Each page rechecks current auth,
689
- grants, pool membership, and trust. Reading a stash inside a program carries
689
+ grants, pool membership, and the endpoint's `readOnly`. Reading a stash inside a program carries
690
690
  its dependencies forward to any new return stash. The page cap is the smallest
691
691
  of the deployment cap and contributing connectors' effective `maxResultBytes`.
692
692
  Read-only endpoints cannot page write results. A UUID is a handle, never a grant.
@@ -737,9 +737,6 @@ and nonzero pad bits are rejected before collection. Size budgets still apply.
737
737
  MCP-native output a return value cannot carry: base64 is not projectable, so a
738
738
  block that fits the inline call limit (`S5`, `L6`) can use the separate
739
739
  emission transport budget instead of the `R2` return guard.
740
- The earlier alternatives are in [decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)
741
- ([#267](https://github.com/zackbart/connecta/issues/267),
742
- [#270](https://github.com/zackbart/connecta/issues/270)).
743
740
 
744
741
  **M1.** `connecta.emit(result)` accepts a rich result from this program's
745
742
  `connecta.call` and forwards its original native content blocks, including
@@ -861,7 +858,7 @@ Programs and direct calls use the same invocation deadline and stages. A call's
861
858
  `timeoutMs` wins, then `calls.defaultTimeoutMs`, then `execute.hostCallTimeoutMs`
862
859
  (default 15,000). A guest may request a longer individual call, but the sandbox
863
860
  and request ceilings still bound the whole run. Timeout details are agent-facing;
864
- INV-6 operator records retain only checked classifications and numeric facts.
861
+ operator records retain only checked classifications and numeric facts.
865
862
  A dispatched write timeout becomes `write_outcome_unknown` and retains its
866
863
  deadline details even when the program catches the failure. The final program
867
864
  error includes the first timeout in `details`; multiple write timeouts also
@@ -882,17 +879,17 @@ deadline past the ceiling means raising the ceiling too.
882
879
  **L4.** Per-execution bounds that are contract, identical in both executors
883
880
  because connecta enforces them above the sandbox:
884
881
 
885
- | Bound | Value |
886
- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
887
- | Host calls per execution, shared by `search`, `describe`, `call`, and `read` | 20 by default, `execute.maxHostCalls` |
888
- | Trusted-pool writes per run, on top of the host calls they also spend (`W10`) | 10 by default, `execute.maxWrites` |
889
- | Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
890
- | Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
891
- | `describe` addresses | ≤ 100 |
892
- | `describe` nearby suggestions | ≤ 3 canonical addresses per failed entry |
893
- | Caller text echoed by `describe` recovery | ≤ 512 UTF-8 bytes per field, plus `…` |
894
- | Result | 24,000 serialized characters |
895
- | Logs presented to the model | 4,000 characters |
882
+ | Bound | Value |
883
+ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
884
+ | Host calls per execution, shared by `search`, `describe`, `call`, and `read` | 20 by default, `execute.maxHostCalls` |
885
+ | Writes per run on a writable endpoint, on top of the host calls they also spend (`W10`) | 10 by default, `execute.maxWrites` |
886
+ | Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
887
+ | Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
888
+ | `describe` addresses | ≤ 100 |
889
+ | `describe` nearby suggestions | ≤ 3 canonical addresses per failed entry |
890
+ | Caller text echoed by `describe` recovery | ≤ 512 UTF-8 bytes per field, plus `…` |
891
+ | Result | 24,000 serialized characters |
892
+ | Logs presented to the model | 4,000 characters |
896
893
 
897
894
  Every `call`, `read`, `search`, and `describe` spends one host call on entry, before
898
895
  resolution, validation, or dispatch. Catching a local refusal refunds nothing.
@@ -901,7 +898,7 @@ The first call beyond the budget ends the host run with one non-retryable
901
898
  `try/catch` cannot swallow it. Later host access, including `emit`, stops; pending
902
899
  replies are withheld. The refusal reaches no connector, returns no partial result,
903
900
  and discards accepted emits (`M4`). Calls still resolving or awaiting admission
904
- are cancelled, including trusted-pool writes past their gate. Dispatched trusted-pool writes
901
+ are cancelled, including writes past their gate. Dispatched writes
905
902
  drain under `W9`; unknown outcomes retain precedence. The lease then releases:
906
903
  QuickJS terminates its child, and the Worker adapter disposes RPC and loader
907
904
  handles independently of the guest deadline. Workers guest computation may
@@ -1003,22 +1000,22 @@ payload-free _by construction_ means the event has nowhere to put a payload.
1003
1000
  distinctive output is the program source — exactly what a payload-free history
1004
1001
  must never keep.
1005
1002
 
1006
- **V5.** A trusted-pool write a program issues after it has already returned is not
1003
+ **V5.** A write a program issues after it has already returned is not
1007
1004
  sent and records nothing: it was never an attempt (`W9`).
1008
1005
 
1009
1006
  ## Writes
1010
1007
 
1011
- Pool trust belongs to deployment code. The default `/mcp` endpoint uses
1012
- `trust: "read-only"`; each named pool has its own `trust`, also defaulting to
1013
- `"read-only"`. A trusted default endpoint does not make named pools trusted.
1014
- The default preserves refusal of ordinary program writes. A deployment must opt into program writes explicitly.
1008
+ Whether an endpoint writes belongs to deployment code. Every deployment sets
1009
+ `readOnly` for `/mcp` explicitly; construction fails without it. Each named pool inherits
1010
+ the deployment's `readOnly` unless it sets its own, in either direction. The
1011
+ client owns approval; Connecta answers one question per endpoint: can it write.
1015
1012
 
1016
1013
  ```ts
1017
1014
  createConnecta({
1018
1015
  connectors, executor,
1019
- trust: "read-only",
1016
+ readOnly: false, // required: false writes, true refuses every write call
1020
1017
  pools: {
1021
- automation: { tools: ["posthog"], trust: "trusted", grant },
1018
+ reporting: { tools: ["posthog"], readOnly: true, members: analysts },
1022
1019
  },
1023
1020
  classification: {
1024
1021
  posthog: { exec: "read" }, // only for a deployment with a read-only exec contract
@@ -1027,6 +1024,9 @@ createConnecta({
1027
1024
  });
1028
1025
  ```
1029
1026
 
1027
+ The removed `trust` option, on the deployment or a pool, fails construction
1028
+ with a message naming `readOnly`.
1029
+
1030
1030
  The registry computes one `classifyTool` verdict on each request-scoped
1031
1031
  published catalog entry. It caches and persists only raw downstream facts.
1032
1032
  Every caller consumes that stored verdict. Config cannot classify an individual
@@ -1044,7 +1044,7 @@ fail construction; unknown remote tool keys fail publication of the whole
1044
1044
  catalog. Overrides are exact tool names, including names containing dots.
1045
1045
  They are never inferred from descriptions or arguments.
1046
1046
 
1047
- **W9.** Programs in trusted pools may write. A dispatched write is awaited even
1047
+ **W9.** Programs on a writable endpoint may write. A dispatched write is awaited even
1048
1048
  if the program does not await it, bounded by its host-call deadline. A write
1049
1049
  that reaches accounting after the program settles is not sent. An unanswered
1050
1050
  write makes the run fail `write_outcome_unknown` with
@@ -1054,7 +1054,7 @@ write is automatically retried, including an ambiguous timeout. A downstream
1054
1054
  refusal or a tool's `isError` can establish failure; a timeout or an unanswered
1055
1055
  transport failure leaves the outcome unknown.
1056
1056
 
1057
- **W10.** `execute.maxWrites`, default 10, bounds writes in trusted programs in
1057
+ **W10.** `execute.maxWrites`, default 10, bounds a program's writes in
1058
1058
  addition to the host-call budget. It is checked after argument validation and
1059
1059
  before admission, so an over-budget write spends no permit.
1060
1060
 
@@ -1067,14 +1067,20 @@ reconciliation. Any new call must use the original arguments when its echo is
1067
1067
  partial or absent. A returned `writes` count cannot make a run safe to repeat.
1068
1068
  See [argument echo rules](./meta-tools.md#echo-budgets) for supported schema shapes.
1069
1069
 
1070
- **W12.** `trusted` endpoints annotate `execute_code` as a write and permit
1071
- program writes. `read-only` endpoints annotate it as read-only and refuse
1072
- program writes before validation, with `call_destructive_tool` as the recovery.
1073
- `call_tool` accepts only reads in either tier; `call_destructive_tool` may send
1074
- writes in either tier. Approval belongs to the host. Trust grants no tool or
1075
- connector access and cannot be set by guest code, request arguments, or a
1076
- downstream catalog. `execute.approval`, `Connector.approval`, and per-tool
1077
- approval exemptions are removed. `W1`–`W8` and `W11` remain retired.
1070
+ **W12.** Writable endpoints annotate `execute_code` as a write
1071
+ (`readOnlyHint: false, destructiveHint: true`) and permit program writes and
1072
+ `call_destructive_tool`; `call_tool` still accepts only reads and refuses a write
1073
+ with `destructive_tool_requires_approval`. Read-only endpoints annotate
1074
+ `execute_code` as read-only (`readOnlyHint: true, destructiveHint: false`),
1075
+ omit write tools from `search_tools`, `connecta.search`, and
1076
+ `connecta.describe`, and refuse every write call through `call_tool`,
1077
+ `call_destructive_tool`, and programs with `read_only_endpoint` before
1078
+ validation or dispatch. Guides and skills are documentation and may still name
1079
+ those tools. `tools/list` is the same six tools on both. Approval belongs to the client.
1080
+ `readOnly` grants no tool or connector access and cannot be set by guest code,
1081
+ request arguments, or a downstream catalog. `execute.approval`,
1082
+ `Connector.approval`, per-tool approval exemptions, and pool `trust` are
1083
+ removed. `W1`–`W8` and `W11` remain retired.
1078
1084
 
1079
1085
  ## Executor exceptions
1080
1086
 
@@ -1185,7 +1191,13 @@ brand check and reads only own string data descriptors for diagnostics; guest
1185
1191
  accessors, serialization hooks, and Proxy traps do not run. Other thrown objects
1186
1192
  receive a fixed description.
1187
1193
 
1188
- ## Verification
1194
+ ## Contributor reference
1195
+
1196
+ ### Verification
1197
+
1198
+ The earlier alternatives are in [decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)
1199
+ ([#267](https://github.com/zackbart/connecta/issues/267),
1200
+ [#270](https://github.com/zackbart/connecta/issues/270)).
1189
1201
 
1190
1202
  `test/guest-contract-cases.ts` runs on QuickJS in `test/guest-api-contract-quickjs.node.test.ts`
1191
1203
  and on the Worker adapter with a real Miniflare Loader in `test/guest-api-contract.test.ts`.
@@ -1225,8 +1237,8 @@ rejection, and branded adapter acceptance across module copies.
1225
1237
  | `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) |
1226
1238
  | `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/d1-storage.node.test.ts` (shared stash transfers), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.node.test.ts` (outer reply serialization failure settles the call) |
1227
1239
  | `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, the one-attempt floor), `test/operator-view.test.ts`, `test/sql-storage-contract.ts`, run by `test/d1-storage.node.test.ts` and `test/sqlite-storage.node.test.ts` (historical pause and approval rows still render and round-trip) |
1228
- | `V5`, `W9` | `test/program-writes.test.ts` (an unawaited trusted-pool write finished and recorded, its unknown outcome reported, counts on a failed program, the classification table), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
1229
- | `W10`, `W12` | `test/program-writes.test.ts` (a trusted-pool write runs and every other write keeps `E4`, the write budget, pool trust and override precedence, `call_tool` still refusing, search and describe verdicts, construction refusals), `test/operator-ui-model.test.ts`, `test/browser/operator-ui.spec.ts` (the badge) |
1240
+ | `V5`, `W9` | `test/program-writes.test.ts` (an unawaited program write finished and recorded, its unknown outcome reported, counts on a failed program, the classification table), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
1241
+ | `W10`, `W12` | `test/program-writes.test.ts` (a writable endpoint's program write runs, a read-only endpoint omits writes from search and describe and refuses every write call on every path, including a write its guide names, a required deployment `readOnly`, the write budget, `readOnly` inheritance and overrides, `call_tool` still refusing, search and describe verdicts, construction refusals including retired `trust`), `test/operator-ui-model.test.ts`, `test/browser/operator-ui.spec.ts` (the badge) |
1230
1242
  | `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) |
1231
1243
  | `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) |
1232
1244
  | `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) |
@@ -27,20 +27,25 @@ path and pins the generated project's Connecta dependency to the CLI version:
27
27
  npx @zackbart/connecta init my-connecta
28
28
  cd my-connecta
29
29
  npm install
30
- npm run --silent provision-token -- "local-machine"
30
+ npm run --silent rotate-token -- local-machine
31
31
  # Save the returned cta_ token privately. It is shown only once.
32
32
  npm start
33
33
  ```
34
34
 
35
- The client connects to `http://localhost:8787/mcp` with
36
- `Authorization: Bearer <returned-cta-token>`. The generated project always
37
- installs `accessTokens(storage)`. An empty database admits no MCP caller even
38
- though `/health` works. `CONNECTA_TOKEN` is only a client/doctor variable;
35
+ The agent connects to its pool's agent endpoint,
36
+ `http://localhost:8787/agent/local-machine`, with
37
+ `Authorization: Bearer <returned-cta-token>`. People use `/mcp` and
38
+ `/mcp/<pool>`, which refuse `cta_` tokens. Machine clients are the agents
39
+ declared in `agents` in `src/connecta.config.ts`; the generated project declares
40
+ `local-machine` and always installs `accessTokens(storage)`. An agent with no
41
+ issued token admits no MCP caller even though `/health` works. Rotating with the
42
+ same command replaces the token immediately. `CONNECTA_TOKEN` is only a client/doctor variable;
39
43
  it never configures server authentication. The local commands read the process
40
44
  environment and do not load `.env`; Docker Compose does.
41
45
 
42
- Edit `src/connecta.config.ts` for connectors, public URL, auth, identity rules,
43
- and optional modules. `src/index.ts` only constructs and starts the deployment.
46
+ Edit `src/connecta.config.ts` for connectors, public URL, auth, maintainers,
47
+ agents, pools, and optional modules. Lay out each agent's pools by
48
+ [Organizing an agent's pools](./auth.md#organizing-an-agents-pools). `src/index.ts` only constructs and starts the deployment.
44
49
  Follow the [template README](../templates/node/README.md) for storage paths,
45
50
  backups, Docker provisioning, secrets, and optional modules. Run the deployment's
46
51
  `npm run typecheck` after configuration changes.
@@ -56,15 +61,77 @@ must be installed for the Worker executor.
56
61
 
57
62
  Attach Cloudflare Access to the Worker destination. Interactive MCP clients
58
63
  need Managed OAuth, Dynamic Client Registration, and the documented redirect
59
- allowlist. Machines need Access service headers at the edge plus a stored
60
- `cta_` bearer inside Connecta; service identity alone is refused. Keep
64
+ allowlist. Machines are declared agents: they use `/agent/<pool>` with the
65
+ agent's `cta_` bearer as their only credential, once the
66
+ [agent bypass application](#agent-endpoints-behind-cloudflare-access) exists;
67
+ Access service identity alone is refused. Keep
61
68
  `enable_request_signal`, construct Connecta once per isolate, and pass `ctx`
62
69
  through to `fetch`. The example owns those details and
63
70
  [the machine-token procedure](../examples/worker/README.md#machine-tokens).
64
71
 
72
+ ## Agent endpoints behind Cloudflare Access
73
+
74
+ Agents use `/agent/<pool>`, which connecta authenticates with the `cta_` token
75
+ alone and which ignores Access context entirely
76
+ ([agent endpoints](./auth.md#agent-endpoints)). Behind Access, the edge must
77
+ let those requests through without an Access login or service token. The P0
78
+ probe ([observations](./architecture.md#production-access-bypass-for-agent-endpoints))
79
+ verified this shape on 2026-10-15:
80
+
81
+ 1. Keep the Worker-level application, whose destination is
82
+ `{ "type": "worker", "worker_id": "<the Worker script tag>" }`, exactly as
83
+ [Cloudflare Access on Workers](./auth.md#cloudflare-access-on-workers)
84
+ describes. It still protects every other path and supplies `ctx.access` to
85
+ human routes.
86
+ 2. For each hostname agents use (the `workers.dev` hostname, each Custom
87
+ Domain, each route host), add one more **self-hosted** Access application.
88
+ In the dashboard, set the domain to that hostname and the path to
89
+ `agent/*`. Give it a single policy whose action is **Bypass** and whose
90
+ include rule is **Everyone**. Through the API:
91
+
92
+ ```json
93
+ {
94
+ "type": "self_hosted",
95
+ "name": "connecta agents",
96
+ "destinations": [{ "type": "public", "uri": "connecta.example.com/agent/*" }],
97
+ "policies": [{ "name": "agents bypass", "decision": "bypass", "include": [{ "everyone": {} }] }]
98
+ }
99
+ ```
100
+
101
+ The more specific hostname-and-path application takes precedence over the
102
+ Worker-level one on matching paths only. Do not use a Worker destination
103
+ for it: a Worker destination belongs to one application, and a second one
104
+ is refused with 409 `destination belongs to another application`. For the
105
+ same reason a second hostname without Access is not an alternative.
106
+
107
+ 3. Point each agent at `https://<host>/agent/<pool>` with
108
+ `Authorization: Bearer cta_…` and drop its `CF-Access-Client-Id` and
109
+ `CF-Access-Client-Secret` headers. The pool must list `"agent:<name>"`.
110
+ 4. Verify after the change has settled. Access changes took 15–19 seconds to
111
+ settle in the probe, and while they propagate the edge can answer with
112
+ transient 500 (`error code: 1104`) or 404 (`error code: 1042` or `1043`)
113
+ responses, and in one round a 403 on a bypassed path after more than 30
114
+ seconds. Do not rely on a fixed wait: repeat these checks until they agree
115
+ for several consecutive rounds. Without credentials, `/agent/<pool>`
116
+ reaches connecta (its 401 carries `WWW-Authenticate: Bearer`, not Access's
117
+ 403 page), while `/mcp` and `/` still get Access's refusal. Then run
118
+ `connecta doctor --pool <pool>` with the agent's token.
119
+
120
+ The bypass makes `/agent/*` reachable from the internet without Access.
121
+ Connecta answers there only an issued token of an agent listed in the pool,
122
+ and gives every other spelling of the agent namespace a 404 before auth,
123
+ because Access matches `/agent/*` after resolving dot segments, folding case,
124
+ decoding unreserved escapes, and collapsing a leading `//`, while the Worker
125
+ sees the raw path. Two cases were not tested: a human's `CF_Authorization`
126
+ cookie on a bypassed path, whose `ctx.access` identity is unknown (connecta
127
+ never reads it at `/agent/*`), and Custom Domains, which use the same
128
+ documented hostname-and-path form but were not observed; only `workers.dev`
129
+ was probed. Pinning the Worker-level application's AUD on human routes is
130
+ recommended hardening that connecta does not yet do.
131
+
65
132
  ## Compose capabilities and access
66
133
 
67
- Both configurations use `defineConfig((env) => …)` and a required executor.
134
+ Both configurations use `defineConfig((env) => …)`, a required executor, and a required `readOnly` choice.
68
135
  [Integrating services](./integrating.md) explains the `connectors` array.
69
136
  Configuration selects optional factories through explicit slots:
70
137
 
@@ -90,51 +157,86 @@ outside the database and source control, and preserve it across upgrades.
90
157
  existing-record migration.
91
158
 
92
159
  One deployment serves one tenant and may admit several people. Node uses Clerk
93
- for human auth; Access is Workers-only. Keep credential administration,
94
- personal connection, token management, and activity access explicit.
95
- [Auth](./auth.md#principals-visibility-and-operators) owns identity and grants;
160
+ for human auth; Access is Workers-only. With inbound auth, `maintainers` is
161
+ required: only they reach plain `/mcp` and the operator UI. Everyone else
162
+ reaches a pool that lists them in `members`: people at `/mcp/<pool>`, agents
163
+ at `/agent/<pool>`. Maintainers alone administer shared credentials, manage
164
+ agent tokens, and read activity; members connect their own accounts on
165
+ personal connectors in their pools.
166
+ [Auth](./auth.md#principals-visibility-and-operators) owns maintainers and members;
96
167
  [Clerk setup](./auth.md#clerk-oauth-tokens-and-operator-sessions) owns resource-bound
97
168
  tokens and `aud_claim_enabled`. A person's operator session is not an MCP token.
98
169
 
99
- Root and named pools default to `read-only`. A `trusted` pool allows writes
100
- inside programs and marks `execute_code` as a write. Host approval remains the
101
- host's decision. Pool membership intersects the caller's grants; it never
102
- widens them. See [pools](./auth.md#pools) before changing trust.
170
+ Every deployment must set `readOnly` in its configuration; startup fails without
171
+ it, and the template and Worker example set `readOnly: false`. Each named pool
172
+ inherits that value unless it sets its own. A writable endpoint (`false`) allows
173
+ writes through `call_destructive_tool` and programs and marks `execute_code` as
174
+ a write; the client decides what to approve. A read-only endpoint (`true`) omits
175
+ write tools from search and describe and refuses every write call; provider
176
+ guides may still name those tools. A pool serves exactly its `tools` to its
177
+ `members`. See [pools](./auth.md#pools) before changing `readOnly`.
103
178
 
104
179
  ## Verify and diagnose
105
180
 
106
- With the deployment running, replace the placeholder with its saved client
107
- token and run doctor:
181
+ With the deployment running, replace the placeholder with its saved agent
182
+ token and run doctor against a pool that lists the agent:
183
+
184
+ ```sh
185
+ CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url http://localhost:8787 --pool local-machine
186
+ ```
187
+
188
+ Doctor checks health, the required executor, and the current six-tool set. A
189
+ `cta_` token is checked at `/agent/<pool>` and needs `--pool`.
190
+ For a Worker protected by Access, `/health` still needs edge admission even
191
+ though `/agent/*` bypasses Access. Use an Access service token admitted by the
192
+ Worker-level policy for that check, alongside the agent token:
108
193
 
109
194
  ```sh
110
- CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url http://localhost:8787
195
+ CF_ACCESS_CLIENT_ID='<access-service-id>' CF_ACCESS_CLIENT_SECRET='<access-service-secret>' CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url https://connecta.example.com --pool assistant
111
196
  ```
112
197
 
113
- Doctor checks health, the required executor, and the current six-tool set.
114
- For a Worker protected by Access, follow the example's doctor credentials
115
- and service-header setup. If the operator UI is enabled, authenticated
198
+ The service headers admit the health probe at the edge; only the `cta_` token
199
+ authenticates the agent endpoint inside Connecta. If the operator UI is enabled, maintainer-authenticated
116
200
  `connecta doctor --config --url <public-url>` prints the caller-scoped
117
201
  configuration snapshot as JSON. It runs no diagnostic program and prints no
118
202
  raw HTTP failure body. [Operator UI](./operator-ui.md) describes the returned
119
- configuration, live facts, and permission rules.
203
+ configuration, live facts, and permission rules. The config route is a human
204
+ endpoint: an agent token cannot authenticate it.
120
205
 
121
206
  Health success alone does not prove a client can authenticate or reach an
122
207
  integration. Check client connection and a bounded read through a chosen
123
208
  connector; resolve credentials using [authorization recovery](./meta-tools.md#authorization-recovery).
124
209
 
210
+ Use the failed check to choose the next step:
211
+
212
+ | Symptom | Action |
213
+ | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
214
+ | Connection refused or timeout | Check the public URL, listening process/port, proxy, and Worker deployment. |
215
+ | `/health` fails behind Access | Check the Worker-level Access policy and the two service headers above. Do not broaden the agent bypass. |
216
+ | Executor check fails | Retain `quickJsExecutor()` and its peer on Node, or `workerExecutor({ loader })`, the codemode peer, and `LOADER` on Workers. |
217
+ | MCP 401 | Check the saved token, declared agent, and slot state; rotated/revoked tokens refuse. Use `/agent/<pool>` with `--pool`. |
218
+ | MCP 404 | Check the pool name and `members: ["agent:assistant"]`; an existing but inaccessible pool is deliberately indistinguishable from a missing one. |
219
+ | Doctor passes but a connector fails | Discover its tools in that pool, check credential status/account ownership, and perform a bounded read. Doctor does not prove downstream access. |
220
+
221
+ After a fix, repeat doctor with the same client credentials and pool. Never
222
+ paste a token or raw downstream failure body into a committed diagnostic log.
223
+
125
224
  ## Upgrade an existing deployment
126
225
 
127
226
  Bump the exact package pin, read the [changelog](../CHANGELOG.md) for every
128
227
  intervening release, and apply the relevant migration before startup. `init`
129
228
  does not merge changes into an existing deployment.
130
229
 
131
- - [Managed tokens from v0.23](./auth.md#managed-client-tokens-and-upgrading-from-v023) keep their secrets when storage, namespaces, and grants are retained. Replace the old boolean config with `accessTokens(storage)`; machine tokens gain no operator authority.
230
+ - [Migration to 0.33](./migration-0.33.md) maps every removed access option to `maintainers`, pool `members`, and `agents`, moves agents to `/agent/<pool>`, and covers pool owners, token revisions, operator contract v2 with its operator route changes, and the pre-auth listen fix.
231
+ - [Tokens issued before agents](./auth.md#agent-tokens-and-upgrading-from-earlier-tokens) stop admitting until each machine's token is bound once to a declared agent with `npm run adopt-token -- <token-id> <agent>` (Node) or `AccessTokenManager.adopt`, which keeps its secret. Human-bound tokens stay refused; issue those agents new tokens. Pool members name `"agent:<name>"` instead of matching token record ids.
132
232
  - [Node storage from 0.28](../templates/node/README.md#storage) uses `connecta migrate-state` to copy the JSON state into SQLite. Activity JSONL is not imported.
133
233
  - [Worker storage from 0.28](../examples/worker/README.md#upgrading-from-028) copies KV into D1 with a one-shot migration.
134
234
  - [Notion, Vercel, and Cloudflare for 0.29](./provider-migration-0.29.md) change defaults and retained API tool names.
135
235
  - [Executor wiring](./code-mode.md#deploy-time-capability) requires branded shipped executors or a self-managed custom executor.
136
236
 
137
- ## Implementation and evidence
237
+ ## Contributor reference
238
+
239
+ ### Implementation and evidence
138
240
 
139
241
  Check the [CLI](https://github.com/zackbart/connecta/blob/main/bin/connecta.mjs),
140
242
  [Node configuration](../templates/node/src/connecta.config.ts), and