@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
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
Use this guide to change connectors in deployment configuration.
|
|
4
4
|
[All agent routes](./README.md) are in the index.
|
|
5
5
|
|
|
6
|
+
## Make one connection read-only
|
|
7
|
+
|
|
8
|
+
Every connector accepts optional `readOnly?: boolean`, defaulting to `false`:
|
|
9
|
+
`remoteMcp("notes", { url: "https://notes.example.com/mcp", auth: { type: "headers", headers: {} }, readOnly: true })`,
|
|
10
|
+
`api("tickets", { readOnly: true, tools: [] })`, and maintained providers such as
|
|
11
|
+
`stripe("billing", { purpose: "Revenue reports", auth: { type: "oauth" }, readOnly: true })`.
|
|
12
|
+
A custom `Connector` sets `readOnly: true` on its connector object.
|
|
13
|
+
|
|
14
|
+
Only tools classified read appear in search and describe, and every write or
|
|
15
|
+
unclassified tool call is refused before dispatch on every endpoint, including
|
|
16
|
+
maintainers' plain `/mcp`, programs, `call_tool`, and `call_destructive_tool`.
|
|
17
|
+
Effective read-only is endpoint `readOnly` OR connector `readOnly`; connector
|
|
18
|
+
`false` never widens an endpoint. Existing pool/deployment inheritance is unchanged.
|
|
19
|
+
Guides may still name unavailable tools. The operator UI shows a Read-only badge.
|
|
20
|
+
|
|
6
21
|
## Add or remove a connector
|
|
7
22
|
|
|
8
23
|
Connectors are code-owned. Edit the deployment's `src/connecta.config.ts`;
|
|
@@ -19,8 +34,10 @@ To add one:
|
|
|
19
34
|
3. Add the id (all its tools) or exact `id.tool` addresses to the intended
|
|
20
35
|
pools' `tools`. Maintainers at plain `/mcp` already see the whole catalog.
|
|
21
36
|
Follow [the agent pool layout](./auth.md#organizing-an-agents-pools) for an
|
|
22
|
-
agent: shared
|
|
23
|
-
|
|
37
|
+
agent: a shared connector goes in the tool list of the lowest tier whose
|
|
38
|
+
entire audience is authorized to use it (billing restricted to leads goes
|
|
39
|
+
in the leads tier); per-user connectors and Workspace connectors with a
|
|
40
|
+
mapping `subject` go only in personal pools.
|
|
24
41
|
4. Run the deployment's `npm run typecheck` (Worker copies: `tsc --noEmit`),
|
|
25
42
|
restart Node or redeploy the Worker, and
|
|
26
43
|
[connect its credential](./auth.md#connect-a-shared-credential) if needed.
|
|
@@ -38,14 +55,15 @@ const crm = remoteMcp("crm", {
|
|
|
38
55
|
authScope: "shared",
|
|
39
56
|
auth: { type: "credential", credential: { label: "CRM API token" } },
|
|
40
57
|
});
|
|
41
|
-
const
|
|
42
|
-
// connectors: [crm, tickets], agents: ["assistant"],
|
|
58
|
+
const base = ["crm", "tickets"]; // the agent's lowest tier
|
|
59
|
+
// connectors: [crm, tickets], agents: ["assistant"],
|
|
60
|
+
// pools: { assistant: { tools: base, members: ["agent:assistant"] } }
|
|
43
61
|
```
|
|
44
62
|
|
|
45
63
|
To remove `crm`, first disconnect its OAuth grant or delete its saved
|
|
46
64
|
credential while the connector still exists, using a signed-in maintainer
|
|
47
65
|
for shared auth. Each person disconnects their own personal account. Remove
|
|
48
|
-
`crm` and all `crm.*` addresses from every pool (including
|
|
66
|
+
`crm` and all `crm.*` addresses from every pool (including tier tool lists),
|
|
49
67
|
then remove its entry from `connectors`, unused imports, and unused secret
|
|
50
68
|
configuration. Remove any pools left with no tools (empty `tools` fails
|
|
51
69
|
construction), and update or retire clients pointing at those pools. Typecheck, restart/redeploy, run doctor, and confirm discovery
|
|
@@ -89,10 +107,10 @@ const tracker = linear("tracker", {
|
|
|
89
107
|
```
|
|
90
108
|
|
|
91
109
|
Linear defaults to OAuth; its read-only endpoint requests only the downstream
|
|
92
|
-
read scope. That is separate from
|
|
110
|
+
read scope. That is separate from Connecta endpoint or connector `readOnly`. Read the installed `dist/providers/linear/index.d.ts` for API-key framing,
|
|
93
111
|
workspace instructions, and admission choices.
|
|
94
112
|
|
|
95
|
-
The [human README](../README.md#what-you-can-
|
|
113
|
+
The [human README](../README.md#what-you-can-connect) lists available
|
|
96
114
|
integrations. In the installed package, `dist/providers/<name>/index.d.ts`
|
|
97
115
|
and its imported declarations describe each factory's options. After adding
|
|
98
116
|
a connector, use `search_tools` to discover its current tools and `connecta.skill("connector:<id>")` in `execute_code`
|
|
@@ -104,6 +122,34 @@ pool's `owner` instead ([auth guide](auth.md#pool-owners-for-google-workspace)).
|
|
|
104
122
|
A request must not choose its own account. Gmail never sends mail, and Drive never permanently
|
|
105
123
|
deletes files.
|
|
106
124
|
|
|
125
|
+
Intercom uses `intercom()` from `@zackbart/connecta/providers/intercom`:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { intercom } from "@zackbart/connecta/providers/intercom";
|
|
129
|
+
|
|
130
|
+
const support = intercom("support", {
|
|
131
|
+
purpose: "Customer support triage",
|
|
132
|
+
region: "eu",
|
|
133
|
+
});
|
|
134
|
+
// Add support to the deployment's connectors array.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Set `region` explicitly to `"us"` or `"eu"` to match the workspace; AU is
|
|
138
|
+
unsupported. OAuth defaults to personal ownership because it acts as the
|
|
139
|
+
authorizing teammate. Non-loopback OAuth callbacks require an Intercom Customer
|
|
140
|
+
Support allowlist entry for the deployment's redirect URI before authorization
|
|
141
|
+
can complete. Alternatively, set `auth: { type: "credential" }` and connect an
|
|
142
|
+
API token through the [shared credential procedure](./auth.md#connect-a-shared-credential);
|
|
143
|
+
token auth defaults to shared ownership. Fin Operator tools require OAuth,
|
|
144
|
+
workspace availability and teammate permission. Their expected absence adds no
|
|
145
|
+
missing-tool drift; missing core tools still do. Schema coverage reports all
|
|
146
|
+
present reviewed tools as unpinned. Article operations, internal
|
|
147
|
+
notes and Operator actions can write; a note can reopen a snoozed conversation.
|
|
148
|
+
Use a read-only endpoint when writes are unwanted. Catalog review is based on
|
|
149
|
+
[Intercom's docs](https://developers.intercom.com/docs/guides/mcp), with unpinned
|
|
150
|
+
schemas; inspect live tools and the runtime guide before acting on sensitive
|
|
151
|
+
customer data.
|
|
152
|
+
|
|
107
153
|
Wispr Flow uses OAuth only through `wisprFlow()` from
|
|
108
154
|
`@zackbart/connecta/providers/wispr-flow`. It reaches the authorized account's
|
|
109
155
|
Notetaker content, with personal and work accounts holding separate data.
|
|
@@ -113,6 +159,8 @@ reviewed classification includes task writes listed in Claude's directory even
|
|
|
113
159
|
though Wispr's public help describes read-only access; discover the account's
|
|
114
160
|
actual tools before assuming those writes exist.
|
|
115
161
|
|
|
162
|
+
For minimal Stripe OAuth/API-key examples with and without connector `readOnly`, see [Stripe setup](./provider-auth-migration.md#stripe). With an API key, prefer a restricted `rk_` key with only read permissions for read-only access; Stripe enforces it too.
|
|
163
|
+
|
|
116
164
|
Stripe requires `auth`: `{ type: "oauth" }` reaches Stripe's hosted MCP
|
|
117
165
|
server, and `{ type: "apiKey" }` with `mode` reaches Connecta's REST connector
|
|
118
166
|
with an operator-managed key. Notion requires `auth` the same way:
|
|
@@ -218,6 +266,15 @@ for precedence, schema digests, and `unlisted: "hide"`. Review the behavior
|
|
|
218
266
|
before declaring a read, and do not use classification to bypass provider target
|
|
219
267
|
scopes or caller grants.
|
|
220
268
|
|
|
269
|
+
A reviewed name is required by default for drift comparison. Mark a tool
|
|
270
|
+
`optional: true` only when the downstream can omit it because of plan,
|
|
271
|
+
feature, or permission gating. For example, a remote `crm` connector can use
|
|
272
|
+
`classify: { tools: { list_accounts: "read", list_customers: { verdict: "read",
|
|
273
|
+
optional: true } } }`. Absence of `list_customers` then adds no unserved count;
|
|
274
|
+
when present, its verdict, annotations, and any pinned schema are still checked.
|
|
275
|
+
A missing schema pin means that tool's schema has not been verified against a
|
|
276
|
+
review. Zero schema changes alone does not establish a matching schema.
|
|
277
|
+
|
|
221
278
|
## A hand-written HTTP capability
|
|
222
279
|
|
|
223
280
|
Define only the operations the deployment needs. Every `api()` tool needs a
|
|
@@ -35,7 +35,7 @@ is annotated `readOnlyHint: true, destructiveHint: false` on a read-only
|
|
|
35
35
|
endpoint, and `readOnlyHint: false, destructiveHint: true` on a writable one.
|
|
36
36
|
Approval belongs to the client. A read-only endpoint keeps all six tools, omits
|
|
37
37
|
write tools from `search_tools` results and describe, and refuses every write
|
|
38
|
-
call
|
|
38
|
+
call. Connector `readOnly` applies the same restriction on every endpoint; effective read-only is endpoint OR connector. Neither setting changes connector visibility or read grants. Guides
|
|
39
39
|
(`queryAnalysis.guideSummary`, `skills`, `connecta.skill`, skill
|
|
40
40
|
`resources/read`) are documentation and may name tools the endpoint refuses. The four tools with structured-only success results
|
|
41
41
|
advertise `outputSchema`. The two direct-call tools omit it because their default
|
|
@@ -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
|
|
|
@@ -221,7 +221,7 @@ declared schema exactly as they do for compact.
|
|
|
221
221
|
A connector may attach a deployment-owned guide as markdown, keeping the
|
|
222
222
|
original `usageGuide: string` configuration, or as
|
|
223
223
|
`{ content, summary?, required? }` — which registers no connector and creates no
|
|
224
|
-
shared runtime template. The
|
|
224
|
+
shared runtime template. The skills catalog prepends Agent Skills frontmatter to
|
|
225
225
|
`content` and preserves the guide body, including its whitespace.
|
|
226
226
|
`summary` is normalized and refuses construction over 120 characters; absent,
|
|
227
227
|
Connecta derives the same bounded fallback the skills listing uses from the
|
|
@@ -253,14 +253,22 @@ A connector-scoped lexical miss keeps that connector's guide metadata under
|
|
|
253
253
|
contains no endpoint vocabulary: a required guide stays discoverable instead of
|
|
254
254
|
disappearing with the zero-tool page.
|
|
255
255
|
|
|
256
|
-
Two built-in skills are byte-identical across deployments. `usage`
|
|
257
|
-
|
|
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
|
|
258
258
|
differences, and repair guidance; `investigate` is on-demand guidance for
|
|
259
259
|
purchase verification, experiment checks, and customer or deployment
|
|
260
260
|
investigations — shared guidance, not a saved workflow and not a source of
|
|
261
261
|
deployment-specific ids. The always-loaded instructions and tool definitions keep
|
|
262
262
|
route selection, the fail-closed boundary, and the minimum guest syntax, so a
|
|
263
|
-
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
|
|
264
272
|
guides stay scoped to the deployment that listed them even when two deployments
|
|
265
273
|
use identical content, and a deployment with no connector guides receives none of
|
|
266
274
|
the short conditional guide pointers in its tool definitions.
|
|
@@ -386,7 +394,11 @@ including dependencies inherited from pages read during the run. Downstream skil
|
|
|
386
394
|
resource content requires a whole-connector grant on every page. Local guides and catalog
|
|
387
395
|
metadata require the contributing connector or tool to remain visible, including
|
|
388
396
|
caught catalog-load errors, unknown-tool failures, and recovery messages that
|
|
389
|
-
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
|
|
390
402
|
require matching identity, origin and endpoint, current auth, and pool membership. A page must
|
|
391
403
|
match those bindings and pass current auth, connector/tool grants, pool membership,
|
|
392
404
|
and read-only checks. Old entries lacking bindings fail closed. A random UUID is a
|
|
@@ -394,7 +406,7 @@ handle, never an access grant.
|
|
|
394
406
|
|
|
395
407
|
New entries store UTF-8 bytes in a base64 envelope split across storage keys,
|
|
396
408
|
48 KiB of result text per chunk — widening past roughly 1.5 MB so no result
|
|
397
|
-
occupies more than
|
|
409
|
+
occupies more than 32 keys, because every chunk is also a write. `connecta.result`
|
|
398
410
|
reads and decodes only the chunks a page covers plus a few boundary bytes, so
|
|
399
411
|
paging a 1.2 MB result costs the same per page as paging a 300 KB one; it
|
|
400
412
|
neither encodes nor reads the whole result per page. Entries without the required identity and invocation bindings fail closed,
|
|
@@ -426,7 +438,7 @@ JSON; everything after the first newline is the preview. `structuredContent`
|
|
|
426
438
|
is absent, so the notice cannot hide the preview in clients that prefer it:
|
|
427
439
|
|
|
428
440
|
```text
|
|
429
|
-
{"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 })"}}}
|
|
430
442
|
{"ts":"2026-09-16T21:31:11.759Z","actor":"sam.ortiz@example.com",…
|
|
431
443
|
```
|
|
432
444
|
|
|
@@ -458,7 +470,7 @@ characters. One that rejects outright — Claude Code with
|
|
|
458
470
|
`MAX_MCP_OUTPUT_TOKENS` set below 12,500 — needs a cap under twice its limit.
|
|
459
471
|
|
|
460
472
|
The preview is the head of what `connecta.result` pages, cut on a character
|
|
461
|
-
boundary, so `nextAction.arguments.
|
|
473
|
+
boundary, so the `offset` in `nextAction.arguments.code` continues exactly where it stops. A
|
|
462
474
|
lone text block — including the one synthesized from `structuredContent` — is
|
|
463
475
|
measured, stashed, previewed, and paged as its text: wrapping it in a content
|
|
464
476
|
envelope added nothing but a second layer of JSON escaping, so a JSON payload
|
|
@@ -521,7 +533,7 @@ cap and a 32 KiB program bridge ceiling, with UTF-8 alignment and forward progre
|
|
|
521
533
|
returning pages unchanged can hit the program result cap. The admitted subject
|
|
522
534
|
and principal, endpoint/pool, request origin, connector, and tool bindings must
|
|
523
535
|
match. The host rechecks current access and whether the endpoint is read-only
|
|
524
|
-
before returning each page; a read-only endpoint cannot read a write stash.
|
|
536
|
+
before returning each page; a read-only endpoint or connector cannot read a write stash.
|
|
525
537
|
Caller arguments select no partition. A read-only endpoint never runs a write,
|
|
526
538
|
so every write result it could page came from a writable endpoint.
|
|
527
539
|
|
|
@@ -682,8 +694,8 @@ replay. Downstream payloads and input responses are capped at 64 KiB, with at
|
|
|
682
694
|
most 16 inputs and 256 characters per downstream key. The combined private
|
|
683
695
|
state history is also capped at 64 KiB. Sealed wire state is
|
|
684
696
|
capped at 128 KiB. `input_required_invalid`, `input_required_limit`,
|
|
685
|
-
|
|
686
|
-
|
|
697
|
+
and `input_required_unsupported` are non-retryable typed failures;
|
|
698
|
+
`input_required_round_limit` is an untyped failure with `retryable: false`.
|
|
687
699
|
|
|
688
700
|
Programs and in-process meta-tools return `input_required_unsupported`, with a
|
|
689
701
|
`nextAction` naming the equivalent direct call and bounded original arguments.
|
|
@@ -965,12 +977,14 @@ Handlers and schemas live in [meta-tools.ts](https://github.com/zackbart/connect
|
|
|
965
977
|
[Catalog service](https://github.com/zackbart/connecta/blob/main/src/catalog-service.ts)
|
|
966
978
|
and [invocation](https://github.com/zackbart/connecta/blob/main/src/invocation.ts)
|
|
967
979
|
serve both direct tools and programs. [Skills](https://github.com/zackbart/connecta/blob/main/src/skills.ts)
|
|
968
|
-
owns the supplied usage instructions
|
|
980
|
+
owns the supplied usage instructions, each endpoint's instructions
|
|
981
|
+
(`endpointInstructions`), and connector guides.
|
|
969
982
|
|
|
970
983
|
[Schema tests](https://github.com/zackbart/connecta/blob/main/test/meta-tool-schemas.test.ts),
|
|
971
984
|
[search tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-search.test.ts),
|
|
972
985
|
[call tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-call.test.ts),
|
|
973
986
|
[skills tests](https://github.com/zackbart/connecta/blob/main/test/skills-extension.test.ts),
|
|
974
|
-
|
|
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)
|
|
975
989
|
check the published contract. Guest parity and clause evidence are in
|
|
976
990
|
[code-mode verification](./code-mode.md#verification).
|
|
@@ -127,7 +127,12 @@ maintainer issues, rotates, and revokes; nothing outside code adds, renames,
|
|
|
127
127
|
or rebinds one, and removing a name stops its token on the next request.
|
|
128
128
|
|
|
129
129
|
1. Add `agents: ["<name>", …]` and keep `accessTokens(storage)` on the same
|
|
130
|
-
storage and key namespace. Drop `maxActive`.
|
|
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.
|
|
131
136
|
2. Keep each machine's existing secret by adopting its token once:
|
|
132
137
|
`npm run adopt-token` lists the tokens that can be adopted, and
|
|
133
138
|
`npm run adopt-token -- <token-id> <agent>` binds one (Node template), or
|
|
@@ -205,7 +210,8 @@ membership contract.
|
|
|
205
210
|
In 0.32 a `cta_` token reached a Workspace connector's `subject` mapping,
|
|
206
211
|
which usually answered nothing for a caller without a principal. In 0.33 an
|
|
207
212
|
agent never reaches the mapping. It acts as the `owner` of the pool it came
|
|
208
|
-
through
|
|
213
|
+
through; in a pool without one the connector is absent for it, as a personal
|
|
214
|
+
connector is. People always act
|
|
209
215
|
as themselves through the mapping, in any pool. A fixed `subject` address
|
|
210
216
|
still applies to every caller.
|
|
211
217
|
|
|
@@ -303,24 +309,26 @@ pools: {
|
|
|
303
309
|
// 0.33
|
|
304
310
|
agents: ["assistant"],
|
|
305
311
|
pools: {
|
|
306
|
-
assistant,
|
|
307
|
-
"assistant-alice": {
|
|
308
|
-
"assistant-bob": {
|
|
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 },
|
|
309
315
|
},
|
|
310
|
-
// with const
|
|
316
|
+
// with const base = ["crm", "tickets"], members = ["agent:assistant"];
|
|
311
317
|
```
|
|
312
318
|
|
|
313
319
|
1. Declare the agent in `agents` and adopt its token (see
|
|
314
320
|
[Agents and their tokens](#agents-and-their-tokens)).
|
|
315
|
-
2. Define the base pool
|
|
316
|
-
|
|
317
|
-
|
|
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.
|
|
318
326
|
3. Collapse per-person Workspace connectors into one connector with a mapping
|
|
319
327
|
`subject`. Keep other per-person accounts as their own shared-auth
|
|
320
328
|
connector ids (`notes_alice`) and list each only in its person's pool.
|
|
321
329
|
4. Change the agent's client to one connection whose URL it sets per turn:
|
|
322
|
-
`/agent/<agent>-<user>` in a one-on-one with that user,
|
|
323
|
-
|
|
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.
|
|
324
332
|
5. Add this line to your deployment's own AGENTS.md (or its CLAUDE.md
|
|
325
333
|
equivalent), so agents find all operator procedures after the upgrade:
|
|
326
334
|
|
|
@@ -339,6 +347,6 @@ pools: {
|
|
|
339
347
|
5. For Workspace, call a read in an agent's personal pool and confirm it acts
|
|
340
348
|
as the pool's owner. In the base pool, confirm Workspace connectors are
|
|
341
349
|
absent: search finds none of their tools and a call fails
|
|
342
|
-
`unknown_address`.
|
|
343
|
-
|
|
350
|
+
`unknown_address`. The same holds in any pool without an owner, even one
|
|
351
|
+
that lists a Workspace connector with a subject mapping.
|
|
344
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).
|
|
@@ -24,7 +24,7 @@ descriptions. On a writable endpoint, programs and `call_destructive_tool` may
|
|
|
24
24
|
write within the caller's grants, and `execute_code` is annotated as a write;
|
|
25
25
|
the client controls approval. On a read-only endpoint, search and describe omit
|
|
26
26
|
write tools and every write call is refused with `read_only_endpoint`, even one
|
|
27
|
-
a connector guide names; tell the user rather than looking for another route. Never change pool, identity, or `readOnly` to work
|
|
27
|
+
a connector guide names; tell the user rather than looking for another route. A connector marked Read-only serves only read-classified tools even on a writable endpoint; writes and unclassified tools are omitted from search/describe and refused on every call path. Endpoint and connector restrictions compose by OR. Never change pool, identity, or `readOnly` to work
|
|
28
28
|
around a refusal.
|
|
29
29
|
[Routing between the call surfaces](./meta-tools.md#routing-between-the-call-surfaces)
|
|
30
30
|
owns the full decision rules.
|
|
@@ -13,15 +13,19 @@ or browser evidence. Read the [operator UI contract](./operator-ui.md) first.
|
|
|
13
13
|
`test/fixtures/operator-visual.ts` owns the visual data. It builds the
|
|
14
14
|
maintainer's contract v2, live overlay, and legacy auth details through the
|
|
15
15
|
current server routes, using the types in `src/operator-ui/contract.ts` and
|
|
16
|
-
`model.ts`; its people carry labels from a fake directory.
|
|
16
|
+
`model.ts`; its people carry labels from a fake directory. It sets a few
|
|
17
|
+
limits, an activity retention, one classification override and a GitHub call
|
|
18
|
+
admission rule in code, so Settings and the connector Configuration card show
|
|
19
|
+
set values beside defaults; Slack describes OAuth facts (scope, endpoints, an
|
|
20
|
+
API origin, client secret, PKCE) for the card's auth row. Its Tokens agents
|
|
17
21
|
(`claude-desktop` issued, `triage` none, `nightly` revoked) are real slots
|
|
18
22
|
committed through the deployment's own store, so the contract's `agents` and
|
|
19
23
|
`GET /ui/access-tokens` agree, revisions included; only their timestamps and
|
|
20
24
|
last seen are fixed. The restricted state is the member fixture from
|
|
21
25
|
`test/fixtures/operator-contract-v2.ts`, and the endpoint and matrix variants
|
|
22
26
|
use its maintainer fixture, which covers every runs-as kind and agent pools,
|
|
23
|
-
with `test/fixtures/operator-console.ts` crowding the team pool so its
|
|
24
|
-
|
|
27
|
+
with `test/fixtures/operator-console.ts` crowding the team pool so its avatar
|
|
28
|
+
stack collapses to "+3".
|
|
25
29
|
Collections use their corresponding browser types.
|
|
26
30
|
`test/operator-visual-fixtures.test.ts` validates contract versions, connector
|
|
27
31
|
IDs, granted tools, matrix cells, classification, provenance, and secret
|
|
@@ -31,7 +35,10 @@ server serializer.
|
|
|
31
35
|
|
|
32
36
|
`test/browser/operator-visual.spec.ts` intercepts API reads and serves the real
|
|
33
37
|
hashed assets and HTML with the production CSP. Loading responses wait on an
|
|
34
|
-
explicit gate. Error responses use fixed HTTP failures.
|
|
38
|
+
explicit gate. Error responses use fixed HTTP failures. The map trace focuses its
|
|
39
|
+
node instead of hovering it: the capture scrolls to the top first, which would
|
|
40
|
+
move the node out from under a pointer and drop the highlight, so the test waits
|
|
41
|
+
for `.map-svg.tracing` before capturing. Screenshots use a fixed
|
|
35
42
|
clock, UTC, en-US, locally hosted fonts, disabled animations, hidden carets,
|
|
36
43
|
and a 1280 × 900 viewport. Narrow fixtures use 390 × 844. Every test waits for
|
|
37
44
|
its state and fonts before capturing.
|
|
@@ -39,11 +46,12 @@ its state and fonts before capturing.
|
|
|
39
46
|
## Page and state matrix
|
|
40
47
|
|
|
41
48
|
Each row runs empty, loading, error, populated, and restricted-viewer fixtures
|
|
42
|
-
in both light and dark themes, for
|
|
49
|
+
in both light and dark themes, for 90 screenshots. The restricted viewer is a
|
|
43
50
|
pool member, who gets the member page, with no tabs, on every path.
|
|
44
51
|
|
|
45
52
|
| Page or tab | Empty state | Loading/error request | Restricted viewer (member) |
|
|
46
53
|
| -------------------- | ------------------------------------ | --------------------- | ----------------------------------- |
|
|
54
|
+
| Overview | No connectors: nothing to map | Config | Member page at `/` |
|
|
47
55
|
| Endpoints | Root endpoint without tools | Config | Member page: endpoints, accounts |
|
|
48
56
|
| Connectors | No connectors | Config | Member page, no connector request |
|
|
49
57
|
| Connector Tools | Empty catalog | Config | Member page, no connector request |
|
|
@@ -53,24 +61,26 @@ pool member, who gets the member page, with no tabs, on every path.
|
|
|
53
61
|
| Activity | No calls | Activity | Member page, no activity request |
|
|
54
62
|
| Settings | Snapshot without connectors or pools | Config | Member page, no configuration facts |
|
|
55
63
|
|
|
56
|
-
Populated
|
|
64
|
+
Populated Overview has a failing connector, auth and credential problems
|
|
65
|
+
and a tokenless agent in its attention list. Populated Endpoints shows who can use each endpoint; populated Tokens has one
|
|
57
66
|
agent per slot state, with pools and last seen.
|
|
58
67
|
|
|
59
|
-
Another
|
|
68
|
+
Another 60 screenshots cover the Overview map on the frozen v2 maintainer, one
|
|
69
|
+
person's trace, and the narrow map list; configured, mismatched, unreadable and multifield
|
|
60
70
|
credentials; all three OAuth client mechanisms; clean and unobserved drift;
|
|
61
|
-
historical Activity; client setup; schema dialog; read/write filters;
|
|
62
|
-
|
|
71
|
+
historical Activity; client setup; schema dialog; read/write filters; narrow
|
|
72
|
+
Settings; narrow sign-in and Connection; deployment branding; `/mcp`
|
|
63
73
|
and an agent pool expanded with every runs-as kind; command search; narrow
|
|
64
74
|
endpoints; the connector × endpoint matrix, one cell's open popover, and the
|
|
65
75
|
narrow matrix; a new token's one-time secret dialog; and the member page, wide
|
|
66
76
|
and narrow. `endpoints-expanded` and `narrow-endpoints` include the team row
|
|
67
|
-
collapsed to "+3
|
|
77
|
+
collapsed to "+3". The base
|
|
68
78
|
populated Activity fixture includes grouped calls, an ungrouped legacy row,
|
|
69
79
|
classification/result size/client facts, and a catalog-change event.
|
|
70
80
|
|
|
71
81
|
`test/browser/operator-matrix.spec.ts` serves the frozen v2 fixtures to the
|
|
72
|
-
real shell. It covers the matrix's table semantics, cell names
|
|
73
|
-
labels, popover keyboard behavior, placement at a short viewport, the in-flow
|
|
82
|
+
real shell. It covers the matrix's table semantics, cell names, status dots,
|
|
83
|
+
runs-as labels in each kind's popover, popover keyboard behavior, placement at a short viewport, the in-flow
|
|
74
84
|
fallback without CSS anchors, and the absence of `style` attributes, agent URLs on Endpoints, the member page (its
|
|
75
85
|
received body checked with `expectMemberViewWithholds`, `/ui/data` answering 404
|
|
76
86
|
and never requested, no maintainer fact rendered, no maintainer request sent),
|
|
@@ -80,9 +90,31 @@ refresh that stalls past its deadline (one focusable Retry, no maintainer
|
|
|
80
90
|
request until Retry succeeds), an open client setup drawer closed by a failed
|
|
81
91
|
refresh with focus on Retry, Tokens under recovery (an open rotate question
|
|
82
92
|
closes with focus on Retry and nothing sent; an unseen secret dialog stays open
|
|
83
|
-
over the banner until dismissed), payload sentinels, CSP, and the refusal of a v1 body.
|
|
93
|
+
over the banner until dismissed), payload sentinels, CSP, and the refusal of a v1 body. Matrix coverage excludes retired grants from
|
|
94
|
+
its live counts and pie, keeps their names inspectable in the popover, and
|
|
95
|
+
renders an unavailable catalog as a dashed empty ring with "?" and an unknown
|
|
96
|
+
accessible name. Cell names distinguish equal grants against different live
|
|
97
|
+
catalog sizes. `test/operator-endpoints.test.ts` covers those view-model cases,
|
|
98
|
+
including a known empty catalog versus an unavailable one.
|
|
99
|
+
`test/operator-connection-map.test.ts` also checks that a failed or unobserved
|
|
100
|
+
catalog counts no tools, which the map draws as a muted dash, while a known
|
|
101
|
+
empty catalog counts zero.
|
|
102
|
+
`test/browser/operator-overview.spec.ts` covers the Overview on the frozen v2
|
|
103
|
+
fixtures: the status pill, stat labels and numbers, map columns, dashed read-only grants and dotted
|
|
104
|
+
tokenless access; attention rows with their fix links, a copied fix prompt equal
|
|
105
|
+
to the catalogue's text, config order through a staggered full refresh, prototype-named
|
|
106
|
+
connector IDs during loading and failure, and stable single-card height when
|
|
107
|
+
its catalog empties. A single long-id, long-title card at 320, 390, 1024 and
|
|
108
|
+
1440px covers all eight status labels, both auth scopes, with and without the
|
|
109
|
+
Read-only badge, tools and an action. Text rows stay single-line, full text
|
|
110
|
+
remains accessible, links stay focusable, and the detail header and Overview
|
|
111
|
+
attention rows retain their height; the hover
|
|
112
|
+
sentence replacing the legend, and dimming; keyboard focus in column order with each node's sentence
|
|
113
|
+
as its accessible name, Enter opening an endpoint's row and a connector; the
|
|
114
|
+
narrow list, whose caller chips link where their map nodes do; an empty deployment; and a member at `/` getting the member page
|
|
115
|
+
with no maintainer read, plus CSP and no `style` attributes.
|
|
84
116
|
`test/browser/operator-console.spec.ts` covers UI-3: who-can-use chips by
|
|
85
|
-
label, raw id and agent mark and "+N
|
|
117
|
+
label, raw id and agent mark and "+N" from the keyboard; Tokens rows,
|
|
86
118
|
issue with the once-only secret dialog, in-row rotate and revoke questions,
|
|
87
119
|
503, 499 and 409 failures that send exactly one request, slots missing from
|
|
88
120
|
the list or left unresolved shown with disabled controls until Refresh; and the
|
|
@@ -121,15 +153,15 @@ cannot prove these rules.
|
|
|
121
153
|
|
|
122
154
|
The source of truth is `*-linux.png`, produced on `ubuntu-latest` with the
|
|
123
155
|
lockfile's Playwright Chromium. Both CI workflows use the same runner, Node 22,
|
|
124
|
-
and `
|
|
156
|
+
and `pnpm run test:browser:install --with-deps`. macOS runs fixture routing and
|
|
125
157
|
readiness checks but omits screenshot comparison. The update script refuses
|
|
126
158
|
non-Linux hosts. Do not commit Darwin baselines.
|
|
127
159
|
|
|
128
160
|
When Docker is unavailable, use the **Operator snapshots** workflow:
|
|
129
161
|
|
|
130
162
|
```sh
|
|
131
|
-
git push --force-with-lease origin
|
|
132
|
-
gh workflow run operator-snapshots.yml --ref
|
|
163
|
+
git push --force-with-lease origin <branch>
|
|
164
|
+
gh workflow run operator-snapshots.yml --ref <branch>
|
|
133
165
|
# Read the completed run ID from the Actions page or gh run list.
|
|
134
166
|
gh run download RUN_ID --name operator-linux-snapshots \
|
|
135
167
|
--dir test/browser/operator-visual.spec.ts-snapshots
|
|
@@ -140,9 +172,9 @@ images and changed files, commit the intentional changes, and push again. The
|
|
|
140
172
|
normal CI browser job must compare and pass against that commit. Snapshot
|
|
141
173
|
updates run only through `workflow_dispatch`.
|
|
142
174
|
|
|
143
|
-
On a matching Linux runner, `
|
|
144
|
-
then run `
|
|
145
|
-
`
|
|
175
|
+
On a matching Linux runner, `pnpm install --frozen-lockfile`, install Chromium with the command above,
|
|
176
|
+
then run `pnpm run test:browser:snapshots`. The normal command is
|
|
177
|
+
`pnpm run test:browser`. Missing baselines fail CI; retries cannot bless them.
|
|
146
178
|
Set `OPERATOR_VISUAL_INSPECT=1` to retain local screenshots under ignored
|
|
147
179
|
`test-results/` for inspection, without updating committed baselines.
|
|
148
180
|
|