@zackbart/connecta 0.33.0 → 0.34.1
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 +65 -0
- package/README.md +100 -128
- package/SECURITY.md +1 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +3 -0
- package/dist/branding.d.ts +7 -0
- package/dist/branding.js +76 -9
- package/dist/catalog-drift.d.ts +30 -26
- package/dist/catalog-drift.js +169 -84
- package/dist/catalog-service.js +16 -2
- package/dist/config-value-sources.js +6 -1
- package/dist/config.d.ts +13 -2
- package/dist/config.js +36 -2
- package/dist/connector-caller.js +2 -1
- package/dist/connectors/api-connector.d.ts +3 -0
- package/dist/connectors/api-connector.js +5 -1
- package/dist/connectors/option-shapes.js +1 -1
- package/dist/connectors/remote-mcp.d.ts +2 -0
- package/dist/connectors/remote-mcp.js +5 -1
- package/dist/describe-config.d.ts +5 -1
- package/dist/describe-config.js +2 -0
- package/dist/execute.js +1 -11
- package/dist/fix-prompt.js +2 -2
- package/dist/index.js +1 -0
- package/dist/invocation.js +3 -2
- package/dist/membership.d.ts +2 -0
- package/dist/meta-tools.js +3 -2
- package/dist/oauth-callback-outcome.js +2 -2
- package/dist/operator-ui/agents.d.ts +1 -1
- package/dist/operator-ui/agents.js +16 -16
- package/dist/operator-ui/config-summary.js +565 -0
- package/dist/operator-ui/connection-map.js +376 -0
- package/dist/operator-ui/endpoints.js +45 -16
- package/dist/operator-ui/fix-prompts.js +1 -1
- package/dist/operator-ui/generated.js +4 -4
- package/dist/operator-ui/health.js +52 -0
- package/dist/operator-ui/member-page.js +13 -28
- package/dist/operator-ui/overview.js +123 -0
- package/dist/operator-ui/view.d.ts +26 -22
- package/dist/operator-ui/view.js +130 -96
- package/dist/page-styles.js +5 -3
- package/dist/provider.d.ts +6 -1
- package/dist/provider.js +18 -1
- package/dist/providers/_shared/google/workspace.d.ts +9 -4
- package/dist/providers/_shared/google/workspace.js +7 -3
- package/dist/providers/basecamp/index.d.ts +2 -0
- package/dist/providers/breeze/index.d.ts +2 -0
- package/dist/providers/ccb/index.d.ts +2 -0
- package/dist/providers/ccb/index.js +2 -2
- package/dist/providers/cloudflare/index.d.ts +2 -0
- package/dist/providers/cloudflare/index.js +5 -1
- package/dist/providers/cloudflare/openapi.generated.js +3296 -3206
- package/dist/providers/cloudflare/rest.js +19 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -2
- package/dist/providers/cloudflare/value-safety.js +51 -18
- package/dist/providers/docs/index.js +1 -1
- package/dist/providers/docs/skill.generated.js +2 -2
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/drive/skill.generated.js +2 -2
- package/dist/providers/forms/index.js +1 -1
- package/dist/providers/forms/skill.generated.js +2 -2
- package/dist/providers/gmail/index.js +1 -1
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/intercom/index.d.ts +24 -0
- package/dist/providers/intercom/index.js +121 -0
- package/dist/providers/intercom/skill.generated.js +8 -0
- package/dist/providers/linear/index.js +204 -19
- package/dist/providers/linear/skill.generated.js +2 -2
- package/dist/providers/mixpanel/index.d.ts +2 -0
- package/dist/providers/mixpanel/index.js +21 -17
- package/dist/providers/mixpanel/skill.generated.js +3 -3
- package/dist/providers/notion/index.d.ts +2 -0
- package/dist/providers/notion/index.js +1 -1
- package/dist/providers/notion/skill.generated.js +1 -1
- package/dist/providers/overflow/index.d.ts +2 -0
- package/dist/providers/overflow/index.js +1 -1
- package/dist/providers/overflow/skill.generated.js +2 -2
- package/dist/providers/planning-center/index.d.ts +2 -0
- package/dist/providers/planning-center/index.js +1 -1
- package/dist/providers/planning-center/skill.generated.js +4 -4
- package/dist/providers/revenuecat/index.d.ts +5 -3
- package/dist/providers/revenuecat/index.js +147 -7
- package/dist/providers/revenuecat/skill.generated.js +3 -3
- package/dist/providers/sheets/index.js +1 -1
- package/dist/providers/sheets/skill.generated.js +2 -2
- package/dist/providers/slides/index.js +1 -1
- package/dist/providers/slides/skill.generated.js +2 -2
- package/dist/providers/stripe/index.d.ts +2 -0
- package/dist/providers/stripe/index.js +50 -30
- package/dist/providers/stripe/skill.generated.js +2 -1
- package/dist/providers/tithely/index.d.ts +2 -0
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/index.d.ts +2 -0
- package/dist/providers/vercel/index.js +2 -2
- package/dist/providers/vercel/openapi.generated.js +391 -374
- package/dist/providers/vercel/skill.generated.js +2 -2
- package/dist/providers/vercel/value-safety.js +14 -4
- package/dist/registry.d.ts +10 -14
- package/dist/registry.js +77 -62
- package/dist/routes/mcp.js +20 -13
- package/dist/routes/oauth.js +2 -2
- package/dist/routes/ui-data.js +8 -2
- package/dist/skills.js +18 -2
- package/dist/storage/sql.js +5 -1
- package/dist/types.d.ts +36 -11
- package/dist/ui.js +9 -4
- package/dist/usage-guide.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/README.md +2 -2
- package/documentation/architecture.md +66 -18
- package/documentation/auth.md +183 -67
- package/documentation/code-mode.md +30 -10
- package/documentation/deploying.md +5 -1
- package/documentation/integrating.md +64 -7
- package/documentation/meta-tools.md +38 -24
- package/documentation/migration-0.33.md +21 -13
- package/documentation/migration-0.34.md +67 -0
- package/documentation/operating.md +1 -1
- package/documentation/operator-tests.md +52 -20
- package/documentation/operator-ui.md +341 -104
- package/documentation/provider-auth-migration.md +22 -0
- package/examples/worker/AGENTS.md +15 -9
- package/examples/worker/README.md +41 -14
- package/examples/worker/src/connecta.config.ts +5 -4
- package/examples/worker/wrangler.jsonc +1 -1
- package/package.json +23 -19
- package/templates/node/AGENTS.md +13 -7
- package/templates/node/README.md +14 -11
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +2 -2
package/documentation/auth.md
CHANGED
|
@@ -66,7 +66,8 @@ comma-separated list of these ids; update that environment variable when
|
|
|
66
66
|
using their roster wiring. Pool membership remains code. To remove a person,
|
|
67
67
|
remove their memberships and maintainer entry, restart/redeploy, and remove
|
|
68
68
|
their sign-in admission as appropriate. Never add another person to an
|
|
69
|
-
agent's `<agent>-<user>` pool
|
|
69
|
+
agent's `<agent>-<user>` pool, and add a person to an agent's tier only if
|
|
70
|
+
they are cleared for it; follow [the pool layout](#organizing-an-agents-pools).
|
|
70
71
|
|
|
71
72
|
## Connect a shared credential
|
|
72
73
|
|
|
@@ -108,8 +109,8 @@ remoteMcp("crm", {
|
|
|
108
109
|
|
|
109
110
|
To rotate a saved token, save its replacement in the same slot; subsequent
|
|
110
111
|
calls use it. Revoke the retired token at the downstream service. For OAuth,
|
|
111
|
-
use
|
|
112
|
-
Disconnect to invalidate the local grant and catalog. To remove a credential,
|
|
112
|
+
use Reconnect (or Connect account) to authorize afresh, Continue requested
|
|
113
|
+
authorization to resume a pending consent link, and Disconnect to invalidate the local grant and catalog. To remove a credential,
|
|
113
114
|
use the connector's credential delete control. Clearing a slot with no saved
|
|
114
115
|
value returns 409 with `problem: "credential_required"`; it does not revoke
|
|
115
116
|
anything downstream. An unreadable stored value can still be cleared for
|
|
@@ -132,7 +133,7 @@ pools: { assistant: { tools: ["crm", "tickets"], members: ["agent:assistant"] }
|
|
|
132
133
|
Typecheck and restart/redeploy, then use either procedure:
|
|
133
134
|
|
|
134
135
|
- **Signed-in maintainer:** with human auth and `operatorUi()` enabled, open
|
|
135
|
-
the
|
|
136
|
+
the Tokens page, select `assistant`, and issue, rotate, or revoke
|
|
136
137
|
its token. Save an issued secret privately immediately; it appears once.
|
|
137
138
|
The UI reads the slot revision and sends it with the operation. If the slot
|
|
138
139
|
changed, reload and decide again.
|
|
@@ -233,7 +234,7 @@ lists them: each name matches `^[a-z0-9_-]+$`, at most 64 characters, and is
|
|
|
233
234
|
unique; anything else, or agents without `accessTokens`, throws at
|
|
234
235
|
construction. `describeConfig()` lists the names and never token state.
|
|
235
236
|
`accessTokens` from `@zackbart/connecta/auth/access-tokens` installs the `cta_`
|
|
236
|
-
inbound adapter and, beside `ui`, the
|
|
237
|
+
inbound adapter and, beside `ui`, the Tokens page and `/ui/access-tokens`
|
|
237
238
|
routes. Omitted, none of it loads or is served. It takes no options;
|
|
238
239
|
`accessTokens(storage, { maxActive })` throws, since each declared agent has
|
|
239
240
|
exactly one token slot.
|
|
@@ -281,7 +282,8 @@ The token routes require the revision the caller decided from:
|
|
|
281
282
|
| `POST /ui/access-tokens/<agent>` (issue, rotate) | JSON body `{ "expect": "<revision>" }`, `Content-Type: application/json` |
|
|
282
283
|
| `DELETE /ui/access-tokens/<agent>` (revoke) | the same body, or `?expect=<revision>` for a client that cannot send a body with DELETE; not both |
|
|
283
284
|
|
|
284
|
-
A missing or malformed `expect` is 400
|
|
285
|
+
A missing or malformed `expect` is 400, as is a JSON body that does not parse
|
|
286
|
+
to an object; a body without `Content-Type: application/json` is 415, and one
|
|
285
287
|
over 1,024 characters is 413. They are checked after authorization and before
|
|
286
288
|
the operation. If the slot's current revision differs, the route answers 409
|
|
287
289
|
`{ "error": "…", "code": "slot_changed" }` before minting a secret or writing
|
|
@@ -387,8 +389,9 @@ outcome depends on cleanup or on undoing a write.
|
|
|
387
389
|
undelivered rotation secret may keep admitting, but it never left the server:
|
|
388
390
|
a liveness problem reported as `outcome_unknown`, never a credential exposure.
|
|
389
391
|
|
|
390
|
-
The routes pass the request's `AbortSignal` to `rotate
|
|
391
|
-
(`{ signal }`);
|
|
392
|
+
The routes pass the request's `AbortSignal` to `rotate` and `revoke`
|
|
393
|
+
(`{ signal, expect }`); `adopt` has no route and runs only from trusted
|
|
394
|
+
provisioning, which may pass its own `{ signal }`; a signal already aborted performs no storage call at all. Authorization caches no token, and D1 and SQLite are strongly
|
|
392
395
|
consistent; admitted requests are not recalled. Management writes require an
|
|
393
396
|
exact same-origin Origin; responses are private and non-cacheable.
|
|
394
397
|
|
|
@@ -463,8 +466,11 @@ one fixed address, is applied once per call and only to a request an inbound
|
|
|
463
466
|
provider authenticated: any `ok`, a subject-less custom credential included, never an open
|
|
464
467
|
deployment's anonymous admission. An agent never reaches the function; it acts
|
|
465
468
|
as its pool's `owner` ([Pool owners](#pool-owners-for-google-workspace)).
|
|
466
|
-
|
|
467
|
-
|
|
469
|
+
Without an owner, a mapping-subject connector is absent for agents, and a call
|
|
470
|
+
by address fails `unknown_address`. The provider-level fallback is
|
|
471
|
+
`auth_required` when no inbound provider authenticated the caller or the
|
|
472
|
+
mapping returns `undefined`; it also rejects an ownerless agent context that
|
|
473
|
+
bypasses the scoped registry. These refusals happen before any request leaves. Arguments, headers, and programs
|
|
468
474
|
never reach it; a deployment's own connectors still cannot read the caller.
|
|
469
475
|
|
|
470
476
|
Access is read from one place, deployment code. Top-level
|
|
@@ -496,10 +502,11 @@ provider other than the built-in agent-token adapter), and a non-empty
|
|
|
496
502
|
can be a maintainer, and plain `/mcp` and every operator API admit nobody; the
|
|
497
503
|
agents reach only `/agent/<pool>` for the pools that list them. With no inbound auth at all, the
|
|
498
504
|
deployment is open (see [pools](#pools)). `describeConfig()` lists
|
|
499
|
-
`maintainers` as `{ namespace, id }` and each pool's resolved `members
|
|
505
|
+
`maintainers` as `{ namespace, id }` and each pool's resolved `members` and
|
|
506
|
+
trimmed `description` (`null` when unset).
|
|
500
507
|
|
|
501
508
|
Pool tools are enforced in the scoped registry view, below the catalog service,
|
|
502
|
-
so `search_tools`,
|
|
509
|
+
so `search_tools`, both call tools, a program's
|
|
503
510
|
`connecta.search`, `connecta.describe`, and `connecta.call` read one filtered
|
|
504
511
|
list. Connector-level discovery, guides, and `authorize_connector` keep a
|
|
505
512
|
connector when any of its tools is in the pool — `docs.read` permits the
|
|
@@ -540,6 +547,10 @@ Marketing's every `campaigns` tool and only `issues.search`. Neither pool
|
|
|
540
547
|
serves `billing`; only the owner's plain `/mcp` does. A person in both pools
|
|
541
548
|
uses two endpoints, never a union.
|
|
542
549
|
|
|
550
|
+
A team pool can also be one of an agent's tiers: the same pool lists the
|
|
551
|
+
cleared people and the agent, and each reaches it at their own address
|
|
552
|
+
([people in a tier](#organizing-an-agents-pools)).
|
|
553
|
+
|
|
543
554
|
| Catalog change | Exact `connector.tool` in a pool |
|
|
544
555
|
| ----------------------- | ----------------------------------------------------------------------- |
|
|
545
556
|
| New name | Excluded until reviewed and added |
|
|
@@ -572,7 +583,7 @@ secrets. Declare each machine as an agent instead:
|
|
|
572
583
|
|
|
573
584
|
1. Configure the existing D1 or SQLite store as `storage`, and install
|
|
574
585
|
`accessTokens(storage)`. Keep its existing keys and namespaces.
|
|
575
|
-
2. Add the machine's name to `agents`. Issue its token from the
|
|
586
|
+
2. Add the machine's name to `agents`. Issue its token from the Tokens
|
|
576
587
|
page as a signed-in maintainer, or from trusted
|
|
577
588
|
deployment code with
|
|
578
589
|
`new AccessTokenManager(storage, { agents }).rotate("machine-name", "deployment-provisioning")`.
|
|
@@ -609,7 +620,11 @@ createConnecta({
|
|
|
609
620
|
maintainers: ["user_owner"],
|
|
610
621
|
agents: ["calendar-bot"],
|
|
611
622
|
pools: {
|
|
612
|
-
support: {
|
|
623
|
+
support: {
|
|
624
|
+
tools: ["linear", "notion.search_pages", "notion.fetch_page"],
|
|
625
|
+
members: supportTeam,
|
|
626
|
+
description: "Support tools: Linear and three Notion reads. Ask a maintainer for anything else.",
|
|
627
|
+
},
|
|
613
628
|
calendar_bot: { tools: ["calendar.create_event"], members: ["agent:calendar-bot"] },
|
|
614
629
|
reporting: { tools: ["linear", "notion"], readOnly: true, members: analysts },
|
|
615
630
|
},
|
|
@@ -623,14 +638,20 @@ warning says, every pool path answers the 404 because nobody can be a member,
|
|
|
623
638
|
and every agent endpoint answers the machine-only 401 because no agent can be
|
|
624
639
|
admitted.
|
|
625
640
|
|
|
626
|
-
| Rule | Contract
|
|
627
|
-
| ------------ |
|
|
628
|
-
| Scope | `/mcp/<pool>` and `/agent/<pool>` serve exactly the pool's `tools` (connector ids or exact `connector.tool` addresses), with the pool's `readOnly`, to its member people and member agents respectively. Plain `/mcp` serves the whole catalog to maintainers. Neither intersects with or widens anything.
|
|
629
|
-
| Refusal | A non-maintainer at `/mcp`, a non-member, and an undeclared pool get an identical 404 status, body, and headers, after auth. The operator log records `not_maintainer`, `not_pool_member`, or `pool_not_declared`. Names are not anonymously enumerable; valid credentials cannot distinguish these cases by content.
|
|
630
|
-
| Agents | An agent member reaches its pools only at `/agent/<pool>`, with its `cta_` token. Human endpoints refuse `cta_`; see [agent endpoints](#agent-endpoints).
|
|
631
|
-
|
|
|
632
|
-
|
|
|
633
|
-
|
|
|
641
|
+
| Rule | Contract |
|
|
642
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
643
|
+
| Scope | `/mcp/<pool>` and `/agent/<pool>` serve exactly the pool's `tools` (connector ids or exact `connector.tool` addresses), with the pool's `readOnly`, to its member people and member agents respectively. Plain `/mcp` serves the whole catalog to maintainers. Neither intersects with or widens anything. |
|
|
644
|
+
| Refusal | A non-maintainer at `/mcp`, a non-member, and an undeclared pool get an identical 404 status, body, and headers, after auth. The operator log records `not_maintainer`, `not_pool_member`, or `pool_not_declared`. Names are not anonymously enumerable; valid credentials cannot distinguish these cases by content. |
|
|
645
|
+
| Agents | An agent member reaches its pools only at `/agent/<pool>`, with its `cta_` token. Human endpoints refuse `cta_`; see [agent endpoints](#agent-endpoints). |
|
|
646
|
+
| Description | Optional `description`: plain text telling the model what this endpoint is for. `/mcp/<pool>` and `/agent/<pool>` append it to the MCP `initialize` instructions after `This endpoint: `; plain `/mcp` and a pool without one serve the shared instructions unchanged. It grants, hides, and refuses nothing, and changes no tool or tool description. |
|
|
647
|
+
| Construction | Malformed names, unknown options or connectors, empty pools, empty or missing `members`, unresolvable member references, a non-boolean `readOnly`, an `owner` that is not a Workspace email address, a `description` that is not 1 to 500 characters of one-line text after trimming, a retired `trust` or `grant`, and addresses absent from an `api()` static catalog throw. Remote catalogs are checked at lazy load. |
|
|
648
|
+
| Discovery | Clerk's pool 401 names `/.well-known/oauth-protected-resource/mcp/<pool>` with the pool URL as `resource`, matching RFC 9728. Cloudflare Managed OAuth is application-level and needs no pool setup. |
|
|
649
|
+
| Drift | No wildcards; pool tools are exact names. Missing live tools are unreachable and warned once while in a bounded 1,024-entry FIFO. |
|
|
650
|
+
|
|
651
|
+
Pool descriptions are trimmed before validation. The remaining text must be
|
|
652
|
+
1 to 500 characters and contain no C0 or C1 control characters (including tabs,
|
|
653
|
+
newlines, and NEL U+0085), LINE SEPARATOR (U+2028), or PARAGRAPH SEPARATOR
|
|
654
|
+
(U+2029).
|
|
634
655
|
|
|
635
656
|
### Agent endpoints
|
|
636
657
|
|
|
@@ -715,7 +736,16 @@ mapping function, the caller decides whose account a call opens:
|
|
|
715
736
|
| ----------------------------- | ------------------------------------------------------------------------------- |
|
|
716
737
|
| A person, in any pool | Their own account, through the `subject` mapping. A pool `owner` never applies. |
|
|
717
738
|
| An agent in a pool | The pool's `owner`, resolved before the mapping, which never sees the agent. |
|
|
718
|
-
| An agent, pool has no `owner` | Nothing
|
|
739
|
+
| An agent, pool has no `owner` | Nothing. The connector is absent for the agent, as a personal connector is. |
|
|
740
|
+
|
|
741
|
+
In a pool without an `owner`, a mapping-subject Workspace connector is absent
|
|
742
|
+
for agents, just as an `authScope: "personal"` connector is: `search_tools`,
|
|
743
|
+
`connecta.search`, `connecta.describe`, `skills`, and connector discovery omit
|
|
744
|
+
it, `authorize_connector` answers "Unknown connector", and a call by address
|
|
745
|
+
fails `unknown_address`, the same as for a connector that does not exist.
|
|
746
|
+
People in the same pool still see it and act as themselves. This is what lets
|
|
747
|
+
one pool be a team pool for people, each acting as themselves on Workspace, and
|
|
748
|
+
also list an agent that reaches only the pool's shared connectors.
|
|
719
749
|
|
|
720
750
|
`owner` is a Workspace email address, checked at construction and listed in
|
|
721
751
|
`describeConfig()` (`null` when unset). Over HTTP, `/ui/api/config` serves it
|
|
@@ -728,19 +758,19 @@ change it, and the `auth_required` message never names an address. One
|
|
|
728
758
|
|
|
729
759
|
```ts
|
|
730
760
|
const team = [
|
|
731
|
-
{
|
|
732
|
-
{
|
|
761
|
+
{ handle: "alice", id: "user_alice", email: "alice@example.com" },
|
|
762
|
+
{ handle: "bob", id: "user_bob", email: "bob@example.com" },
|
|
733
763
|
];
|
|
734
764
|
createConnecta({
|
|
735
765
|
auth: clerkAuth({ publishableKey, secretKey }),
|
|
736
766
|
storage,
|
|
737
767
|
accessTokens: accessTokens(storage),
|
|
738
768
|
readOnly: false,
|
|
739
|
-
maintainers: ["
|
|
769
|
+
maintainers: ["user_alice"],
|
|
740
770
|
agents: ["assistant"],
|
|
741
771
|
pools: Object.fromEntries(
|
|
742
772
|
team.map((person) => [
|
|
743
|
-
`assistant-${person.
|
|
773
|
+
`assistant-${person.handle}`,
|
|
744
774
|
{ tools: ["gmail", "drive"], owner: person.email, members: [person.id, "agent:assistant"] },
|
|
745
775
|
]),
|
|
746
776
|
),
|
|
@@ -767,9 +797,12 @@ members must not share that account.
|
|
|
767
797
|
|
|
768
798
|
### Organizing an agent's pools
|
|
769
799
|
|
|
770
|
-
Use this layout for every agent's pools. It gives
|
|
771
|
-
|
|
772
|
-
|
|
800
|
+
Use this layout for every agent's pools. It gives a chat no more than
|
|
801
|
+
everyone in it is cleared for, and gives each person's own accounts to the
|
|
802
|
+
agent only when that person is the one talking, all over one connection. This
|
|
803
|
+
section holds the complete rules.
|
|
804
|
+
|
|
805
|
+
**The pools**
|
|
773
806
|
|
|
774
807
|
1. **One connection per agent.** Each agent has exactly one Connecta
|
|
775
808
|
connection, and its client (the harness, never the model) chooses the
|
|
@@ -777,24 +810,32 @@ connection. This section holds the complete rules.
|
|
|
777
810
|
it: the model would get two sets of meta-tools, and `execute_code` cannot
|
|
778
811
|
join results across connections. The agent's one `cta_` token works on
|
|
779
812
|
every pool that lists it.
|
|
780
|
-
2. **
|
|
781
|
-
|
|
782
|
-
|
|
813
|
+
2. **Tiers.** An agent's tiers are named pools ordered from least to most
|
|
814
|
+
capable. Recommended names are `<agent>` for the lowest tier and
|
|
815
|
+
`<agent>-<tier>` for higher tiers (`assistant-team`, `assistant-leads`).
|
|
816
|
+
A tier may be any named pool, including an existing team pool such as
|
|
817
|
+
`team` or `leadership`, as long as its human members are all cleared for
|
|
818
|
+
it (rule 10). An agent with one tier uses it as the lowest. Tiers never
|
|
819
|
+
hold per-user connectors and never set an `owner`.
|
|
783
820
|
3. **Personal pool `<agent>-<user>`.** `/agent/<agent>-<user>` serves pool
|
|
784
|
-
`<agent>-<user>`: the
|
|
785
|
-
a one-on-one with that user.
|
|
786
|
-
(`alice`), never a provider
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
821
|
+
`<agent>-<user>`: the highest tier that user is cleared for, plus that
|
|
822
|
+
user's own connectors. Use it only in a one-on-one with that user.
|
|
823
|
+
`<user>` is a short, stable handle you choose (`alice`), never a provider
|
|
824
|
+
id, principal id, or address. When using the recommended `<agent>-<tier>`
|
|
825
|
+
names, no user handle may equal a tier suffix: their pool names would
|
|
826
|
+
collide. With any naming scheme, keep personal pool names distinct from
|
|
827
|
+
tier pool names. Pool names match `[a-z0-9_-]+`.
|
|
828
|
+
4. **Nest by spreading tool lists.** Define each tier's `tools` once, as a
|
|
829
|
+
constant that spreads the tier below it, and spread a user's highest tier
|
|
830
|
+
into their personal pool's `tools`. A change to a tier then reaches every
|
|
831
|
+
pool above it. Spread tool lists, not whole pools: `members`, `readOnly`,
|
|
832
|
+
and `owner` belong to each pool, and a spread pool would copy them.
|
|
790
833
|
5. **`owner` is the user's Workspace address.** Set `owner` on
|
|
791
834
|
`<agent>-<user>` to that user's Google Workspace email address; it is
|
|
792
835
|
checked as an address at construction, and it makes Workspace connectors
|
|
793
836
|
act as that user for the agent ([pool owners](#pool-owners-for-google-workspace)).
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
connector listed in a pool without an owner fails `auth_required` for an
|
|
797
|
-
agent.)
|
|
837
|
+
Tiers have no owner, so a Workspace connector with a mapping `subject`
|
|
838
|
+
is absent for the agent there.
|
|
798
839
|
6. **A user's own account is its own connector id.** Agents have no
|
|
799
840
|
principal, so `authScope: "personal"` connectors never appear for them.
|
|
800
841
|
Give each user's account its own connector id with shared auth, which a
|
|
@@ -802,20 +843,68 @@ connection. This section holds the complete rules.
|
|
|
802
843
|
that user's pool. Google Workspace is the exception: one connector whose
|
|
803
844
|
`subject` is a mapping function, with each personal pool's `owner`
|
|
804
845
|
choosing the account.
|
|
805
|
-
7. **
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
846
|
+
7. **Where each connector goes.** A shared connector (the deployment's
|
|
847
|
+
account, including a fixed-subject Workspace connector) goes in the lowest
|
|
848
|
+
tier whose entire audience is authorized to use the connector, and so
|
|
849
|
+
reaches every tier above. For example, billing restricted to leads goes
|
|
850
|
+
in the leads tier. Per-user connectors (`notes_alice`) go only in personal
|
|
851
|
+
pools, and so do the Workspace connectors with a mapping `subject` that
|
|
852
|
+
the agent uses; rule 10 covers listing them in a tier for its people.
|
|
853
|
+
8. **Changes stay in config.** Adding an account for a user adds its
|
|
854
|
+
connector and lists it in that user's pool `tools`. Adding a user adds one
|
|
855
|
+
`<agent>-<user>` pool. Adding a tier adds one pool and one constant, and
|
|
856
|
+
the tier's place in the harness's tier list.
|
|
857
|
+
9. **No other person in a personal pool.** The only human member an
|
|
809
858
|
`<agent>-<user>` pool may have is that same user, and usually it has none.
|
|
810
859
|
Never add another person: its per-user connectors (`notes_alice`) use that
|
|
811
860
|
user's account for everyone the pool admits, so another member would read
|
|
812
861
|
and write the user's account as their own. A person's own use normally
|
|
813
|
-
goes through their own pools or plain `/mcp
|
|
862
|
+
goes through their own pools or plain `/mcp`.
|
|
863
|
+
10. **People in a tier are cleared for it.** A tier can also be a human team
|
|
864
|
+
pool: list the people cleared for that tier in its `members` beside the
|
|
865
|
+
agent. They use it at `/mcp/<pool>`, the agent at `/agent/<pool>`.
|
|
866
|
+
List no one else: a member reaches
|
|
867
|
+
every shared connector the tier holds, on the deployment's account. Such a
|
|
868
|
+
pool may also list Workspace mapping connectors or `authScope: "personal"`
|
|
869
|
+
connectors for its people, who act as themselves on them; those connectors
|
|
870
|
+
are absent for the agent, since a tier has no owner and an agent has no
|
|
871
|
+
principal.
|
|
872
|
+
|
|
873
|
+
**Choosing the pool each turn**
|
|
874
|
+
|
|
875
|
+
The harness decides which pool a turn uses, from its own verified view of
|
|
876
|
+
the chat: the platform's sender and participant ids, never names or claims in
|
|
877
|
+
message text, and never the model. Connecta checks only that the agent is a
|
|
878
|
+
member of the pool it opens. The agent's one token opens every pool that
|
|
879
|
+
lists it, so clearance is enforced nowhere but the harness.
|
|
880
|
+
|
|
881
|
+
- In a one-on-one with a user who has a personal pool, use that pool.
|
|
882
|
+
- Otherwise, use the most capable tier that everyone present is cleared for.
|
|
883
|
+
Everyone present means every participant who can read the reply, people and
|
|
884
|
+
other bots, not only the sender.
|
|
885
|
+
- If anyone present is unknown to the harness, or it cannot verify who is
|
|
886
|
+
present, use the lowest tier.
|
|
887
|
+
- A scheduled run uses the tier of the audience that will read its output, by
|
|
888
|
+
the same rule; with no audience, the lowest tier.
|
|
889
|
+
- Choose again on every turn. Someone joining a chat lowers its tier from the
|
|
890
|
+
next turn.
|
|
891
|
+
|
|
892
|
+
**The lowest tier.** The first pool in the harness's tier list is the
|
|
893
|
+
fallback (recommended name: `<agent>`), so make it safe in front of anyone
|
|
894
|
+
who can reach the agent. Where unknown people can, make it a guest tier:
|
|
895
|
+
`readOnly: true`, with exact `connector.tool` addresses for the reads guests
|
|
896
|
+
may use. A new downstream tool then never appears there, and no write runs.
|
|
897
|
+
The tier above spreads the guest list and adds whole connectors.
|
|
814
898
|
|
|
815
899
|
```ts
|
|
816
900
|
import type { GoogleWorkspaceOptions } from "@zackbart/connecta/providers/gmail";
|
|
817
901
|
|
|
818
|
-
const
|
|
902
|
+
const agent = "agent:assistant";
|
|
903
|
+
// Tiers, lowest first. Each tier's tools spread the tier below.
|
|
904
|
+
const guestTools = ["crm.search_contacts", "tickets.list_tickets"];
|
|
905
|
+
const teamTools = [...guestTools, "crm", "tickets"];
|
|
906
|
+
const leadsTools = [...teamTools, "billing"];
|
|
907
|
+
|
|
819
908
|
const emailOf = (id?: string) => (id === "user_alice" ? "alice@example.com" : undefined);
|
|
820
909
|
const workspace: GoogleWorkspaceOptions = {
|
|
821
910
|
purpose: "Mail and files of the person in a one-on-one",
|
|
@@ -831,21 +920,27 @@ createConnecta({
|
|
|
831
920
|
maintainers: ["user_alice"],
|
|
832
921
|
agents: ["assistant"],
|
|
833
922
|
pools: {
|
|
834
|
-
|
|
923
|
+
// Lowest tier and fallback: reads guests may use.
|
|
924
|
+
assistant: { tools: guestTools, readOnly: true, members: [agent] },
|
|
925
|
+
// Also the team's own pool at /mcp/assistant-team.
|
|
926
|
+
"assistant-team": { tools: teamTools, members: [agent, "user_alice", "user_bob"] },
|
|
927
|
+
"assistant-leads": { tools: leadsTools, members: [agent] },
|
|
928
|
+
// Each personal pool spreads its user's highest tier.
|
|
835
929
|
"assistant-alice": {
|
|
836
|
-
...
|
|
837
|
-
tools: [...assistant.tools, "notes_alice", "gmail", "drive"],
|
|
930
|
+
tools: [...leadsTools, "notes_alice", "gmail", "drive"],
|
|
838
931
|
owner: "alice@example.com",
|
|
932
|
+
members: [agent],
|
|
839
933
|
},
|
|
840
934
|
"assistant-bob": {
|
|
841
|
-
...
|
|
842
|
-
tools: [...assistant.tools, "notes_bob", "gmail"],
|
|
935
|
+
tools: [...teamTools, "notes_bob", "gmail"],
|
|
843
936
|
owner: "bob@example.com",
|
|
937
|
+
members: [agent],
|
|
844
938
|
},
|
|
845
939
|
},
|
|
846
940
|
connectors: [
|
|
847
941
|
crm,
|
|
848
942
|
tickets,
|
|
943
|
+
billing,
|
|
849
944
|
// Shared auth: a maintainer connects each user's own account once.
|
|
850
945
|
remoteMcp("notes_alice", { url: "https://notes.example.com/mcp", auth: { type: "oauth" } }),
|
|
851
946
|
remoteMcp("notes_bob", { url: "https://notes.example.com/mcp", auth: { type: "oauth" } }),
|
|
@@ -856,17 +951,38 @@ createConnecta({
|
|
|
856
951
|
});
|
|
857
952
|
```
|
|
858
953
|
|
|
859
|
-
|
|
954
|
+
Alice is cleared for leads and Bob for team. The agent's client picks the
|
|
955
|
+
pool from who is present, for example:
|
|
860
956
|
|
|
861
957
|
```ts
|
|
862
|
-
|
|
863
|
-
const
|
|
864
|
-
const
|
|
958
|
+
// Lowest first. A person's tier is an index into this list.
|
|
959
|
+
const tiers = ["assistant", "assistant-team", "assistant-leads"];
|
|
960
|
+
const people: Record<string, { tier: number; handle?: string }> = {
|
|
961
|
+
"<alice's sender id>": { tier: 2, handle: "alice" },
|
|
962
|
+
"<bob's sender id>": { tier: 1, handle: "bob" },
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
// `verifiedParticipants`: everyone who can read the reply, other than the
|
|
966
|
+
// agent, from the platform's ids; undefined when the harness cannot verify it.
|
|
967
|
+
function poolFor(turn: { isDirect: boolean; verifiedParticipants?: string[] }) {
|
|
968
|
+
const present = turn.verifiedParticipants;
|
|
969
|
+
if (!present?.length) return tiers[0];
|
|
970
|
+
const known = present.map((id) => people[id]);
|
|
971
|
+
if (turn.isDirect && present.length === 1 && known[0]?.handle) {
|
|
972
|
+
return `assistant-${known[0].handle}`;
|
|
973
|
+
}
|
|
974
|
+
return tiers[Math.min(...known.map((person) => person?.tier ?? 0))];
|
|
975
|
+
}
|
|
976
|
+
const url = `https://connecta.example.com/agent/${poolFor(turn)}`;
|
|
865
977
|
```
|
|
866
978
|
|
|
867
|
-
|
|
868
|
-
|
|
979
|
+
A chat with Alice and Bob gets `assistant-team`; add an unknown guest and it
|
|
980
|
+
gets `assistant`, even if the platform marks the chat as direct. Rule 9 is
|
|
981
|
+
why each personal pool lists only the agent: people act as themselves only
|
|
982
|
+
through a Workspace mapping, and a shared-auth connector such as
|
|
869
983
|
`notes_alice` acts as Alice for whoever the pool admits.
|
|
984
|
+
Rule 10 is why `assistant-team` may list Alice and Bob: it holds only
|
|
985
|
+
connectors that every member may use on the deployment's account.
|
|
870
986
|
|
|
871
987
|
## Shared and personal auth
|
|
872
988
|
|
|
@@ -941,12 +1057,12 @@ namespace is left to clean up. They then delete each consent whose epoch is no
|
|
|
941
1057
|
longer live; one left behind cannot complete and expires with its link.
|
|
942
1058
|
`POST /ui/oauth/<id>` takes a `mode`:
|
|
943
1059
|
|
|
944
|
-
| Request | What it does
|
|
945
|
-
| ---------------------------------------- |
|
|
946
|
-
| `POST /ui/oauth/<id>` or `?mode=restart` | Issues a signed `/connect/<id>` link requesting a fresh epoch. No downstream authorization starts until the verified browser visits it.
|
|
947
|
-
| `?mode=continue` | Issues a signed `/connect/<id>` link requesting continuation. At the browser visit, a recent pending consent can be reused; otherwise authorization begins in the current epoch.
|
|
948
|
-
| `GET /connect/<id>?h=...` | Verifies the signed handoff and, when signed in, browser identity, connector visibility and management permission. With the UI mounted, hands off to `/connectors/<id>?h=...#auth` without consuming the link or starting OAuth; Clerk sign-in uses that shell. The Auth tab continues with `start=1`, which repeats verification, consumes the link and calls `startAuth`. Without the UI, the original browser visit starts consent. Explicit `POST /ui/oauth/<id>` actions return a signed link with `start=1` when the UI is mounted. |
|
|
949
|
-
| `DELETE /ui/oauth/<id>` | Disconnects and invalidates the cached catalog, even if the browser leaves.
|
|
1060
|
+
| Request | What it does |
|
|
1061
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1062
|
+
| `POST /ui/oauth/<id>` or `?mode=restart` | Issues a signed `/connect/<id>` link requesting a fresh epoch. No downstream authorization starts until the verified browser visits it. |
|
|
1063
|
+
| `?mode=continue` | Issues a signed `/connect/<id>` link requesting continuation. At the browser visit, a recent pending consent can be reused; otherwise authorization begins in the current epoch. |
|
|
1064
|
+
| `GET /connect/<id>?h=...` | Verifies the signed handoff and, when signed in, browser identity, connector visibility and management permission. With the UI mounted and a maintainer signed in, hands off to `/connectors/<id>?h=...#auth` without consuming the link or starting OAuth; Clerk sign-in uses that shell. The Auth tab continues with `start=1`, which repeats verification, consumes the link and calls `startAuth`. Without the UI, or for a pool member, the original browser visit starts consent. Explicit `POST /ui/oauth/<id>` actions return a signed link with `start=1` when the UI is mounted. |
|
|
1065
|
+
| `DELETE /ui/oauth/<id>` | Disconnects and invalidates the cached catalog, even if the browser leaves. |
|
|
950
1066
|
|
|
951
1067
|
`authorize_connector` issues the same browser link, with `force` carried in the
|
|
952
1068
|
signed handoff. On hosts without URL elicitation, direct and program auth
|
|
@@ -1827,7 +1943,7 @@ For providers offering both, deployment config chooses; handlers check `ctx.oaut
|
|
|
1827
1943
|
|
|
1828
1944
|
## Request-local downstream Bearer tokens
|
|
1829
1945
|
|
|
1830
|
-
`remoteMcp({ auth: { type: "request", token: async (ctx) => … } })` resolves
|
|
1946
|
+
`remoteMcp("<id>", { auth: { type: "request", token: async (ctx) => … } })` resolves
|
|
1831
1947
|
its token before connecting and before reusing a client. The callback receives
|
|
1832
1948
|
the operation's cancellation context, never an agent-selected credential owner.
|
|
1833
1949
|
Only shared ownership is accepted. Tokens remain inside the request's hardened
|
|
@@ -283,7 +283,7 @@ 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 call admission decision. On a read-only endpoint,
|
|
286
|
+
authority and change no call admission decision. On a read-only endpoint or connector,
|
|
287
287
|
search and describe omit write tools (`W12`): a write is absent from every
|
|
288
288
|
filter, and describing one is `unknown_tool`. `connecta.skill` guides are not
|
|
289
289
|
filtered and may name such a write; calling it is still refused.
|
|
@@ -555,12 +555,21 @@ URL-elicitation hosts retain their existing flow.
|
|
|
555
555
|
**E2.** The taxonomy: `retryable` is what connecta reports, `Y3` what a program
|
|
556
556
|
may do. A provider maps each downstream failure to the code that tells the
|
|
557
557
|
caller what to do next, and never invents a cause it was not told.
|
|
558
|
+
The table lists the codes a program commonly sees. Codes owned by one
|
|
559
|
+
mechanism are specified with it: `resource_template_ambiguous` and
|
|
560
|
+
`resource_match_budget_exceeded` with resource reads above; the
|
|
561
|
+
`executor_*` admission codes in `E5`; `destructive_tool_requires_approval` with
|
|
562
|
+
direct calls; and `input_required_invalid`, `input_required_limit`,
|
|
563
|
+
`downstream_oauth_required`, `oauth_revocation_failed`,
|
|
564
|
+
`provider_permission_denied`, and `auth_replay_refused` in
|
|
565
|
+
[meta-tools](./meta-tools.md). Each of those is non-retryable except
|
|
566
|
+
`executor_overloaded`.
|
|
558
567
|
|
|
559
568
|
| Code | Raised when | `retryable` |
|
|
560
569
|
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
561
570
|
| `unknown_address` | no visible connector owns the tool address or grants the resource reader | false |
|
|
562
571
|
| `unknown_tool` | the connector has no such tool | false |
|
|
563
|
-
| `read_only_endpoint` | a program on a read-only endpoint attempted a write (`E4`)
|
|
572
|
+
| `read_only_endpoint` | a program on a read-only endpoint or connector attempted a write (`E4`) | false |
|
|
564
573
|
| `auth_required` | the credential is missing, expired, or rejected | false |
|
|
565
574
|
| `invalid_args` | arguments or discovery bounds were rejected | false |
|
|
566
575
|
| `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 |
|
|
@@ -577,6 +586,7 @@ caller what to do next, and never invents a cause it was not told.
|
|
|
577
586
|
| `result_too_large` | a discovery response exceeded its byte bound | false |
|
|
578
587
|
| `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
|
|
579
588
|
| `write_outcome_unknown` | a program write was sent and no answer came back; it is never sent again (`W9`) | false |
|
|
589
|
+
| `executor_failed` | the executor returned a malformed result or failed outside the guest program | false |
|
|
580
590
|
|
|
581
591
|
**E3.** `auth_required` carries the same recovery envelope as `call_tool`:
|
|
582
592
|
`connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
|
|
@@ -584,7 +594,7 @@ caller what to do next, and never invents a cause it was not told.
|
|
|
584
594
|
sentence. A program cannot recover credentials — only an operator can — so stop
|
|
585
595
|
and let the failure reach the model.
|
|
586
596
|
|
|
587
|
-
**E4.** A tool classified as a write never runs on a read-only endpoint
|
|
597
|
+
**E4.** A tool classified as a write never runs on a read-only endpoint or connector
|
|
588
598
|
(`W12`). A program's call is refused with `read_only_endpoint` before
|
|
589
599
|
validation and before anything is sent. The refusal carries no `nextAction` and
|
|
590
600
|
echoes no arguments: no route on that endpoint writes, so the program reports
|
|
@@ -685,8 +695,14 @@ when the metadata describes a write on a read-only endpoint.
|
|
|
685
695
|
Caught call failures retain that connector visibility when catalog loading or
|
|
686
696
|
tool resolution fails. Recovery messages listing configured connectors or
|
|
687
697
|
local guides retain visibility of those contributors too.
|
|
698
|
+
Search guidance retains its referenced connectors even when no tool matches,
|
|
699
|
+
including connectors named by id or title and identities summarized as an
|
|
700
|
+
additional count. Scoped guides and summaries, absence rosters, and catalog
|
|
701
|
+
errors retain their contributing connectors as well.
|
|
702
|
+
Search counts, ranking, and term analysis retain every searched connector,
|
|
703
|
+
including matches outside the returned page or beyond the requested offset.
|
|
688
704
|
Each page rechecks current auth,
|
|
689
|
-
grants, pool membership, and
|
|
705
|
+
grants, pool membership, and endpoint OR connector `readOnly`. Reading a stash inside a program carries
|
|
690
706
|
its dependencies forward to any new return stash. The page cap is the smallest
|
|
691
707
|
of the deployment cap and contributing connectors' effective `maxResultBytes`.
|
|
692
708
|
Read-only endpoints cannot page write results. A UUID is a handle, never a grant.
|
|
@@ -711,7 +727,7 @@ has no host log stream and cannot supply logs from its suspended guest (`L4`).
|
|
|
711
727
|
|
|
712
728
|
**R6.** Every run returns `hostCalls: { attempted, admitted, succeeded, failed }`,
|
|
713
729
|
including admission failure, cancellation, budget refusal, and code-size refusal.
|
|
714
|
-
Calls include search, describe, call, result, and skill. `emit` has separate budgets.
|
|
730
|
+
Calls include search, describe, call, read, result, and skill. `emit` has separate budgets.
|
|
715
731
|
An abandoned host call counts as failed when the run ends; guest catches do not
|
|
716
732
|
change the counts. These numbers are outside the program's returned `result`.
|
|
717
733
|
`diagnostics: true` adds the existing payload-free timing block. Emitting adds
|
|
@@ -770,7 +786,8 @@ a successful result, appended to `content` after the JSON envelope, which gains
|
|
|
770
786
|
response (`R6`). `structuredContent` stays the envelope alone — emission is
|
|
771
787
|
presentation, not a second data channel.
|
|
772
788
|
|
|
773
|
-
**M3.** Text emission shares `R2`'s 24,000 serialized-character cap
|
|
789
|
+
**M3.** Text emission shares `R2`'s 24,000 serialized-character cap, less 512
|
|
790
|
+
characters reserved for the envelope (23,488 for emitted text). Its
|
|
774
791
|
serialized block size reduces the return budget, and oversized text fails with
|
|
775
792
|
`result_too_large`. Image/audio blocks use the separate transport budget and
|
|
776
793
|
reach the model as real MCP media content, including when `emit` is not awaited.
|
|
@@ -881,7 +898,7 @@ because connecta enforces them above the sandbox:
|
|
|
881
898
|
|
|
882
899
|
| Bound | Value |
|
|
883
900
|
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
884
|
-
| Host calls per execution, shared by
|
|
901
|
+
| Host calls per execution, shared by every guest call except `emit` | 20 by default, `execute.maxHostCalls` |
|
|
885
902
|
| Writes per run on a writable endpoint, on top of the host calls they also spend (`W10`) | 10 by default, `execute.maxWrites` |
|
|
886
903
|
| Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
|
|
887
904
|
| Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
|
|
@@ -891,8 +908,9 @@ because connecta enforces them above the sandbox:
|
|
|
891
908
|
| Result | 24,000 serialized characters |
|
|
892
909
|
| Logs presented to the model | 4,000 characters |
|
|
893
910
|
|
|
894
|
-
Every `call`, `read`, `search`, and `
|
|
895
|
-
resolution, validation, or dispatch
|
|
911
|
+
Every `call`, `read`, `search`, `describe`, `result`, and `skill` spends one host
|
|
912
|
+
call on entry, before resolution, validation, or dispatch; automatic transfer
|
|
913
|
+
pages of a paged result do not. Catching a local refusal refunds nothing.
|
|
896
914
|
The first call beyond the budget ends the host run with one non-retryable
|
|
897
915
|
`budget_exceeded` (`E2`). Its bridge stays pending until lease disposal so guest
|
|
898
916
|
`try/catch` cannot swallow it. Later host access, including `emit`, stops; pending
|
|
@@ -1008,7 +1026,9 @@ sent and records nothing: it was never an attempt (`W9`).
|
|
|
1008
1026
|
Whether an endpoint writes belongs to deployment code. Every deployment sets
|
|
1009
1027
|
`readOnly` for `/mcp` explicitly; construction fails without it. Each named pool inherits
|
|
1010
1028
|
the deployment's `readOnly` unless it sets its own, in either direction. The
|
|
1011
|
-
client owns approval
|
|
1029
|
+
client owns approval. Every connector also accepts optional `readOnly?: boolean`
|
|
1030
|
+
(default `false`), enforced on every endpoint. Effective read-only is endpoint OR
|
|
1031
|
+
connector; setting connector `false` cannot reopen a read-only endpoint.
|
|
1012
1032
|
|
|
1013
1033
|
```ts
|
|
1014
1034
|
createConnecta({
|
|
@@ -197,7 +197,10 @@ CF_ACCESS_CLIENT_ID='<access-service-id>' CF_ACCESS_CLIENT_SECRET='<access-servi
|
|
|
197
197
|
|
|
198
198
|
The service headers admit the health probe at the edge; only the `cta_` token
|
|
199
199
|
authenticates the agent endpoint inside Connecta. If the operator UI is enabled, maintainer-authenticated
|
|
200
|
-
`connecta doctor --config --url <public-url>`
|
|
200
|
+
`connecta doctor --config --url <public-url>` (on Node, `CONNECTA_TOKEN` set to a
|
|
201
|
+
maintainer's current Clerk session token issued for this deployment's origin;
|
|
202
|
+
Clerk OAuth access tokens are refused here, and Access on Workers has no
|
|
203
|
+
non-browser maintainer credential, so otherwise use the UI's Settings page) prints the caller-scoped
|
|
201
204
|
configuration snapshot as JSON. It runs no diagnostic program and prints no
|
|
202
205
|
raw HTTP failure body. [Operator UI](./operator-ui.md) describes the returned
|
|
203
206
|
configuration, live facts, and permission rules. The config route is a human
|
|
@@ -227,6 +230,7 @@ Bump the exact package pin, read the [changelog](../CHANGELOG.md) for every
|
|
|
227
230
|
intervening release, and apply the relevant migration before startup. `init`
|
|
228
231
|
does not merge changes into an existing deployment.
|
|
229
232
|
|
|
233
|
+
- [Migration to 0.34](./migration-0.34.md) covers the Overview landing page, Endpoints at `/endpoints`, default browser titles, and Workspace discovery in owner-less agent pools. No storage migration or token rotation is needed from 0.33.
|
|
230
234
|
- [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
235
|
- [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.
|
|
232
236
|
- [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.
|