@zackbart/connecta 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +30 -20
  3. package/bin/connecta.mjs +25 -6
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.d.ts +167 -23
  6. package/dist/access-tokens.js +676 -140
  7. package/dist/agent-identity.js +47 -0
  8. package/dist/auth/cloudflare-access.js +13 -5
  9. package/dist/auth-elicitation.js +1 -1
  10. package/dist/call-admission-context.js +15 -0
  11. package/dist/call-admission.js +30 -43
  12. package/dist/call-recovery.js +6 -2
  13. package/dist/catalog-service.js +7 -2
  14. package/dist/config-value-sources.js +14 -9
  15. package/dist/config.d.ts +58 -78
  16. package/dist/config.js +143 -47
  17. package/dist/connector-access.d.ts +4 -33
  18. package/dist/connector-access.js +15 -99
  19. package/dist/connector-caller.d.ts +7 -1
  20. package/dist/connector-caller.js +3 -2
  21. package/dist/connectors/api-connector.d.ts +27 -4
  22. package/dist/connectors/api-connector.js +2 -0
  23. package/dist/connectors/guarded-fetch.d.ts +9 -0
  24. package/dist/connectors/guarded-fetch.js +7 -0
  25. package/dist/connectors/remote-mcp.js +2 -0
  26. package/dist/describe-config.d.ts +32 -7
  27. package/dist/describe-config.js +9 -18
  28. package/dist/errors.d.ts +1 -1
  29. package/dist/errors.js +1 -0
  30. package/dist/execute.js +16 -14
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +30 -10
  33. package/dist/invocation.js +12 -7
  34. package/dist/membership.d.ts +51 -0
  35. package/dist/membership.js +121 -0
  36. package/dist/meta-tools.js +17 -19
  37. package/dist/module-contracts.d.ts +23 -6
  38. package/dist/operator-record.d.ts +2 -2
  39. package/dist/operator-record.js +2 -2
  40. package/dist/operator-ui/agents.d.ts +74 -0
  41. package/dist/operator-ui/agents.js +104 -0
  42. package/dist/operator-ui/contract.d.ts +151 -29
  43. package/dist/operator-ui/endpoints.js +192 -0
  44. package/dist/operator-ui/fix-prompts.js +4 -4
  45. package/dist/operator-ui/generated.js +5 -4
  46. package/dist/operator-ui/member-page.js +112 -0
  47. package/dist/operator-ui/model.d.ts +15 -7
  48. package/dist/operator-ui/view.d.ts +84 -19
  49. package/dist/operator-ui/view.js +83 -21
  50. package/dist/page-styles.js +1 -1
  51. package/dist/program-writes.js +2 -2
  52. package/dist/provider.d.ts +63 -11
  53. package/dist/provider.js +46 -7
  54. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  55. package/dist/providers/_shared/google/workspace.js +31 -9
  56. package/dist/providers/_shared/rest/tools.js +2 -2
  57. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  58. package/dist/providers/_shared/rest/value-safety.js +699 -7
  59. package/dist/providers/breeze/skill.generated.js +2 -2
  60. package/dist/providers/ccb/index.js +1 -1
  61. package/dist/providers/cloudflare/index.js +3 -3
  62. package/dist/providers/cloudflare/rest.js +25 -3
  63. package/dist/providers/cloudflare/skill.generated.js +2 -2
  64. package/dist/providers/cloudflare/value-safety.js +32 -0
  65. package/dist/providers/docs/index.d.ts +1 -1
  66. package/dist/providers/docs/index.js +5 -5
  67. package/dist/providers/docs/skill.generated.js +2 -2
  68. package/dist/providers/drive/index.d.ts +1 -1
  69. package/dist/providers/drive/index.js +4 -4
  70. package/dist/providers/drive/skill.generated.js +2 -2
  71. package/dist/providers/forms/index.d.ts +1 -1
  72. package/dist/providers/forms/index.js +5 -5
  73. package/dist/providers/forms/skill.generated.js +2 -2
  74. package/dist/providers/github/skill.generated.js +2 -2
  75. package/dist/providers/gmail/index.d.ts +1 -1
  76. package/dist/providers/gmail/index.js +279 -26
  77. package/dist/providers/gmail/skill.generated.js +2 -2
  78. package/dist/providers/linear/index.js +1 -1
  79. package/dist/providers/mixpanel/index.d.ts +27 -12
  80. package/dist/providers/mixpanel/index.js +58 -6
  81. package/dist/providers/mixpanel/operations.js +20 -0
  82. package/dist/providers/mixpanel/rest.js +603 -0
  83. package/dist/providers/mixpanel/skill.generated.js +3 -2
  84. package/dist/providers/mixpanel/value-safety.js +16 -0
  85. package/dist/providers/notion/index.d.ts +7 -0
  86. package/dist/providers/notion/index.js +116 -11
  87. package/dist/providers/notion/rest.js +183 -24
  88. package/dist/providers/notion/skill.generated.js +4 -4
  89. package/dist/providers/notion/value-safety.js +30 -5
  90. package/dist/providers/overflow/index.js +1 -1
  91. package/dist/providers/planning-center/index.d.ts +27 -0
  92. package/dist/providers/planning-center/index.js +287 -25
  93. package/dist/providers/planning-center/skill.generated.js +24 -4
  94. package/dist/providers/sheets/index.d.ts +4 -1
  95. package/dist/providers/sheets/index.js +9 -6
  96. package/dist/providers/sheets/skill.generated.js +2 -2
  97. package/dist/providers/slides/index.d.ts +1 -1
  98. package/dist/providers/slides/index.js +6 -6
  99. package/dist/providers/slides/skill.generated.js +2 -2
  100. package/dist/providers/stripe/rest.js +9 -2
  101. package/dist/providers/stripe/skill.generated.js +2 -2
  102. package/dist/providers/stripe/value-safety.js +22 -0
  103. package/dist/providers/vercel/index.js +2 -2
  104. package/dist/providers/vercel/rest.js +20 -2
  105. package/dist/providers/vercel/skill.generated.js +2 -2
  106. package/dist/providers/vercel/value-safety.js +22 -0
  107. package/dist/providers/wispr-flow/index.d.ts +14 -0
  108. package/dist/providers/wispr-flow/index.js +117 -0
  109. package/dist/providers/wispr-flow/skill.generated.js +8 -0
  110. package/dist/registry.d.ts +2 -3
  111. package/dist/registry.js +8 -9
  112. package/dist/routes/access-tokens.js +82 -75
  113. package/dist/routes/activity-disclosure.js +17 -18
  114. package/dist/routes/activity.js +34 -54
  115. package/dist/routes/actor-labels.js +33 -4
  116. package/dist/routes/agent-path.js +43 -0
  117. package/dist/routes/connect.js +16 -12
  118. package/dist/routes/credentials.js +16 -40
  119. package/dist/routes/mcp.js +155 -85
  120. package/dist/routes/oauth-management.js +11 -5
  121. package/dist/routes/oauth.js +3 -9
  122. package/dist/routes/operator.js +25 -17
  123. package/dist/routes/shared.d.ts +60 -20
  124. package/dist/routes/shared.js +187 -96
  125. package/dist/routes/ui-config.js +437 -97
  126. package/dist/routes/ui-data.js +2 -2
  127. package/dist/routes/ui.js +9 -16
  128. package/dist/runtime/call-admission.js +9 -0
  129. package/dist/server.js +8 -0
  130. package/dist/skills.js +5 -4
  131. package/dist/storage/keys.js +9 -1
  132. package/dist/tool-safety.js +13 -5
  133. package/dist/types.d.ts +12 -1
  134. package/dist/ui.d.ts +2 -2
  135. package/dist/ui.js +17 -15
  136. package/dist/usage-guide.js +3 -3
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/workspace-address.js +9 -0
  140. package/dist/workspace-subject.js +23 -0
  141. package/documentation/README.md +46 -22
  142. package/documentation/architecture.md +378 -58
  143. package/documentation/auth.md +806 -215
  144. package/documentation/code-mode.md +93 -81
  145. package/documentation/deploying.md +128 -26
  146. package/documentation/integrating.md +145 -22
  147. package/documentation/meta-tools.md +58 -46
  148. package/documentation/migration-0.33.md +344 -0
  149. package/documentation/operating.md +14 -9
  150. package/documentation/operator-tests.md +117 -31
  151. package/documentation/operator-ui.md +326 -55
  152. package/documentation/provider-auth-migration.md +42 -27
  153. package/documentation/provider-migration-0.29.md +3 -3
  154. package/examples/worker/AGENTS.md +35 -8
  155. package/examples/worker/README.md +92 -53
  156. package/examples/worker/src/connecta.config.ts +41 -20
  157. package/examples/worker/wrangler.jsonc +3 -0
  158. package/package.json +7 -2
  159. package/templates/node/.env.example +3 -0
  160. package/templates/node/AGENTS.md +33 -10
  161. package/templates/node/Dockerfile +2 -2
  162. package/templates/node/README.md +71 -44
  163. package/templates/node/docker-compose.yml +3 -2
  164. package/templates/node/package.json +4 -3
  165. package/templates/node/src/adopt-token.ts +23 -0
  166. package/templates/node/src/connecta.config.ts +33 -12
  167. package/templates/node/src/rotate-token.ts +17 -0
  168. package/dist/tool-safety.d.ts +0 -9
  169. package/templates/node/src/provision-token.ts +0 -17
