@zackbart/connecta 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/README.md +95 -116
- package/SECURITY.md +1 -1
- package/bin/connecta.mjs +24 -6
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +167 -23
- package/dist/access-tokens.js +676 -140
- package/dist/activity.d.ts +3 -0
- package/dist/agent-identity.js +47 -0
- package/dist/auth/cloudflare-access.js +13 -5
- package/dist/branding.d.ts +7 -0
- package/dist/branding.js +76 -9
- package/dist/call-admission-context.js +15 -0
- package/dist/call-admission.js +30 -43
- package/dist/call-recovery.js +6 -2
- package/dist/catalog-service.js +15 -1
- package/dist/config-value-sources.js +16 -9
- package/dist/config.d.ts +48 -75
- package/dist/config.js +144 -36
- package/dist/connector-access.d.ts +4 -34
- package/dist/connector-access.js +15 -99
- package/dist/connector-caller.d.ts +7 -1
- package/dist/connector-caller.js +4 -2
- package/dist/connectors/api-connector.d.ts +27 -4
- package/dist/connectors/api-connector.js +2 -0
- package/dist/connectors/remote-mcp.js +2 -0
- package/dist/describe-config.d.ts +31 -7
- package/dist/describe-config.js +8 -17
- package/dist/execute.js +1 -11
- package/dist/fix-prompt.js +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +30 -10
- package/dist/invocation.js +5 -3
- package/dist/membership.d.ts +53 -0
- package/dist/membership.js +121 -0
- package/dist/module-contracts.d.ts +23 -6
- package/dist/oauth-callback-outcome.js +2 -2
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +2 -4
- package/dist/operator-ui/agents.d.ts +74 -0
- package/dist/operator-ui/agents.js +104 -0
- package/dist/operator-ui/config-summary.js +562 -0
- package/dist/operator-ui/connection-map.js +375 -0
- package/dist/operator-ui/contract.d.ts +149 -28
- package/dist/operator-ui/endpoints.js +221 -0
- package/dist/operator-ui/fix-prompts.js +5 -5
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/member-page.js +97 -0
- package/dist/operator-ui/model.d.ts +14 -6
- package/dist/operator-ui/overview.js +134 -0
- package/dist/operator-ui/view.d.ts +107 -37
- package/dist/operator-ui/view.js +163 -86
- package/dist/page-styles.js +5 -3
- package/dist/provider.d.ts +61 -8
- package/dist/provider.js +46 -7
- package/dist/providers/_shared/google/workspace.d.ts +56 -17
- package/dist/providers/_shared/google/workspace.js +34 -8
- package/dist/providers/_shared/rest/tools.js +2 -2
- package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
- package/dist/providers/_shared/rest/value-safety.js +699 -7
- package/dist/providers/ccb/index.js +2 -2
- package/dist/providers/cloudflare/index.js +3 -3
- package/dist/providers/cloudflare/rest.js +25 -3
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/cloudflare/value-safety.js +32 -0
- package/dist/providers/docs/index.js +4 -4
- package/dist/providers/docs/skill.generated.js +2 -2
- package/dist/providers/drive/index.js +4 -4
- package/dist/providers/drive/skill.generated.js +2 -2
- package/dist/providers/forms/index.js +4 -4
- package/dist/providers/forms/skill.generated.js +2 -2
- package/dist/providers/gmail/index.js +11 -9
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/mixpanel/index.js +1 -1
- package/dist/providers/mixpanel/skill.generated.js +3 -3
- package/dist/providers/notion/index.d.ts +7 -0
- package/dist/providers/notion/index.js +100 -11
- package/dist/providers/notion/rest.js +45 -5
- package/dist/providers/notion/skill.generated.js +2 -2
- package/dist/providers/notion/value-safety.js +30 -5
- package/dist/providers/overflow/index.js +2 -2
- package/dist/providers/overflow/skill.generated.js +2 -2
- package/dist/providers/planning-center/index.js +2 -2
- package/dist/providers/planning-center/skill.generated.js +4 -4
- package/dist/providers/revenuecat/skill.generated.js +2 -2
- package/dist/providers/sheets/index.d.ts +3 -0
- package/dist/providers/sheets/index.js +7 -4
- package/dist/providers/sheets/skill.generated.js +2 -2
- package/dist/providers/slides/index.js +4 -4
- package/dist/providers/slides/skill.generated.js +2 -2
- package/dist/providers/stripe/rest.js +9 -2
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/stripe/value-safety.js +22 -0
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/index.js +1 -1
- package/dist/providers/vercel/rest.js +20 -2
- package/dist/providers/vercel/skill.generated.js +2 -2
- package/dist/providers/vercel/value-safety.js +22 -0
- package/dist/registry.d.ts +2 -3
- package/dist/registry.js +23 -9
- package/dist/routes/access-tokens.js +82 -75
- package/dist/routes/activity-disclosure.js +17 -18
- package/dist/routes/activity.js +34 -54
- package/dist/routes/actor-labels.js +33 -4
- package/dist/routes/agent-path.js +43 -0
- package/dist/routes/connect.js +16 -12
- package/dist/routes/credentials.js +16 -40
- package/dist/routes/mcp.js +161 -104
- package/dist/routes/oauth-management.js +11 -5
- package/dist/routes/oauth.js +5 -11
- package/dist/routes/operator.js +24 -23
- package/dist/routes/shared.d.ts +60 -20
- package/dist/routes/shared.js +187 -96
- package/dist/routes/ui-config.js +437 -97
- package/dist/routes/ui-data.js +2 -2
- package/dist/routes/ui.js +9 -16
- package/dist/runtime/call-admission.js +9 -0
- package/dist/server.js +5 -0
- package/dist/skills.js +10 -0
- package/dist/storage/keys.js +9 -1
- package/dist/storage/sql.js +5 -1
- package/dist/types.d.ts +29 -7
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +23 -16
- package/dist/usage-guide.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace-address.js +9 -0
- package/dist/workspace-subject.js +23 -0
- package/documentation/README.md +47 -23
- package/documentation/architecture.md +371 -61
- package/documentation/auth.md +897 -249
- package/documentation/code-mode.md +37 -16
- package/documentation/deploying.md +126 -25
- package/documentation/integrating.md +97 -23
- package/documentation/meta-tools.md +50 -31
- package/documentation/migration-0.33.md +352 -0
- package/documentation/migration-0.34.md +67 -0
- package/documentation/operating.md +3 -1
- package/documentation/operator-tests.md +147 -34
- package/documentation/operator-ui.md +519 -64
- package/documentation/provider-auth-migration.md +37 -23
- package/documentation/provider-migration-0.29.md +2 -2
- package/examples/worker/AGENTS.md +37 -9
- package/examples/worker/README.md +127 -61
- package/examples/worker/src/connecta.config.ts +40 -23
- package/examples/worker/wrangler.jsonc +4 -1
- package/package.json +19 -18
- package/templates/node/.env.example +3 -0
- package/templates/node/AGENTS.md +33 -6
- package/templates/node/Dockerfile +2 -2
- package/templates/node/README.md +80 -52
- package/templates/node/docker-compose.yml +3 -2
- package/templates/node/package.json +4 -3
- package/templates/node/src/adopt-token.ts +23 -0
- package/templates/node/src/connecta.config.ts +28 -12
- package/templates/node/src/rotate-token.ts +17 -0
- package/templates/node/src/provision-token.ts +0 -17
|
@@ -43,14 +43,14 @@ mode returns native `content`. Default-mode successes declare
|
|
|
43
43
|
`_meta["dev.connecta/format"]: "json" | "text"`; value mode places the value in
|
|
44
44
|
`data` and declares `format` in its complete result envelope.
|
|
45
45
|
|
|
46
|
-
| Tool | Arguments | Returns
|
|
47
|
-
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
48
|
-
| `execute_code` | `code`, `diagnostics?` | `{ result?, error?, hostCalls }`, plus diagnostics when asked
|
|
49
|
-
| `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json" \| "typescript"` | `{ catalogErrors, tools, total, offset, limit, hasMore }`, plus `queryAnalysis` on a partial or
|
|
50
|
-
| `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes
|
|
51
|
-
| `call_destructive_tool` | the same, plus `reason?` | the same
|
|
52
|
-
| `authorize_connector` | `connector`, `force?` | the class-specific handoff in [authorization recovery](#authorization-recovery)
|
|
53
|
-
| `skills` | `name?` | the listing when `name` is absent, that skill's markdown when it is present
|
|
46
|
+
| Tool | Arguments | Returns |
|
|
47
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
48
|
+
| `execute_code` | `code`, `diagnostics?` | `{ result?, error?, hostCalls }`, plus `logs`, `emitted`, and `emittedDiscarded` when present and diagnostics when asked |
|
|
49
|
+
| `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json" \| "typescript"` | `{ catalogErrors, tools, total, offset, limit, hasMore }`, plus `nextOffset` when more remain, `absence` when the query names a service with no configured connector, and `queryAnalysis` on a partial, failed, unsearchable, or absent-service search |
|
|
50
|
+
| `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes |
|
|
51
|
+
| `call_destructive_tool` | the same, plus `reason?` | the same |
|
|
52
|
+
| `authorize_connector` | `connector`, `force?` | the class-specific handoff in [authorization recovery](#authorization-recovery) |
|
|
53
|
+
| `skills` | `name?` | the listing when `name` is absent, that skill's markdown when it is present |
|
|
54
54
|
|
|
55
55
|
`limit` defaults to 8 and is capped at 100, as is one `connecta.describe` batch.
|
|
56
56
|
`connecta.result`'s `offset` is a whole number of bytes ≥ 0 defaulting to 0 and
|
|
@@ -66,8 +66,8 @@ refuse a consequential call.
|
|
|
66
66
|
|
|
67
67
|
`diagnostics: true` adds compact request-local timing and serialized-size
|
|
68
68
|
aggregates for a caller measuring a workflow: a `diagnostics` block from
|
|
69
|
-
`execute_code`, a `timing` block on
|
|
70
|
-
|
|
69
|
+
`execute_code`, a `timing` block on every structured call response: value-mode successes
|
|
70
|
+
and every `{ ok: false }` failure. Normal calls pay nothing for it,
|
|
71
71
|
and the measurements never contain program source, arguments, values, addresses,
|
|
72
72
|
credentials, logs, or raw error text.
|
|
73
73
|
|
|
@@ -194,8 +194,7 @@ discovery made the same bet).
|
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
It is something to read, not something that runs: programs stay JavaScript,
|
|
197
|
-
and `execute_code` accepts no erasable TypeScript
|
|
198
|
-
([decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)). The output half is the provider's declared
|
|
197
|
+
and `execute_code` accepts no erasable TypeScript. The output half is the provider's declared
|
|
199
198
|
schema, else an `S9` observation that opens with the
|
|
200
199
|
`/* observed, not declared */` marker and keeps the `outputSchemaSource`
|
|
201
200
|
field, else `unknown` — runtime evidence never reads as a contract, even when
|
|
@@ -222,7 +221,7 @@ declared schema exactly as they do for compact.
|
|
|
222
221
|
A connector may attach a deployment-owned guide as markdown, keeping the
|
|
223
222
|
original `usageGuide: string` configuration, or as
|
|
224
223
|
`{ content, summary?, required? }` — which registers no connector and creates no
|
|
225
|
-
shared runtime template. The
|
|
224
|
+
shared runtime template. The skills catalog prepends Agent Skills frontmatter to
|
|
226
225
|
`content` and preserves the guide body, including its whitespace.
|
|
227
226
|
`summary` is normalized and refuses construction over 120 characters; absent,
|
|
228
227
|
Connecta derives the same bounded fallback the skills listing uses from the
|
|
@@ -254,14 +253,22 @@ A connector-scoped lexical miss keeps that connector's guide metadata under
|
|
|
254
253
|
contains no endpoint vocabulary: a required guide stays discoverable instead of
|
|
255
254
|
disappearing with the zero-tool page.
|
|
256
255
|
|
|
257
|
-
Two built-in skills are byte-identical across deployments. `usage`
|
|
258
|
-
|
|
256
|
+
Two built-in skills are byte-identical across deployments. `usage` is fetched
|
|
257
|
+
only for missing guidance or program repair, and owns program selection detail, examples, runtime
|
|
259
258
|
differences, and repair guidance; `investigate` is on-demand guidance for
|
|
260
259
|
purchase verification, experiment checks, and customer or deployment
|
|
261
260
|
investigations — shared guidance, not a saved workflow and not a source of
|
|
262
261
|
deployment-specific ids. The always-loaded instructions and tool definitions keep
|
|
263
262
|
route selection, the fail-closed boundary, and the minimum guest syntax, so a
|
|
264
|
-
client that never fetches a skill can still write a valid first program.
|
|
263
|
+
client that never fetches a skill can still write a valid first program.
|
|
264
|
+
The instructions are the same on every endpoint except one addition: at
|
|
265
|
+
`/mcp/<pool>` and `/agent/<pool>`, a pool that sets a
|
|
266
|
+
[`description`](./auth.md#pools) has it appended after `This endpoint: `, so
|
|
267
|
+
the model learns that endpoint's scope, for example
|
|
268
|
+
`This endpoint: Team tier: shared tools only. For someone's own mail or files, ask them to message you one-on-one.`
|
|
269
|
+
Plain `/mcp` never carries one. The description changes no tool or tool
|
|
270
|
+
description, and `initialize` and `server/discover` stay `cacheScope: "private"`,
|
|
271
|
+
so a client keeps what it learned per endpoint. Connector
|
|
265
272
|
guides stay scoped to the deployment that listed them even when two deployments
|
|
266
273
|
use identical content, and a deployment with no connector guides receives none of
|
|
267
274
|
the short conditional guide pointers in its tool definitions.
|
|
@@ -282,7 +289,7 @@ is its slug; other IDs use `connector-` followed by a stable SHA-256 prefix.
|
|
|
282
289
|
The suffix makes each
|
|
283
290
|
document a valid Agent Skills file; the roots `skill://connecta/usage` and
|
|
284
291
|
`skill://connecta/connectors/<connector-id>` also resolve. Provider guides are
|
|
285
|
-
built from
|
|
292
|
+
built from maintained provider guides with the existing
|
|
286
293
|
connection context and deployment instructions. Local manifests contain a
|
|
287
294
|
SHA-256 digest and UTF-8 byte size of the complete document. `usage` and
|
|
288
295
|
`investigate` remain compatibility names; `connector:<id>` remains a
|
|
@@ -387,7 +394,11 @@ including dependencies inherited from pages read during the run. Downstream skil
|
|
|
387
394
|
resource content requires a whole-connector grant on every page. Local guides and catalog
|
|
388
395
|
metadata require the contributing connector or tool to remain visible, including
|
|
389
396
|
caught catalog-load errors, unknown-tool failures, and recovery messages that
|
|
390
|
-
list visible connectors or local guides.
|
|
397
|
+
list visible connectors or local guides. No-match search guidance also retains
|
|
398
|
+
the connectors it names or summarizes by count, including matches to connector
|
|
399
|
+
titles; scoped guides and summaries, absence rosters, and catalog errors retain
|
|
400
|
+
their contributors. Search counts, ranking, and term analysis also retain every
|
|
401
|
+
searched connector, including matches outside the returned page. Returns with no downstream calls still
|
|
391
402
|
require matching identity, origin and endpoint, current auth, and pool membership. A page must
|
|
392
403
|
match those bindings and pass current auth, connector/tool grants, pool membership,
|
|
393
404
|
and read-only checks. Old entries lacking bindings fail closed. A random UUID is a
|
|
@@ -395,7 +406,7 @@ handle, never an access grant.
|
|
|
395
406
|
|
|
396
407
|
New entries store UTF-8 bytes in a base64 envelope split across storage keys,
|
|
397
408
|
48 KiB of result text per chunk — widening past roughly 1.5 MB so no result
|
|
398
|
-
occupies more than
|
|
409
|
+
occupies more than 32 keys, because every chunk is also a write. `connecta.result`
|
|
399
410
|
reads and decodes only the chunks a page covers plus a few boundary bytes, so
|
|
400
411
|
paging a 1.2 MB result costs the same per page as paging a 300 KB one; it
|
|
401
412
|
neither encodes nor reads the whole result per page. Entries without the required identity and invocation bindings fail closed,
|
|
@@ -427,7 +438,7 @@ JSON; everything after the first newline is the preview. `structuredContent`
|
|
|
427
438
|
is absent, so the notice cannot hide the preview in clients that prefer it:
|
|
428
439
|
|
|
429
440
|
```text
|
|
430
|
-
{"truncated":true,"resultId":"…","totalBytes":161420,"hint":"This write already ran: do not call it again to see its result. Bytes 0-24000 of 161420 follow; page the rest with connecta.result using nextAction.","nextOffset":24000,"nextAction":{"tool":"execute_code","arguments":{"code":"async () => await connecta.result(\"…\", { offset: 24000 })"}}}
|
|
441
|
+
{"truncated":true,"resultId":"…","totalBytes":161420,"hint":"This write already ran: do not call it again to see its result. Bytes 0-24000 of 161420 follow; page the rest with connecta.result using nextAction.","nextOffset":24000,"nextAction":{"tool":"execute_code","arguments":{"code":"async () => await connecta.result(\"…\", { offset: 24000, maxBytes: 3829 })"}}}
|
|
431
442
|
{"ts":"2026-09-16T21:31:11.759Z","actor":"sam.ortiz@example.com",…
|
|
432
443
|
```
|
|
433
444
|
|
|
@@ -459,7 +470,7 @@ characters. One that rejects outright — Claude Code with
|
|
|
459
470
|
`MAX_MCP_OUTPUT_TOKENS` set below 12,500 — needs a cap under twice its limit.
|
|
460
471
|
|
|
461
472
|
The preview is the head of what `connecta.result` pages, cut on a character
|
|
462
|
-
boundary, so `nextAction.arguments.
|
|
473
|
+
boundary, so the `offset` in `nextAction.arguments.code` continues exactly where it stops. A
|
|
463
474
|
lone text block — including the one synthesized from `structuredContent` — is
|
|
464
475
|
measured, stashed, previewed, and paged as its text: wrapping it in a content
|
|
465
476
|
envelope added nothing but a second layer of JSON escaping, so a JSON payload
|
|
@@ -598,8 +609,8 @@ same when no visible connector ID or title names that service. This service-name
|
|
|
598
609
|
vocabulary is query interpretation, not an inventory of configured providers.
|
|
599
610
|
For other service names, use the explicit `connector` scope. An unavailable
|
|
600
611
|
catalog is a catalog failure, never an absence. Absence records and catalog
|
|
601
|
-
errors use only the endpoint
|
|
602
|
-
|
|
612
|
+
errors use only the endpoint's tools; a connector the endpoint does not serve
|
|
613
|
+
is indistinguishable from an unconfigured one.
|
|
603
614
|
|
|
604
615
|
If no tool covers every search term, wider any-term fallback remains available
|
|
605
616
|
for ordinary action/object searches and the page carries `matchMode: "partial"`.
|
|
@@ -683,8 +694,8 @@ replay. Downstream payloads and input responses are capped at 64 KiB, with at
|
|
|
683
694
|
most 16 inputs and 256 characters per downstream key. The combined private
|
|
684
695
|
state history is also capped at 64 KiB. Sealed wire state is
|
|
685
696
|
capped at 128 KiB. `input_required_invalid`, `input_required_limit`,
|
|
686
|
-
|
|
687
|
-
|
|
697
|
+
and `input_required_unsupported` are non-retryable typed failures;
|
|
698
|
+
`input_required_round_limit` is an untyped failure with `retryable: false`.
|
|
688
699
|
|
|
689
700
|
Programs and in-process meta-tools return `input_required_unsupported`, with a
|
|
690
701
|
`nextAction` naming the equivalent direct call and bounded original arguments.
|
|
@@ -933,9 +944,13 @@ list connectors from another pool or identity's grants.
|
|
|
933
944
|
A write that times out after dispatch returns `write_outcome_unknown` with
|
|
934
945
|
`retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted?, recovery? }`.
|
|
935
946
|
No write is automatically replayed. `recovery` carries facts the connector
|
|
936
|
-
recorded before dispatch
|
|
937
|
-
|
|
938
|
-
|
|
947
|
+
recorded before dispatch, such as a REST connector's
|
|
948
|
+
generated or caller-supplied `idempotencyKey`. Recovery accepts keys up to 255
|
|
949
|
+
characters, matching the REST write schema. Other recovery values remain capped
|
|
950
|
+
at 128 characters, with at most four facts per call and camelCase names of at
|
|
951
|
+
most 32 characters. Values outside these bounds are dropped whole. A deliberate
|
|
952
|
+
retry that reuses the key with the exact original arguments cannot repeat the
|
|
953
|
+
write while the vendor retains the key
|
|
939
954
|
(Stripe may prune v1 keys after 24 hours). After that, look the object up before
|
|
940
955
|
retrying. When a connector recorded such facts, any other dispatched write whose
|
|
941
956
|
outcome is unknown (a lost response body, a 5xx) also carries `uncertainCall`
|
|
@@ -954,18 +969,22 @@ SDK timeout classes, and runtime network codes. Untyped prose is non-retryable:
|
|
|
954
969
|
name, or URL never changes the verdict. JSON-RPC `ProtocolError.data` is dropped
|
|
955
970
|
at the downstream boundary, even when the agent may see the error's message.
|
|
956
971
|
|
|
957
|
-
##
|
|
972
|
+
## Contributor reference
|
|
973
|
+
|
|
974
|
+
### Source and tests
|
|
958
975
|
|
|
959
976
|
Handlers and schemas live in [meta-tools.ts](https://github.com/zackbart/connecta/blob/main/src/meta-tools.ts).
|
|
960
977
|
[Catalog service](https://github.com/zackbart/connecta/blob/main/src/catalog-service.ts)
|
|
961
978
|
and [invocation](https://github.com/zackbart/connecta/blob/main/src/invocation.ts)
|
|
962
979
|
serve both direct tools and programs. [Skills](https://github.com/zackbart/connecta/blob/main/src/skills.ts)
|
|
963
|
-
owns the supplied usage instructions
|
|
980
|
+
owns the supplied usage instructions, each endpoint's instructions
|
|
981
|
+
(`endpointInstructions`), and connector guides.
|
|
964
982
|
|
|
965
983
|
[Schema tests](https://github.com/zackbart/connecta/blob/main/test/meta-tool-schemas.test.ts),
|
|
966
984
|
[search tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-search.test.ts),
|
|
967
985
|
[call tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-call.test.ts),
|
|
968
986
|
[skills tests](https://github.com/zackbart/connecta/blob/main/test/skills-extension.test.ts),
|
|
969
|
-
|
|
987
|
+
[surface tests](https://github.com/zackbart/connecta/blob/main/test/code-first-surface.test.ts),
|
|
988
|
+
and [pool description tests](https://github.com/zackbart/connecta/blob/main/test/pool-description.test.ts)
|
|
970
989
|
check the published contract. Guest parity and clause evidence are in
|
|
971
990
|
[code-mode verification](./code-mode.md#verification).
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Migration to 0.33
|
|
2
|
+
|
|
3
|
+
Use this upgrade guide for an existing deployment moving to 0.33.
|
|
4
|
+
[Deployment upgrades](./deploying.md#upgrade-an-existing-deployment) cover the
|
|
5
|
+
general procedure; [the agent index](./README.md) routes other work.
|
|
6
|
+
|
|
7
|
+
0.33 makes pools the access model.
|
|
8
|
+
Every deployment with inbound auth changes its configuration. Each removed
|
|
9
|
+
option fails construction with a message naming its replacement, so a
|
|
10
|
+
deployment that starts has no leftover option silently ignored. Work through
|
|
11
|
+
the sections in order, then [verify](#verify).
|
|
12
|
+
|
|
13
|
+
## Old option to new option
|
|
14
|
+
|
|
15
|
+
| 0.32 | 0.33 |
|
|
16
|
+
| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `identity.connectorAccess` | A pool per audience, with `tools` and `members`. A guarded `{ tool, requireReadOnly: true }` grant becomes a pool with `readOnly: true` |
|
|
18
|
+
| Top-level `grant` | `maintainers` |
|
|
19
|
+
| Pool `grant` | Pool `members` |
|
|
20
|
+
| `describeConfig().hasGrant` (root and pools) | `describeConfig().maintainers` and each pool's `members` |
|
|
21
|
+
| `identity.credentialAdministration` | `maintainers` (interactive) |
|
|
22
|
+
| `identity.personalConnection` | Pool `members` for personal connectors in their pools; maintainers for their own |
|
|
23
|
+
| `identity.accessTokenManagement` | `maintainers` (interactive) |
|
|
24
|
+
| `identity.activityAccess` | `maintainers`, still narrowed by `activity.readGate` |
|
|
25
|
+
| The `identity` section, even empty | Removed; delete it |
|
|
26
|
+
| `ConnectaIdentityConfig`, `ConnectorPermission` types | Removed |
|
|
27
|
+
| Tokens created or named in the UI, human-bound tokens | `agents: ["<name>"]`, one token slot each |
|
|
28
|
+
| `accessTokens(storage, { maxActive })` | `accessTokens(storage)` |
|
|
29
|
+
| `describeConfig().modules.accessTokens.maxActive` | `describeConfig().agents` |
|
|
30
|
+
| Node template `provision-token` | `npm run --silent rotate-token -- <agent>`; existing tokens: `npm run adopt-token` |
|
|
31
|
+
| A machine at `/mcp` or `/mcp/<pool>` with `cta_` | `/agent/<pool>`, with `"agent:<name>"` in the pool's `members` |
|
|
32
|
+
| Access service token plus `cta_` behind Cloudflare Access | `cta_` alone, through an `/agent/*` Access Bypass application |
|
|
33
|
+
| Grant callbacks matching token record ids | Membership by agent name; `identity.agent` in custom code |
|
|
34
|
+
| A Workspace `subject` mapping that answered for machine tokens | The pool's `owner` |
|
|
35
|
+
| One Workspace connector per person (`gmail_alice`) | One connector with a mapping `subject`, plus pool owners |
|
|
36
|
+
| `POST`/`DELETE /ui/access-tokens/<agent>` without a body | `{ "expect": "<revision>" }` from `GET /ui/access-tokens` |
|
|
37
|
+
| Operator contract `schemaVersion: 1`, `you.grants`, `you.permissions`, and `hasGrant` | `schemaVersion: 2` with `role`, `root`, `pools`, `agents`, `matrix`, and `connectors`; `OperatorUiContract` names v2 |
|
|
38
|
+
| `OperatorViewer`, `OperatorPool` types | Removed; `OperatorPoolV2`, `OperatorRootEndpoint`, `OperatorMemberConnector` and the other v2 types |
|
|
39
|
+
| `/ui/data`, `/ui/connectors/<id>` | Maintainers only; members read their view from `/ui/api/config` |
|
|
40
|
+
| A connector that connects with `startAuth` alone, or disconnects only alongside it | Connect needs `startAuth`, `finishAuth`, and `verifyState`; disconnect needs only `disconnectAuth` |
|
|
41
|
+
| `DELETE /ui/credentials/<id>` on an empty slot answering 204 | 409 `{ "problem": "credential_required" }` |
|
|
42
|
+
| The `agent_not_maintainer` denial reason | Removed; agents are refused at human endpoints before admission |
|
|
43
|
+
|
|
44
|
+
## Maintainers and pool members
|
|
45
|
+
|
|
46
|
+
0.32 combined an identity resolver, a top-level `grant`, and pool `grant`
|
|
47
|
+
callbacks. 0.33 reads access from two static lists in code:
|
|
48
|
+
|
|
49
|
+
- `maintainers` names the people who reach plain `/mcp`, which serves the
|
|
50
|
+
whole catalog, and every operator route. It is required whenever an inbound
|
|
51
|
+
provider that admits people is configured.
|
|
52
|
+
- Each pool's `members` names who reaches it: people at `/mcp/<pool>`, agents
|
|
53
|
+
as `"agent:<name>"` at `/agent/<pool>`. A pool serves exactly its `tools`,
|
|
54
|
+
intersected with nothing. A maintainer opens no pool implicitly.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// 0.32
|
|
58
|
+
createConnecta({
|
|
59
|
+
auth: clerkAuth({ publishableKey, secretKey }),
|
|
60
|
+
grant: ({ principal }) => principal?.id === "user_alice",
|
|
61
|
+
identity: { connectorAccess, credentialAdministration, activityAccess },
|
|
62
|
+
pools: {
|
|
63
|
+
support: { tools: ["tickets", "crm.search"], grant: ({ principal }) => support.has(principal?.id ?? "") },
|
|
64
|
+
},
|
|
65
|
+
// …
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 0.33
|
|
69
|
+
createConnecta({
|
|
70
|
+
auth: clerkAuth({ publishableKey, secretKey }),
|
|
71
|
+
maintainers: ["user_alice"],
|
|
72
|
+
pools: {
|
|
73
|
+
support: { tools: ["tickets", "crm.search"], members: [...support] },
|
|
74
|
+
},
|
|
75
|
+
// …
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A reference is a bare principal id when the deployment has exactly one
|
|
80
|
+
interactive provider, `{ namespace, id }` otherwise, or `"agent:<name>"`.
|
|
81
|
+
Rewrite a predicate as the list it computed; ordinary code at construction
|
|
82
|
+
(`team.map(...)`) can build it. [Principals and operators](./auth.md#principals-visibility-and-operators)
|
|
83
|
+
owns the rules. A non-maintainer at `/mcp`, a non-member, and an undeclared
|
|
84
|
+
pool get the same 404.
|
|
85
|
+
|
|
86
|
+
### Preserve effective access
|
|
87
|
+
|
|
88
|
+
Before deleting `identity.connectorAccess`, compute what each audience could
|
|
89
|
+
actually use: its endpoint's tools intersected with that identity's old
|
|
90
|
+
connector permissions, including read-only guards. Write that effective set
|
|
91
|
+
into the new pool. Copying an old pool's broader `tools` verbatim would widen
|
|
92
|
+
access because 0.33 has no second intersection. A person previously admitted
|
|
93
|
+
to plain `/mcp` with restricted connector permissions needs a pool and a new
|
|
94
|
+
`/mcp/<pool>` client URL, not a maintainer entry. Maintainers have the whole
|
|
95
|
+
root catalog and administrative authority.
|
|
96
|
+
|
|
97
|
+
For example, suppose Alice administered the deployment, while Bob's old team
|
|
98
|
+
pool listed `crm` and `tickets` but his `connectorAccess` allowed only
|
|
99
|
+
`{ tool: "tickets.search", requireReadOnly: true }`. The new configuration is:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
maintainers: ["user_alice"],
|
|
103
|
+
pools: {
|
|
104
|
+
team: { tools: ["tickets.search"], members: ["user_bob"], readOnly: true },
|
|
105
|
+
},
|
|
106
|
+
// Delete identity and all grant callbacks; Bob connects at /mcp/team.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
A read-only guard applies to the whole new endpoint. If an audience previously
|
|
110
|
+
had both writable tools and guarded reads, separate those into explicit pools
|
|
111
|
+
with the appropriate `readOnly` choice rather than dropping the guard. Then
|
|
112
|
+
update clients to the intended endpoints.
|
|
113
|
+
|
|
114
|
+
## Management
|
|
115
|
+
|
|
116
|
+
The four management resolvers are gone, and the `identity` section with them.
|
|
117
|
+
[Management permissions](./auth.md#management-permissions) has the table:
|
|
118
|
+
interactive maintainers administer shared credentials and OAuth grants and
|
|
119
|
+
issue, rotate, and revoke agent tokens; maintainers read activity, within an
|
|
120
|
+
optional `readGate`; a pool member connects and disconnects their own account
|
|
121
|
+
on a personal connector in one of their pools. Agents manage nothing.
|
|
122
|
+
|
|
123
|
+
## Agents and their tokens
|
|
124
|
+
|
|
125
|
+
Machine clients are agents declared by name. Each has one token slot that a
|
|
126
|
+
maintainer issues, rotates, and revokes; nothing outside code adds, renames,
|
|
127
|
+
or rebinds one, and removing a name stops its token on the next request.
|
|
128
|
+
|
|
129
|
+
1. Add `agents: ["<name>", …]` and keep `accessTokens(storage)` on the same
|
|
130
|
+
storage and key namespace. Drop `maxActive`. A Node template deployment
|
|
131
|
+
from before 0.33 also copies `src/rotate-token.ts` and `src/adopt-token.ts`
|
|
132
|
+
from `node_modules/@zackbart/connecta/templates/node/src/`, deletes
|
|
133
|
+
`src/provision-token.ts`, and replaces the `provision-token` and `doctor`
|
|
134
|
+
scripts with the installed template's `rotate-token`, `adopt-token`, and
|
|
135
|
+
`doctor` (`connecta doctor --pool <pool>`) scripts.
|
|
136
|
+
2. Keep each machine's existing secret by adopting its token once:
|
|
137
|
+
`npm run adopt-token` lists the tokens that can be adopted, and
|
|
138
|
+
`npm run adopt-token -- <token-id> <agent>` binds one (Node template), or
|
|
139
|
+
call `AccessTokenManager.adopt(tokenId, agent)` from trusted provisioning.
|
|
140
|
+
Until then the token refuses.
|
|
141
|
+
3. A human-bound token, issued from the UI before 0.33, cannot be adopted and
|
|
142
|
+
stays refused. Issue that agent a new token with
|
|
143
|
+
`npm run --silent rotate-token -- <agent>` or from the Tokens page.
|
|
144
|
+
4. Clients that call the token routes send the slot's `revision`:
|
|
145
|
+
`POST /ui/access-tokens/<agent>` (issue, rotate) and `DELETE` (revoke) take
|
|
146
|
+
`{ "expect": "<revision>" }` from `GET /ui/access-tokens`, and a slot that
|
|
147
|
+
changed answers 409 `slot_changed`. A missing `expect` is 400.
|
|
148
|
+
`AccessTokenManager` takes the same optional `expect`.
|
|
149
|
+
|
|
150
|
+
### Programmatic token management
|
|
151
|
+
|
|
152
|
+
Trusted provisioning code that constructs its own `AccessTokenManager` must
|
|
153
|
+
pass the declared agents. The deployment's top-level `agents` does not bind a
|
|
154
|
+
separately constructed manager, so `new AccessTokenManager(storage)` knows no
|
|
155
|
+
agent and every `rotate` and `adopt` fails with `unknown_agent`.
|
|
156
|
+
|
|
157
|
+
| 0.32 | 0.33 |
|
|
158
|
+
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
|
159
|
+
| `new AccessTokenManager(storage, { maxActive })` | `new AccessTokenManager(storage, { agents })`, with the same names as the deployment's `agents` |
|
|
160
|
+
| `create(name, createdBy)` | `rotate(agent, issuedBy?, options?)`, returning `{ token, agent }`; the first rotation issues the slot's token |
|
|
161
|
+
| `rename(id, name)` | Removed; agents are named only in code |
|
|
162
|
+
| `revoke(tokenId, revokedBy)` | `revoke(agent, options?)`, returning the slot |
|
|
163
|
+
| `list()` of token records | `list()` of one slot per declared agent: `{ name, state, issuedAt?, revokedAt?, revision }` |
|
|
164
|
+
| (none) | `adopt(tokenId, agent, options?)` and `unboundTokens()` for tokens issued before agents |
|
|
165
|
+
|
|
166
|
+
`options` is `{ expect?, signal?, deadline? }`. `expect` is the slot
|
|
167
|
+
`revision` read from `list()`; when given, a slot that changed since refuses
|
|
168
|
+
with an `AccessTokenError` whose `code` is `slot_changed`, and nothing is
|
|
169
|
+
minted or written. Without it the calls behave as before.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { AccessTokenManager } from "@zackbart/connecta/auth/access-tokens";
|
|
173
|
+
|
|
174
|
+
const manager = new AccessTokenManager(storage, { agents: ["assistant"] });
|
|
175
|
+
const slot = (await manager.list()).find((agent) => agent.name === "assistant")!;
|
|
176
|
+
const { token } = await manager.rotate("assistant", "provisioning", { expect: slot.revision });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Rotation is an immediate cutover: the old secret refuses on its next request.
|
|
180
|
+
An agent has no principal, so personal connectors are absent for it.
|
|
181
|
+
[Agent tokens](./auth.md#agent-tokens-and-upgrading-from-earlier-tokens) owns
|
|
182
|
+
the slot, revision, and adoption contracts.
|
|
183
|
+
|
|
184
|
+
## Agent endpoints
|
|
185
|
+
|
|
186
|
+
Agents move from `/mcp/<pool>` to `/agent/<pool>`, and every human route
|
|
187
|
+
(`/mcp`, `/mcp/<pool>`, the operator pages and APIs, `/connect`, OAuth starts
|
|
188
|
+
and callbacks) refuses a `cta_` token before reading token storage. Agents are
|
|
189
|
+
never maintainers, so a machine that used plain `/mcp` moves to a pool that
|
|
190
|
+
holds its tools.
|
|
191
|
+
|
|
192
|
+
1. List `"agent:<name>"` in the `members` of each pool the agent uses.
|
|
193
|
+
2. Point its client at `https://<host>/agent/<pool>` with
|
|
194
|
+
`Authorization: Bearer cta_…` as its only credential.
|
|
195
|
+
3. Behind Cloudflare Access, add one self-hosted Access application per agent
|
|
196
|
+
hostname for `<host>/agent/*` with a single Bypass policy, beside the
|
|
197
|
+
Worker-level application, then drop the agent's `CF-Access-Client-Id` and
|
|
198
|
+
`CF-Access-Client-Secret` headers. The
|
|
199
|
+
[deploying guide](./deploying.md#agent-endpoints-behind-cloudflare-access)
|
|
200
|
+
has the steps and the checks to repeat until the change settles. Never
|
|
201
|
+
bypass any other path.
|
|
202
|
+
4. Run `connecta doctor --pool <pool>` with the agent's token; doctor checks
|
|
203
|
+
`/agent/<pool>` for a `cta_` token and requires `--pool`.
|
|
204
|
+
|
|
205
|
+
[Agent endpoints](./auth.md#agent-endpoints) owns the path, auth, and
|
|
206
|
+
membership contract.
|
|
207
|
+
|
|
208
|
+
## Google Workspace pool owners
|
|
209
|
+
|
|
210
|
+
In 0.32 a `cta_` token reached a Workspace connector's `subject` mapping,
|
|
211
|
+
which usually answered nothing for a caller without a principal. In 0.33 an
|
|
212
|
+
agent never reaches the mapping. It acts as the `owner` of the pool it came
|
|
213
|
+
through; in a pool without one the connector is absent for it, as a personal
|
|
214
|
+
connector is. People always act
|
|
215
|
+
as themselves through the mapping, in any pool. A fixed `subject` address
|
|
216
|
+
still applies to every caller.
|
|
217
|
+
|
|
218
|
+
If your mapping returned an address for a machine token, set that address as
|
|
219
|
+
the `owner` of the agent's pool. Replace per-person connectors such as
|
|
220
|
+
`gmail_alice` and `gmail_bob` with one `gmail()` whose `subject` is a mapping
|
|
221
|
+
function, and give each person's pool its `owner`.
|
|
222
|
+
[Pool owners](./auth.md#pool-owners-for-google-workspace) owns the rules.
|
|
223
|
+
|
|
224
|
+
## Operator contract v2
|
|
225
|
+
|
|
226
|
+
`GET /ui/api/config` serves `OperatorUiContractV2` (`schemaVersion: 2`) and
|
|
227
|
+
is the operator page's only gate; the v1 contract is removed:
|
|
228
|
+
|
|
229
|
+
- A signed-out caller gets 401 with the auth challenge.
|
|
230
|
+
- A `cta_` agent token gets the machine-only 401 Bearer challenge, as on every
|
|
231
|
+
human route, before token storage is read.
|
|
232
|
+
- A signed-in person who is neither a maintainer nor in any pool's `members`
|
|
233
|
+
gets the private JSON 404.
|
|
234
|
+
- A maintainer, or a person in a pool's `members`, gets 200 with their `role`.
|
|
235
|
+
|
|
236
|
+
The maintainer view carries `config`, `configSources`, `root`, every pool
|
|
237
|
+
with its owner and labelled members, every declared agent with its token slot
|
|
238
|
+
and `lastSeenAt`, and the connector × endpoint `matrix`. The member view
|
|
239
|
+
carries only that member's pools, without owner or members, and the
|
|
240
|
+
connectors in them, with their own connection and a `manage` naming the
|
|
241
|
+
actions they may take. `/ui/data` and `/ui/connectors/<id>` are
|
|
242
|
+
maintainer-only and give anyone else the same 404. Code that read v1's
|
|
243
|
+
`hasGrant`, `you.grants`, or `you.permissions` reads v2's `role`, `root`,
|
|
244
|
+
`pools`, `agents`, `matrix`, and `connectors` instead;
|
|
245
|
+
[the operator contract](./operator-ui.md#operator-data-contract) documents
|
|
246
|
+
every field. Read the published declarations in
|
|
247
|
+
`dist/operator-ui/contract.d.ts` inside the installed package for the exact
|
|
248
|
+
typed shape; `you.interactive` remains in v2. `OperatorUiContract` now names v2, and `OperatorViewer` and
|
|
249
|
+
`OperatorPool` are removed. `connecta doctor --config` reads v2 and prints a
|
|
250
|
+
maintainer's `config`; a member's view has none.
|
|
251
|
+
|
|
252
|
+
The connector auth routes accept what `manage` lists, with one recovery
|
|
253
|
+
exception: a stored credential that cannot be read can still be cleared,
|
|
254
|
+
although `manage` lists only `save` in that state.
|
|
255
|
+
`/ui/oauth/<id>` gates each verb on its own hooks: connecting needs
|
|
256
|
+
`startAuth`, `finishAuth`, and `verifyState`, and disconnecting needs only
|
|
257
|
+
`disconnectAuth`. A custom connector that offered connect with `startAuth`
|
|
258
|
+
alone adds the other two, without which its callback never completed. A
|
|
259
|
+
connector provisioned out of band, with `disconnectAuth` and no `startAuth`,
|
|
260
|
+
can now be disconnected. `DELETE /ui/credentials/<id>` refuses to clear a slot
|
|
261
|
+
that holds nothing, the caller's own partition for a personal connector, with
|
|
262
|
+
409 `{ "error": "no credential is stored to clear", "problem": "credential_required" }`.
|
|
263
|
+
The bundled operator UI needs no change.
|
|
264
|
+
|
|
265
|
+
## Security fix: pre-auth listen buffering
|
|
266
|
+
|
|
267
|
+
Versions 0.29.0 through 0.32.0 buffered the whole body of an unauthenticated
|
|
268
|
+
`POST` carrying `Mcp-Method: subscriptions/listen` to `/mcp` or `/mcp/<pool>`,
|
|
269
|
+
with no byte cap, before authentication and request admission. An anonymous
|
|
270
|
+
client could hold unbounded memory per request without taking an admission
|
|
271
|
+
permit. No credential or data was exposed; the risk is memory exhaustion.
|
|
272
|
+
|
|
273
|
+
0.33 authenticates first, then checks endpoint admission, before reading any
|
|
274
|
+
body on `/mcp`, `/mcp/<pool>`, and `/agent/<pool>`. A pre-dispatch read takes
|
|
275
|
+
at most 64 KiB; past that, connecta answers 413 with JSON-RPC code `-33006`
|
|
276
|
+
and stops reading. The new `/agent/*` Access bypass therefore does not reopen
|
|
277
|
+
the issue.
|
|
278
|
+
|
|
279
|
+
What operators should know:
|
|
280
|
+
|
|
281
|
+
- The fix ships in 0.33.0 only; there is no patch release for 0.29–0.32.
|
|
282
|
+
Upgrade.
|
|
283
|
+
- No configuration change is needed for the fix itself.
|
|
284
|
+
- A Worker behind a Worker-level Access application received only requests
|
|
285
|
+
Access admitted. A deployment reachable without edge authentication, such
|
|
286
|
+
as Node behind a public URL, was exposed. If you cannot upgrade at once,
|
|
287
|
+
cap request body size at your proxy or refuse unauthenticated requests that
|
|
288
|
+
carry `Mcp-Method: subscriptions/listen` there.
|
|
289
|
+
|
|
290
|
+
## Moving to the agent pool layout
|
|
291
|
+
|
|
292
|
+
A 0.32 agent usually reached `/mcp/<pool>` through a pool `grant` matching
|
|
293
|
+
its token, often with one connector per person and a second connection or
|
|
294
|
+
pool for each person's accounts. Move it to
|
|
295
|
+
[connecta's agent pool layout](./auth.md#organizing-an-agents-pools): one
|
|
296
|
+
connection; a base pool named after the agent with shared connectors and no
|
|
297
|
+
owner; one `<agent>-<user>` pool per person with their own connectors and
|
|
298
|
+
their Workspace address as `owner`.
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
// 0.32
|
|
302
|
+
pools: {
|
|
303
|
+
assistant: {
|
|
304
|
+
tools: ["crm", "tickets", "notes_alice", "gmail_alice", "notes_bob", "gmail_bob"],
|
|
305
|
+
grant: ({ actor }) => actor.kind === "access_token" && actor.id === assistantTokenId,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
// 0.33
|
|
310
|
+
agents: ["assistant"],
|
|
311
|
+
pools: {
|
|
312
|
+
assistant: { tools: base, members },
|
|
313
|
+
"assistant-alice": { tools: [...base, "notes_alice", "gmail"], owner: "alice@example.com", members },
|
|
314
|
+
"assistant-bob": { tools: [...base, "notes_bob", "gmail"], owner: "bob@example.com", members },
|
|
315
|
+
},
|
|
316
|
+
// with const base = ["crm", "tickets"], members = ["agent:assistant"];
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
1. Declare the agent in `agents` and adopt its token (see
|
|
320
|
+
[Agents and their tokens](#agents-and-their-tokens)).
|
|
321
|
+
2. Define the base pool's tool list once with the shared connectors, and
|
|
322
|
+
spread it into the base pool `<agent>` and one `<agent>-<user>` pool per
|
|
323
|
+
person, adding that person's connectors and `owner`. Each pool lists
|
|
324
|
+
`members: ["agent:<agent>"]`. If group chats should get different tools
|
|
325
|
+
depending on who is present, add tiers as the layout describes.
|
|
326
|
+
3. Collapse per-person Workspace connectors into one connector with a mapping
|
|
327
|
+
`subject`. Keep other per-person accounts as their own shared-auth
|
|
328
|
+
connector ids (`notes_alice`) and list each only in its person's pool.
|
|
329
|
+
4. Change the agent's client to one connection whose URL it sets per turn:
|
|
330
|
+
`/agent/<agent>-<user>` in a one-on-one with that user, otherwise the
|
|
331
|
+
layout's tier for who is present (`/agent/<agent>` with a single tier). Remove any second Connecta connection.
|
|
332
|
+
5. Add this line to your deployment's own AGENTS.md (or its CLAUDE.md
|
|
333
|
+
equivalent), so agents find all operator procedures after the upgrade:
|
|
334
|
+
|
|
335
|
+
```text
|
|
336
|
+
For every Connecta operator task, start at node_modules/@zackbart/connecta/documentation/README.md#operator-tasks
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Verify
|
|
340
|
+
|
|
341
|
+
1. Start the deployment; construction errors name each removed option's
|
|
342
|
+
replacement.
|
|
343
|
+
2. Sign in as a maintainer and confirm plain `/mcp` and the operator UI work.
|
|
344
|
+
3. Run `connecta doctor --pool <pool>` with each agent's token, and confirm
|
|
345
|
+
the same token at `/mcp` is refused.
|
|
346
|
+
4. Confirm a pool member sees exactly that pool's tools at `/mcp/<pool>`.
|
|
347
|
+
5. For Workspace, call a read in an agent's personal pool and confirm it acts
|
|
348
|
+
as the pool's owner. In the base pool, confirm Workspace connectors are
|
|
349
|
+
absent: search finds none of their tools and a call fails
|
|
350
|
+
`unknown_address`. The same holds in any pool without an owner, even one
|
|
351
|
+
that lists a Workspace connector with a subject mapping.
|
|
352
|
+
6. Confirm each `<agent>-<user>` pool lists no other person in `members`.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Migration to 0.34
|
|
2
|
+
|
|
3
|
+
Use this guide when upgrading an existing deployment from 0.33 to 0.34.
|
|
4
|
+
[Deployment upgrades](./deploying.md#upgrade-an-existing-deployment) cover the
|
|
5
|
+
general procedure; [the agent index](./README.md) routes other work. Upgrades
|
|
6
|
+
from earlier versions must also apply the intervening migrations.
|
|
7
|
+
|
|
8
|
+
No storage migration, token rotation or required configuration field is added.
|
|
9
|
+
Review the following changes before updating the package pin.
|
|
10
|
+
|
|
11
|
+
## Operator links and browser titles
|
|
12
|
+
|
|
13
|
+
Maintainers now land on Overview at `/`. Endpoints is at `/endpoints`, and
|
|
14
|
+
`/access` redirects to `/endpoints`. Update saved links and browser automation
|
|
15
|
+
that expected Endpoints at `/`, including row links: `/#mcp/support` becomes
|
|
16
|
+
`/endpoints#mcp/support`. Members still get their member page on every operator
|
|
17
|
+
page path. MCP client URLs (`/mcp`, `/mcp/<pool>` and `/agent/<pool>`) do not change.
|
|
18
|
+
|
|
19
|
+
The default `branding.pageTitle` joins `productName` and `ownerName` with a
|
|
20
|
+
middle dot, for example `Assistant · Example team`. A title set explicitly in
|
|
21
|
+
`branding.pageTitle` is unchanged. If automation requires the previous title,
|
|
22
|
+
set that exact title explicitly; otherwise update its expectation. With no
|
|
23
|
+
owner name, the default remains the product name alone.
|
|
24
|
+
|
|
25
|
+
Activity links can use `failed` or `succeeded` for the outcome filter. Older
|
|
26
|
+
exact outcome values in saved links still work. See the
|
|
27
|
+
[operator UI guide](./operator-ui.md) for the new pages and controls.
|
|
28
|
+
|
|
29
|
+
## Workspace connectors in agent pools
|
|
30
|
+
|
|
31
|
+
An agent in a pool without an `owner` no longer sees a Google Workspace
|
|
32
|
+
connector whose `subject` is a mapping function. Previously the connector
|
|
33
|
+
appeared but its calls failed `auth_required`; now discovery, search, describe,
|
|
34
|
+
skills and code mode omit it, and a call by address fails `unknown_address`.
|
|
35
|
+
Paging a saved program return also rechecks visibility, including contributions
|
|
36
|
+
to search counts and guidance.
|
|
37
|
+
|
|
38
|
+
Keep a shared or group pool owner-less when it must not act as one person.
|
|
39
|
+
If an agent needs to act as Alice, put that access in her personal pool and set
|
|
40
|
+
`owner: "alice@example.com"` there, using the Workspace account she is intended
|
|
41
|
+
to use. Confirm the pool's tools and members before adding an owner. Follow
|
|
42
|
+
[the agent pool layout](./auth.md#organizing-an-agents-pools) for the full
|
|
43
|
+
configuration. People in the same pool, agents in owned pools and connectors
|
|
44
|
+
with a fixed `subject` address keep their behavior.
|
|
45
|
+
|
|
46
|
+
## Optional settings and development tooling
|
|
47
|
+
|
|
48
|
+
Pool `description` is optional; omit it to preserve the existing MCP
|
|
49
|
+
instructions. A custom activity store can optionally return
|
|
50
|
+
`describe().optionSources.retentionDays` as `"config"` or `"default"` to mark
|
|
51
|
+
whether code explicitly set retention. Existing implementations need no
|
|
52
|
+
change; Settings marks retention as explicitly set only when the store
|
|
53
|
+
reports `"config"`.
|
|
54
|
+
|
|
55
|
+
The repository now uses pnpm for development and CI. Installed deployments and
|
|
56
|
+
the Node template continue to use npm; keep their npm commands and lockfiles.
|
|
57
|
+
Deployments without the operator UI or mapping-subject Workspace connectors in
|
|
58
|
+
owner-less agent pools can skip the UI and discovery steps above.
|
|
59
|
+
|
|
60
|
+
## Verify
|
|
61
|
+
|
|
62
|
+
After upgrading, open `/` as a maintainer and confirm Overview loads, then open
|
|
63
|
+
`/endpoints` and a saved endpoint row link. Check any automation that reads the
|
|
64
|
+
browser title. With an agent's existing credentials, discover tools in its base
|
|
65
|
+
and personal pools and perform a bounded Workspace read in the owned pool.
|
|
66
|
+
Owner-less pools must omit mapping-subject Workspace connectors. Run
|
|
67
|
+
`connecta doctor` as described in [deployment upgrades](./deploying.md#upgrade-an-existing-deployment).
|
|
@@ -126,7 +126,9 @@ mean the provider applied it; inspect state or ask for the missing decision
|
|
|
126
126
|
before issuing it again. [Retry semantics](./code-mode.md#retry-semantics) and
|
|
127
127
|
[writes](./code-mode.md#writes) describe program outcomes and write accounting.
|
|
128
128
|
|
|
129
|
-
##
|
|
129
|
+
## Contributor reference
|
|
130
|
+
|
|
131
|
+
### Implementation and evidence
|
|
130
132
|
|
|
131
133
|
The endpoint's built-in [usage skill](https://github.com/zackbart/connecta/blob/main/src/skills.ts)
|
|
132
134
|
is maintained alongside [meta-tools](https://github.com/zackbart/connecta/blob/main/src/meta-tools.ts)
|