@@ -137,8 +137,8 @@ export function uiData(registry, baseUrl, options) {
137
137
  const fields = credentialFields();
138
138
  return Effect.succeed({
139
139
  ...card,
140
- ...(fields?.length ? { fields } : {}),
141
- configured: false,
140
+ ...(fields?.length ? { fields: fields.map((field) => ({ ...field, configured: null })) } : {}),
141
+ configured: null,
142
142
  removable: true,
143
143
  testable: testRule.mode !== null,
144
144
  error: "Stored credential could not be read.",
package/dist/routes/ui.js CHANGED
@@ -9,7 +9,7 @@ import { CONNECTA_VERSION } from "../version.js";
9
9
  import { CONNECTA_FAVICON_ICO } from "../favicon.js";
10
10
  import { CONNECTA_FAVICON_SVG, operatorPageForPath, renderUiHtml } from "../ui.js";
11
11
  import { authorized, refuse, scopeFor, serveOperator, visibleRegistry, } from "./operator.js";
12
- import { mayManageConnector, privateJson } from "./shared.js";
12
+ import { managesAgentTokens, mayManageConnector, privateJson, readsActivity } from "./shared.js";
13
13
  import { routeUiConfig } from "./ui-config.js";
14
14
  import { uiData } from "./ui-data.js";
15
15
  /**
@@ -84,7 +84,8 @@ export async function routeUi(context) {
84
84
  ? opts.config.auth.find((provider) => provider.uiAuth?.kind === "cloudflare-access")?.uiAuth
85
85
  : undefined;
86
86
  const uiAuth = ambient ??
87
- opts.config.auth.find((provider) => provider.uiAuth && provider.uiAuth.kind !== "cloudflare-access")?.uiAuth;
87
+ opts.config.auth.find((provider) => provider.uiAuth && provider.uiAuth.kind !== "cloudflare-access")?.uiAuth ??
88
+ opts.config.auth.find((provider) => provider.uiAuth?.kind === "cloudflare-access")?.uiAuth;
88
89
  const mcpUrl = new URL("/mcp", baseUrl).toString();
89
90
  const clerkOrigin = uiAuth?.kind === "clerk" && isSafeHttpsUrl(uiAuth.frontendApiUrl)
90
91
  ? new URL(uiAuth.frontendApiUrl).origin
@@ -115,14 +116,14 @@ function operatorView({ opts }, authz, registry) {
115
116
  const visible = registry.listConnectors();
116
117
  const mayManage = (id) => {
117
118
  const connector = registry.getConnector(id);
118
- return Boolean(connector && mayManageConnector(authz, connector));
119
+ return Boolean(connector && mayManageConnector(opts, authz, connector));
119
120
  };
120
121
  const permissions = (connector) => ({
121
122
  use: true,
122
123
  manageSharedAuth: connector.authScope !== "personal" && mayManage(connector.id),
123
124
  connectPersonal: connector.authScope === "personal" && mayManage(connector.id),
124
125
  });
125
- const activityEnabled = Boolean(opts.config.activity?.store.list) && authz.operator;
126
+ const activityEnabled = Boolean(opts.config.activity?.store.list) && readsActivity(opts, authz);
126
127
  const credentialManagement = visible.some((c) => c.credential && mayManage(c.id))
127
128
  ? opts.config.vault
128
129
  ? "available"
@@ -169,17 +170,9 @@ function summary(context) {
169
170
  const authz = yield* authorized(context);
170
171
  const registry = yield* visibleRegistry(context, authz);
171
172
  const { visible, mayManage, permissions, activityEnabled, credentialManagement } = operatorView(context, authz, registry);
172
- // Setup commands are offered per pool, but only for pools this identity's
173
- // grant admits: `/mcp/<name>` answers every other name with one flat 404 so
174
- // a credential cannot enumerate them, and the page must not undo that.
175
- const pools = [];
176
- for (const [name, pool] of opts.pools ?? []) {
177
- const granted = yield* Effect.tryPromise(async () => (await pool.grant(authz.identity)) === true).pipe(
178
- // A throwing grant is a refusal at the endpoint, and so a refusal here.
179
- Effect.orElseSucceed(() => false));
180
- if (granted)
181
- pools.push(name);
182
- }
173
+ // Only maintainers reach here (#837); setup commands are offered for every
174
+ // declared pool, since they administer them all.
175
+ const pools = [...opts.pools.keys()];
183
176
  return privateJson({
184
177
  ...(pools.length ? { pools } : {}),
185
178
  serverInfo: opts.config.serverInfo,
@@ -187,7 +180,7 @@ function summary(context) {
187
180
  activityEnabled,
188
181
  ...(opts.config.accessTokens
189
182
  ? {
190
- accessTokenManagement: authz.accessTokenManagement && authz.identity.principal ? "available" : "requires_operator",
183
+ accessTokenManagement: managesAgentTokens(opts, authz) ? "available" : "requires_operator",
191
184
  }
192
185
  : {}),
193
186
  credentialManagement,
@@ -12,6 +12,15 @@
12
12
  // shared queue between them was refused.
13
13
  import { Effect } from "effect";
14
14
  let program;
15
+ const budgetCharges = new WeakMap();
16
+ /** Bind budget-only admission to the partition already owned by this permit. */
17
+ export function provideCallBudgetCharge(permit, charge) {
18
+ budgetCharges.set(permit, charge);
19
+ }
20
+ /** Charge an internal request without acquiring another concurrency permit. */
21
+ export function chargeCallBudget(permit, signal) {
22
+ return Effect.clockWith((clock) => budgetCharges.get(permit)?.(clock.currentTimeMillisUnsafe(), signal) ?? Effect.void);
23
+ }
15
24
  // What the live Clock reads, looked up on every call rather than bound once,
16
25
  // so a Date faked after a caller queued (vitest's setSystemTime) still counts.
17
26
  const liveNow = () => Date.now();
package/dist/server.js CHANGED
@@ -2,6 +2,7 @@ import { Effect } from "effect";
2
2
  import { notFoundResponse } from "./branding.js";
3
3
  import { aggregateCallAdmissionSnapshots } from "./call-admission.js";
4
4
  import { isAdmittingExecutor } from "./executor-admission.js";
5
+ import { isAgentPath } from "./routes/agent-path.js";
5
6
  import { createMcpRoute, MCP_CORS_HEADERS } from "./routes/mcp.js";
6
7
  import { routeOAuthCallback } from "./routes/oauth.js";
7
8
  import { routeOAuthClientMetadata } from "./routes/oauth-client-metadata.js";
@@ -59,6 +60,10 @@ export function createFetchHandler(opts) {
59
60
  };
60
61
  const route = (context) => Effect.gen(function* () {
61
62
  const { request, path, baseUrl } = context;
63
+ // The agent namespace is routed before anything else, so no path
64
+ // Cloudflare Access bypasses as `/agent/*` can reach a human route.
65
+ if (isAgentPath(path))
66
+ return (yield* routeMcp.handle(context)) ?? notFoundResponse(request, opts.config);
62
67
  const clientMetadata = routeOAuthClientMetadata(context);
63
68
  if (clientMetadata)
64
69
  return clientMetadata;
@@ -88,6 +93,9 @@ export function createFetchHandler(opts) {
88
93
  });
89
94
  }
90
95
  if (path.startsWith("/.well-known/")) {
96
+ const guardedMetadata = yield* routeMcp.handle(context);
97
+ if (guardedMetadata)
98
+ return guardedMetadata;
91
99
  const response = yield* metadata(request, baseUrl);
92
100
  if (response)
93
101
  return response;
package/dist/skills.js CHANGED
@@ -7,11 +7,12 @@ import { sentSecretsForRequest } from "./sent-secrets.js";
7
7
  const ROUTE = "Choose a route before discovery. One known-address read uses call_tool; one known-address write uses call_destructive_tool. Unknown-address read-only work starts with execute_code to discover, call, and return the answer; use the same route for reduction, multiple or dependent calls, loops, joins, or branches. Keep discovery and calls together when schemas suffice; do not return catalog matches alone. Sample unfamiliar reads.";
8
8
  const RECOVERY = 'When an auth error includes a handoff, give it to the user or operator. Use authorize_connector for a fresh link. Page resultId notices and format:"paged" handles with connecta.result(handleOrId, { page? }). Fetch skills({ name: "usage" }) only for missing guidance or program repair.';
9
9
  /**
10
- * The always-loaded MCP `instructions` string. A program runs reads and the
11
- * writes in trusted pools; a read-only pool routes every write to top-level
12
- * call_destructive_tool, where the host's permission prompt is the approval.
10
+ * The always-loaded MCP `instructions` string. An endpoint is writable or
11
+ * read-only: a writable one lets programs and call_destructive_tool write,
12
+ * and the client owns approval; a read-only one omits write tools from search
13
+ * and describe and refuses every write call. Guides may still name them.
13
14
  */
14
- export const CONNECTA_INSTRUCTIONS = `${ROUTE} Programs call reads in read-only pools and may also write in trusted pools. The execute_code description names this endpoint's trust. In a read-only pool, discover writes with search_tools then use call_destructive_tool. Never repeat a write to recover its output. ${RECOVERY}`;
15
+ export const CONNECTA_INSTRUCTIONS = `${ROUTE} On a writable endpoint, programs and call_destructive_tool may write. On a read-only endpoint, search omits write tools and every write call is refused; guides may still name them. The execute_code description says which this endpoint is. Never repeat a write to recover its output. ${RECOVERY}`;
15
16
  export { USAGE_SKILL } from "./usage-guide.js";
16
17
  /** True when at least one of `connectors` carries a usage guide. */
17
18
  export function hasConnectorGuides(connectors) {
@@ -164,7 +164,12 @@ export const oauthHandoffKeys = {
164
164
  },
165
165
  handoff: (connectorId, stateHash) => validateStorageKey(`oauth-handoff:v1:${connectorId}:${stateHash}`),
166
166
  };
167
- /** Connecta-issued `cta_` access tokens: records, digest lookups, capacity. */
167
+ /**
168
+ * Connecta-issued `cta_` access tokens: token records, digest lookups, one
169
+ * slot per declared agent naming its live record, and a never-deleted claim per
170
+ * legacy record an adoption has taken. `active` is the capacity
171
+ * ledger releases before 0.33 wrote; nothing writes it now.
172
+ */
168
173
  export const accessTokenKeys = {
169
174
  family: {
170
175
  name: "access-token",
@@ -178,6 +183,9 @@ export const accessTokenKeys = {
178
183
  recordPrefix: "access-token:v1:record:",
179
184
  record: (id) => validateStorageKey(`access-token:v1:record:${id}`),
180
185
  lookup: (tokenHash) => validateStorageKey(`access-token:v1:lookup:${tokenHash}`),
186
+ agent: (name) => validateStorageKey(`access-token:v1:agent:${name}`),
187
+ /** Write-once claim that a legacy token record has been taken by one agent's adoption. */
188
+ adopted: (legacyRecordId) => validateStorageKey(`access-token:v1:adopted:${legacyRecordId}`),
181
189
  active: "access-token:v1:active",
182
190
  };
183
191
  /**
@@ -14,9 +14,17 @@ export function classifyTool(definition, override, review) {
14
14
  ? "read"
15
15
  : "write";
16
16
  }
17
- /** The only permission decision for a connector call. Approval is host-owned. */
18
- export function surfaceAllowsTool(classification, surface, trust = "read-only") {
19
- return (classification === "read" ||
20
- surface === "call_destructive_tool" ||
21
- (surface === "execute_code" && trust === "trusted"));
17
+ /**
18
+ * The only permission decision for a connector call. Approval is client-owned.
19
+ * A read-only endpoint refuses every write on every surface; a writable one
20
+ * sends writes through call_destructive_tool and programs, never call_tool.
21
+ */
22
+ export function writeRefusal(classification, surface, readOnly = false) {
23
+ if (classification === "read")
24
+ return undefined;
25
+ if (readOnly)
26
+ return "read_only_endpoint";
27
+ return surface === "call_destructive_tool" || surface === "execute_code"
28
+ ? undefined
29
+ : "destructive_tool_requires_approval";
22
30
  }
package/dist/types.d.ts CHANGED
@@ -95,7 +95,7 @@ export interface ToolDef {
95
95
  * Standard MCP tool behavior hints plus provider-specific extensions.
96
96
  * The registry classifies these after deployment overrides and provider
97
97
  * review. Only a stored read verdict may use call_tool; program writes
98
- * require a trusted pool. Approval belongs to the host.
98
+ * require a writable endpoint. Approval belongs to the host.
99
99
  */
100
100
  annotations?: ToolAnnotations;
101
101
  }
@@ -727,6 +727,11 @@ export type AuthResult = {
727
727
  sessionCookies?: readonly string[];
728
728
  /** Human owner represented by a non-interactive access credential. */
729
729
  principal?: IdentityReference;
730
+ /**
731
+ * Declared agent name. Only the built-in `accessTokens()` adapter may
732
+ * set it; a result from any other provider that carries it is refused.
733
+ */
734
+ agent?: string;
730
735
  } | {
731
736
  ok: false;
732
737
  response: Response;
@@ -747,6 +752,12 @@ export interface AuthenticatedIdentity {
747
752
  subject?: IdentityReference;
748
753
  /** Human owner of personal connector authentication. */
749
754
  principal?: IdentityReference;
755
+ /**
756
+ * Name of the code-declared agent this caller is, from `agents`. Set only
757
+ * when the built-in access-token adapter admitted a `cta_` token bound to
758
+ * a currently declared agent; an agent never has a `principal`.
759
+ */
760
+ agent?: string;
750
761
  interactive: boolean;
751
762
  }
752
763
  /** Public browser-auth configuration exposed to connecta's status UI. */
package/dist/ui.d.ts CHANGED
@@ -9,7 +9,7 @@ export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, i
9
9
  export type OperatorPage = import("./operator-ui/view.js").OperatorPage;
10
10
  export declare function operatorPageForPath(path: string): OperatorPage | undefined;
11
11
  export declare function operatorPageTitle(page: OperatorPage, configuredTitle: string): string;
12
- /** Report the verdict the registry stored, independent of pool trust. */
12
+ /** Report the verdict the registry stored, independent of whether an endpoint is read-only. */
13
13
  export declare function uiToolSafety(definition: ToolDef): UiToolSafety;
14
14
  /**
15
15
  * The fix-prompt key for a connector that is not usable. Chosen from the
@@ -49,4 +49,4 @@ _nonce?: string, page?: OperatorPage): string;
49
49
  export declare function operatorUi(options?: {
50
50
  branding?: ConnectaBranding;
51
51
  }): OperatorSurface;
52
- export type { OperatorUiContract, OperatorConnectorOverlay, OperatorTool, OperatorViewer, OperatorGrant, OperatorPool, OperatorLastCall, } from "./operator-ui/contract.js";
52
+ export type { OperatorUiContract, OperatorUiContractV2, OperatorRole, OperatorMember, OperatorPoolV2, OperatorRootEndpoint, OperatorMemberConnector, OperatorMemberManage, OperatorAgent, OperatorSharedSource, OperatorRunsAs, OperatorConnectorPoolCell, OperatorConnectorOverlay, OperatorTool, OperatorGrant, OperatorLastCall, } from "./operator-ui/contract.js";
package/dist/ui.js CHANGED
@@ -4,33 +4,31 @@ import { routeCredentials } from "./routes/credentials.js";
4
4
  import { routeOAuthManagement } from "./routes/oauth-management.js";
5
5
  import { uiData } from "./routes/ui-data.js";
6
6
  import { runEdge } from "./runtime/run.js";
7
- import { OPERATOR_UI_STYLE_PATH, OPERATOR_UI_SCRIPT_PATH, OPERATOR_UI_NOTICES_PATH } from "./operator-ui/generated.js";
7
+ import { OPERATOR_UI_STYLE_PATH, OPERATOR_UI_SCHEME_PATH, OPERATOR_UI_SCRIPT_PATH, OPERATOR_UI_NOTICES_PATH, } from "./operator-ui/generated.js";
8
8
  export { filterUiConnectors, } from "./operator-ui/model.js";
9
9
  import { escapeHtml, isSafeHttpsUrl, renderPage, resolveBranding } from "./branding.js";
10
10
  export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
11
11
  const OPERATOR_PAGE_LABELS = {
12
- overview: "Overview",
12
+ endpoints: "Endpoints",
13
13
  connections: "Connectors",
14
14
  connector: "Connector",
15
- tools: "Tools",
16
- access: "Access",
17
- config: "Config",
18
- tokens: "Access tokens",
15
+ settings: "Settings",
16
+ tokens: "Tokens",
19
17
  activity: "Activity",
20
18
  };
21
19
  export function operatorPageForPath(path) {
22
20
  if (path === "/")
23
- return "overview";
21
+ return "endpoints";
24
22
  if (path === "/connectors")
25
23
  return "connections";
26
24
  if (/^\/connectors\/[a-z0-9_-]+$/.test(path))
27
25
  return "connector";
28
26
  if (path === "/tools")
29
- return "tools";
27
+ return "connections";
30
28
  if (path === "/access")
31
- return "access";
32
- if (path === "/config")
33
- return "config";
29
+ return "endpoints";
30
+ if (path === "/config" || path === "/settings")
31
+ return "settings";
34
32
  if (path === "/tokens")
35
33
  return "tokens";
36
34
  if (path === "/activity")
@@ -40,7 +38,7 @@ export function operatorPageForPath(path) {
40
38
  export function operatorPageTitle(page, configuredTitle) {
41
39
  return `${OPERATOR_PAGE_LABELS[page]} — ${configuredTitle}`;
42
40
  }
43
- /** Report the verdict the registry stored, independent of pool trust. */
41
+ /** Report the verdict the registry stored, independent of whether an endpoint is read-only. */
44
42
  export function uiToolSafety(definition) {
45
43
  if (definition.classification === "read")
46
44
  return "runs_in_programs";
@@ -101,7 +99,7 @@ function jsonForInlineScript(value) {
101
99
  */
102
100
  export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding,
103
101
  /** @deprecated Scripts load from same-origin assets; ignored. */
104
- _nonce, page = "overview") {
102
+ _nonce, page = "endpoints") {
105
103
  const clerk = uiAuth?.kind === "clerk" ? uiAuth : undefined;
106
104
  // The Clerk loader's origin. A value that fails the gate is dropped rather
107
105
  // than escaped into the page: the loader tag is simply not emitted, the gate
@@ -130,13 +128,15 @@ _nonce, page = "overview") {
130
128
  : "";
131
129
  // The shared layout owns the head, scheme, theme, and masthead; the shell
132
130
  // adds its own bundle (which already imports the shared tokens), the nav
133
- // mount, and the app.
131
+ // mount, and the app. The scheme initializer blocks, ahead of the
132
+ // stylesheet, so a saved color scheme holds from the first paint.
134
133
  return renderPage(branding, {
135
134
  title: operatorPageTitle(page, brand.pageTitle),
136
135
  uiMounted: true,
137
136
  operatorShell: true,
138
137
  styles: "",
139
- head: `<link rel="stylesheet" href="${OPERATOR_UI_STYLE_PATH}"><link rel="license" href="${OPERATOR_UI_NOTICES_PATH}">` +
138
+ head: `<script src="${OPERATOR_UI_SCHEME_PATH}"></script>` +
139
+ `<link rel="stylesheet" href="${OPERATOR_UI_STYLE_PATH}"><link rel="license" href="${OPERATOR_UI_NOTICES_PATH}">` +
140
140
  clerkScript,
141
141
  skipTo: { id: "operatorContent", label: "Skip to operator page" },
142
142
  mastheadEnd: '<div id="operatorNav"></div>',
@@ -156,6 +156,7 @@ _nonce, page = "overview") {
156
156
  auth,
157
157
  mcpUrl,
158
158
  initialPage: page,
159
+ schemePinned: brand.theme.colorScheme !== "system",
159
160
  titleSuffix: brand.pageTitle,
160
161
  productName: brand.productName,
161
162
  productDescription: brand.description,
@@ -186,6 +187,7 @@ export function operatorUi(options = {}) {
186
187
  "/tools",
187
188
  "/access",
188
189
  "/config",
190
+ "/settings",
189
191
  "/ui",
190
192
  "/ui/*",
191
193
  "/favicon.svg",
@@ -16,9 +16,9 @@ description: How to route work between one execute_code program and Connecta's e
16
16
 
17
17
  Read one account with call_tool: \`{ "address": "crm.get_account", "args": { "id": "acct_42" } }\`.
18
18
  Discover and call together when schemas suffice. search_tools covers catalog
19
- inspection and write discovery in read-only pools. Programs may
20
- write only in trusted pools; the tool description states this endpoint's trust.
21
- Unannotated tools fail closed to writes.
19
+ inspection. On a read-only endpoint, as execute_code's description says, search
20
+ omits writes (guides may name them) and each fails read_only_endpoint; tell the
21
+ user. Unannotated tools fail closed to writes.
22
22
 
23
23
  ## The global and the envelopes
24
24
 
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const CONNECTA_VERSION = "0.31.0";
1
+ export declare const CONNECTA_VERSION = "0.33.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated from package.json by scripts/generate-version.mjs.
2
2
  // Workers-safe: no runtime filesystem read or deployment override.
3
- export const CONNECTA_VERSION = "0.31.0";
3
+ export const CONNECTA_VERSION = "0.33.0";
@@ -0,0 +1,9 @@
1
+ // The one rule for a Google Workspace address, shared by the Workspace
2
+ // providers' fixed `subject` and a pool's `owner` (#837, decisions/0006), so
3
+ // config accepts exactly the addresses a delegated token can be minted for.
4
+ /** A plausible mailbox: one `@`, no whitespace or control characters. */
5
+ const ADDRESS = /^[^\s@<>()",;:\\[\]]+@[^\s@<>()",;:\\[\]]+\.[^\s@<>()",;:\\[\]]+$/;
6
+ /** Whether `value`, already trimmed, is a Workspace email address. */
7
+ export function isWorkspaceAddress(value) {
8
+ return typeof value === "string" && ADDRESS.test(value);
9
+ }
@@ -0,0 +1,23 @@
1
+ // Whose account a Google Workspace connector acts as, for the operator
2
+ // matrix (#837, decisions/0006). The Workspace providers tag the connector
3
+ // they build; nothing else can, and nothing a request sends reaches this.
4
+ // Read only by the operator contract, which never shows a mapping function.
5
+ const tagged = new WeakMap();
6
+ /** Tag a Workspace connector with its subject kind, returning it unchanged. */
7
+ export function tagWorkspaceSubject(connector, subject) {
8
+ tagged.set(connector, typeof subject === "string" ? { kind: "fixed", subject } : { kind: "mapping" });
9
+ return connector;
10
+ }
11
+ /**
12
+ * The subject kind of a tagged connector, or undefined for any other. Walks
13
+ * the prototype chain, since provider construction may wrap a connector it
14
+ * cannot restamp in an object that inherits from it.
15
+ */
16
+ export function workspaceSubjectOf(connector) {
17
+ for (let current = connector; current; current = Object.getPrototypeOf(current)) {
18
+ const subject = tagged.get(current);
19
+ if (subject)
20
+ return subject;
21
+ }
22
+ return undefined;
23
+ }
@@ -1,34 +1,58 @@
1
1
  # Agent documentation
2
2
 
3
- Choose a route by the work you are doing. Each route identifies the task guide
4
- and contracts needed to operate an endpoint, set up a deployment, connect a
5
- service, or change Connecta itself.
3
+ Start here when operating a deployment installed with `@zackbart/connecta`.
4
+ All operator procedures below are available in the installed package; paths in
5
+ this directory are relative to `node_modules/@zackbart/connecta/documentation/`.
6
+ For an existing endpoint, start with [Operating an endpoint](./operating.md).
7
+ For a new deployment, start with [Deploying Connecta](./deploying.md).
6
8
 
7
- | Your task | Start here | Read next when needed |
8
- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
9
- | Use an existing endpoint to answer a question or perform a task | [Operating an endpoint](./operating.md) | [Meta-tools](./meta-tools.md), [guest API](./code-mode.md) |
10
- | Set up, configure, upgrade, or diagnose a deployment | [Deploying Connecta](./deploying.md) | [Auth](./auth.md), [operator UI](./operator-ui.md) |
11
- | Add a maintained integration, remote MCP, or HTTP API to a deployment | [Integrating services](./integrating.md) | Provider skill, [classification](./architecture.md#providers-and-reviewed-classification), [downstream auth](./auth.md#shared-and-personal-auth) |
12
- | Change Connecta's source, tests, or published contracts | [AGENTS.md](https://github.com/zackbart/connecta/blob/main/AGENTS.md) | [Principles](https://github.com/zackbart/connecta/blob/main/PRINCIPLES.md), [architecture](./architecture.md), then the affected contract |
9
+ ## Operator tasks
10
+
11
+ | Task | Self-contained procedure |
12
+ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
13
+ | Add or remove a connector | [Change the connector roster](./integrating.md#add-or-remove-a-connector) |
14
+ | Organize an agent's pools | [Organizing an agent's pools](./auth.md#organizing-an-agents-pools) |
15
+ | Add a person (maintainer or pool member) | [Add a person](./auth.md#add-a-person), [member page](./operator-ui.md#use-the-member-page) |
16
+ | Connect a shared credential | [Connect a shared credential](./auth.md#connect-a-shared-credential) |
17
+ | Issue, rotate, or revoke an agent token | [Manage an agent token](./auth.md#issue-rotate-or-revoke-an-agent-token) |
18
+ | Upgrade between versions | [Upgrade an existing deployment](./deploying.md#upgrade-an-existing-deployment), including [migration to 0.33](./migration-0.33.md) |
19
+ | Diagnose with `connecta doctor` | [Verify and diagnose](./deploying.md#verify-and-diagnose) |
20
+
21
+ ## Audience rule
22
+
23
+ Shipped docs (`documentation/`, `templates/`, `examples/worker/`, and the root
24
+ `README.md`) are written first for an agent operating a deployment from the
25
+ installed package. Operator conventions must be self-contained rules plus a
26
+ neutral example. Decision records and issues explain why; no operator task
27
+ requires them, `spec/`, `.claude/`, or the Connecta source checkout. Those
28
+ three directories do not ship. Contributor source maps, INV citations, and
29
+ implementation/test evidence belong under clearly labeled contributor
30
+ headings and are optional for operators. Use `assistant`, `alice`, `bob`,
31
+ `crm`, `tickets`, `notes_alice`, and `alice@example.com` in examples; never
32
+ real deployment, client, person, or organization names. Maintained provider
33
+ product names stay.
13
34
 
14
35
  ## Contract reference
15
36
 
16
- | Guide | Owns |
17
- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
18
- | [Meta-tools](./meta-tools.md) | Six top-level tools, discovery, direct-call results, paging, skills, and recovery |
19
- | [Code mode](./code-mode.md) | JavaScript guest API, executor requirements, output, retries, cancellation, limits, and writes |
20
- | [Auth](./auth.md) | Inbound identity, grants, pools, machine tokens, credential ownership, and downstream OAuth |
21
- | [Architecture](./architecture.md) | Request lifetimes, routes, storage, configuration, import boundaries, and Effect implementation |
22
- | [Operator UI](./operator-ui.md) | Optional UI setup, scoped configuration and live-data contract, activity disclosure, and UI implementation |
23
- | [Operator UI tests](./operator-tests.md) | Browser state matrix, real-server flows, and snapshot maintenance |
24
- | [Provider migration for 0.29](./provider-migration-0.29.md) | Upgrade actions for Notion, Vercel, and Cloudflare; provider reconciliation tables supply exact name mappings |
25
- | [Provider auth migration](./provider-auth-migration.md) | Upgrade actions when `auth` selects hosted MCP or Connecta's REST connector, Stripe and Vercel |
37
+ | Guide | Owns |
38
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
39
+ | [Meta-tools](./meta-tools.md) | Six top-level tools, discovery, direct-call results, paging, skills, and recovery |
40
+ | [Code mode](./code-mode.md) | JavaScript guest API, executor requirements, output, retries, cancellation, limits, and writes |
41
+ | [Auth](./auth.md) | Inbound identity, maintainers, pools and members, agents, credential ownership, and downstream OAuth |
42
+ | [Architecture (contributor reference)](./architecture.md) | Request lifetimes, routes, storage, configuration, import boundaries, and Effect implementation |
43
+ | [Operator UI](./operator-ui.md) | Optional UI setup, scoped configuration and live-data contract, activity disclosure, and UI implementation |
44
+ | [Operator UI tests (contributor reference)](./operator-tests.md) | Browser state matrix, real-server flows, and snapshot maintenance |
45
+ | [Migration to 0.33](./migration-0.33.md) | Upgrade actions from 0.32 options to maintainers, members, agents, agent endpoints, and pool owners |
46
+ | [Provider migration for 0.29](./provider-migration-0.29.md) | Upgrade actions for Notion, Vercel, and Cloudflare; provider reconciliation tables supply exact name mappings |
47
+ | [Provider auth migration](./provider-auth-migration.md) | Upgrade actions when `auth` selects hosted MCP or Connecta's REST connector, Stripe and Vercel |
26
48
 
27
49
  Each long contract starts with task routes. Follow those section links before
28
- reading the whole guide. Source and test pointers identify where to check a
29
- claim or implement a change.
50
+ reading the whole guide. Contributor references at the end are optional
51
+ implementation evidence.
52
+
53
+ ## Contributor reference
30
54
 
31
- ## Documentation ownership
55
+ ### Documentation ownership
32
56
 
33
57
  The root [README](../README.md) explains the product for people. It owns what
34
58
  Connecta offers, how the pieces connect, deployment choices, and the setup