@zackbart/connecta 0.23.0 → 0.24.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.
Files changed (81) hide show
  1. package/AGENTS.md +5 -0
  2. package/CHANGELOG.md +53 -0
  3. package/README.md +18 -10
  4. package/dist/activity-friction.d.ts +3 -0
  5. package/dist/activity-friction.js +19 -0
  6. package/dist/activity.d.ts +11 -2
  7. package/dist/activity.js +15 -19
  8. package/dist/auth/downstream-oauth.d.ts +2 -1
  9. package/dist/auth/downstream-oauth.js +10 -1
  10. package/dist/branding.d.ts +67 -0
  11. package/dist/branding.js +176 -0
  12. package/dist/connectors/remote-mcp.js +3 -5
  13. package/dist/credential-contract.d.ts +24 -0
  14. package/dist/credential-contract.js +1 -0
  15. package/dist/credential-rules.d.ts +85 -0
  16. package/dist/credential-rules.js +107 -0
  17. package/dist/credentials.d.ts +4 -100
  18. package/dist/credentials.js +3 -107
  19. package/dist/execute.d.ts +6 -0
  20. package/dist/execute.js +17 -7
  21. package/dist/index.d.ts +35 -56
  22. package/dist/index.js +34 -58
  23. package/dist/invocation.js +19 -4
  24. package/dist/meta-tools.d.ts +4 -0
  25. package/dist/meta-tools.js +8 -4
  26. package/dist/module-contracts.d.ts +19 -0
  27. package/dist/module-contracts.js +1 -0
  28. package/dist/operator-ui/generated.js +2 -2
  29. package/dist/operator-ui/model.d.ts +6 -3
  30. package/dist/operator-ui/view.d.ts +2 -18
  31. package/dist/operator-ui/view.js +3 -20
  32. package/dist/registry.d.ts +4 -1
  33. package/dist/registry.js +4 -6
  34. package/dist/routes/activity.js +1 -1
  35. package/dist/routes/credentials.js +5 -2
  36. package/dist/routes/mcp.js +13 -3
  37. package/dist/routes/oauth-management.d.ts +2 -0
  38. package/dist/routes/oauth-management.js +108 -0
  39. package/dist/routes/oauth.d.ts +0 -1
  40. package/dist/routes/oauth.js +21 -121
  41. package/dist/routes/shared.d.ts +23 -17
  42. package/dist/routes/shared.js +48 -44
  43. package/dist/routes/ui.js +36 -33
  44. package/dist/server.js +6 -26
  45. package/dist/types.d.ts +2 -0
  46. package/dist/ui.d.ts +15 -70
  47. package/dist/ui.js +176 -317
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/documentation/architecture.md +26 -17
  51. package/documentation/auth.md +61 -106
  52. package/documentation/cloudflare.md +1 -1
  53. package/documentation/code-mode.md +2 -2
  54. package/documentation/connectors.md +1 -1
  55. package/documentation/linear.md +1 -1
  56. package/documentation/meta-tools.md +6 -4
  57. package/documentation/mixpanel.md +1 -1
  58. package/documentation/notion.md +2 -2
  59. package/documentation/operations.md +14 -14
  60. package/documentation/operator-ui.md +82 -104
  61. package/documentation/optional-modules-upgrade.md +243 -0
  62. package/documentation/provider-conventions.md +5 -3
  63. package/documentation/revenuecat.md +1 -1
  64. package/documentation/storage-and-credentials.md +59 -40
  65. package/documentation/stripe.md +1 -1
  66. package/documentation/upgrading.md +33 -4
  67. package/ethos.md +22 -30
  68. package/examples/worker/AGENTS.md +3 -1
  69. package/examples/worker/README.md +68 -84
  70. package/examples/worker/src/d1-activity.ts +1 -1
  71. package/examples/worker/src/index.ts +11 -6
  72. package/package.json +17 -1
  73. package/templates/node/AGENTS.md +8 -6
  74. package/templates/node/README.md +56 -67
  75. package/templates/node/package.json +1 -1
  76. package/templates/node/src/file-activity.ts +1 -1
  77. package/templates/node/src/index.ts +11 -12
  78. package/dist/access-tokens.d.ts +0 -31
  79. package/dist/access-tokens.js +0 -236
  80. package/dist/routes/access-tokens.d.ts +0 -6
  81. package/dist/routes/access-tokens.js +0 -83
package/dist/index.d.ts CHANGED
@@ -1,34 +1,9 @@
1
+ import type { CredentialVault } from "./credential-contract.js";
1
2
  import { Registry } from "./registry.js";
2
- import type { ActivityReadGate, ActivityStore } from "./activity.js";
3
- import type { AuthenticatedIdentity, Connector, ConnectaBranding, Executor, IdentityReference, InboundAuth, KVStorage, Logger } from "./types.js";
4
- /** Payload-free activity storage and operator-read policy. */
5
- export interface ConnectaActivityConfig {
6
- /**
7
- * Privacy-minimal downstream tool activity storage. Writes are best-effort
8
- * and never change tool results. Implement `list` to enable the Activity UI.
9
- */
10
- store: ActivityStore;
11
- /**
12
- * Optional authorization gate for the Activity read API. MCP authentication
13
- * is still required first. Omit to admit every authenticated actor.
14
- */
15
- readGate?: ActivityReadGate;
16
- /** Stable deployment label included in activity events, e.g. "production". */
17
- deploymentId?: string;
18
- }
19
- /** Operator-vault encryption. */
20
- export interface ConnectaCredentialsConfig {
21
- /**
22
- * Base64-encoded 32-byte AES key for credentials managed on /credentials.
23
- * Keep this in the runtime's secret store, never in KV or source control.
24
- */
25
- encryptionKey?: string;
26
- }
27
- /** Operator-issued credentials for clients connecting to this deployment. */
28
- export interface ConnectaAccessTokensConfig {
29
- /** Maximum simultaneously active access tokens. Defaults to 100. */
30
- maxActive?: number;
31
- }
3
+ import type { ActivityModule, OperatorSurface } from "./module-contracts.js";
4
+ export type { ActivityModule, OperatorSurface } from "./module-contracts.js";
5
+ export type { CredentialVault, CredentialMetadata } from "./credential-contract.js";
6
+ import type { AuthenticatedIdentity, Connector, Executor, IdentityReference, InboundAuth, KVStorage, Logger } from "./types.js";
32
7
  /** Tool-catalog caching, persistence, stale fallback, and probe deadlines. */
33
8
  export interface ConnectaDiscoveryConfig {
34
9
  /**
@@ -76,7 +51,7 @@ export interface ConnectaCallsConfig {
76
51
  */
77
52
  maxResultBytes?: number;
78
53
  }
79
- /** Budgets for rich output emitted by execute_code programs (`connecta.emit`). */
54
+ /** Budgets for execute_code programs: host calls and rich output (`connecta.emit`). */
80
55
  export interface ConnectaExecuteConfig {
81
56
  /**
82
57
  * Aggregate serialized bytes `connecta.emit` accepts per run. Default
@@ -87,6 +62,18 @@ export interface ConnectaExecuteConfig {
87
62
  maxEmittedBytes?: number;
88
63
  /** Content blocks `connecta.emit` accepts per run. Default 32. */
89
64
  maxEmittedBlocks?: number;
65
+ /**
66
+ * Host calls one program may make. Default 20. Invalid values fall back to
67
+ * the default.
68
+ */
69
+ maxHostCalls?: number;
70
+ /**
71
+ * Deadline for each host call a program makes, in milliseconds. Default
72
+ * 15_000. Raise it for providers whose legitimate calls run longer, such as
73
+ * analytics queries; `call_tool`'s own `timeoutMs` is unaffected. Invalid
74
+ * values fall back to the default.
75
+ */
76
+ hostCallTimeoutMs?: number;
90
77
  }
91
78
  export interface AdmissionPoolConfig {
92
79
  /** Simultaneous work admitted to this pool. */
@@ -116,22 +103,22 @@ export interface ConnectaAdmissionConfig {
116
103
  code?: AdmissionPoolConfig;
117
104
  }
118
105
  /** Config-owned identity rules for one deployment and tenant. */
106
+ export type ConnectorPermission = "all" | "none" | readonly string[];
119
107
  export interface ConnectaIdentityConfig {
120
108
  /** Connector ids this admitted identity may discover and call. */
121
109
  connectorAccess?(identity: Readonly<AuthenticatedIdentity>): "all" | readonly string[] | Promise<"all" | readonly string[]>;
122
- /**
123
- * Whether a human may manage deployment access tokens and global activity.
124
- * Connector access already permits that human to manage the visible
125
- * connector's shared or personal auth. Omit to preserve the existing
126
- * all-interactive-humans operator rule.
127
- */
128
- operatorAccess?(principal: Readonly<IdentityReference>): boolean | Promise<boolean>;
110
+ /** Global payload-free activity reads. Defaults to interactive humans. */
111
+ activityAccess?(principal: Readonly<IdentityReference>): boolean | Promise<boolean>;
112
+ /** Shared credential and OAuth administration. Defaults to none. */
113
+ credentialAdministration?(identity: Readonly<AuthenticatedIdentity>): ConnectorPermission | Promise<ConnectorPermission>;
114
+ /** Connecting or changing the caller's personal account. Defaults to none. */
115
+ personalConnection?(identity: Readonly<AuthenticatedIdentity>): ConnectorPermission | Promise<ConnectorPermission>;
129
116
  }
130
117
  export interface ConnectaConfig {
131
118
  connectors: Connector[];
132
119
  /** Inbound auth adapters. Includes bearerToken(...); omit for open (dev). */
133
120
  auth?: InboundAuth | InboundAuth[];
134
- /** Identity-derived connector visibility and deployment operator membership. */
121
+ /** Code-derived connection visibility and independent management permissions. */
135
122
  identity?: ConnectaIdentityConfig;
136
123
  /** KVStorage impl. Defaults to memoryStorage(). */
137
124
  storage?: KVStorage;
@@ -140,15 +127,12 @@ export interface ConnectaConfig {
140
127
  * HTTPS URL also redirects matching inbound HTTP requests to HTTPS.
141
128
  */
142
129
  publicUrl?: string;
143
- /** Payload-free tool activity storage and operator-read policy. */
144
- activity?: ConnectaActivityConfig;
145
- /** Operator credential vault settings. */
146
- credentials?: ConnectaCredentialsConfig;
147
- /**
148
- * Named, revocable Bearer tokens for MCP clients. Creation and mutation
149
- * require an eligible interactive operator; token secrets are returned once.
150
- */
151
- accessTokens?: ConnectaAccessTokensConfig;
130
+ /** Optional recorder and reader, created by activityHistory() from /activity. */
131
+ activity?: ActivityModule;
132
+ /** Replaceable owner-partitioned credential storage. Omit for config-owned secrets. */
133
+ vault?: CredentialVault;
134
+ /** Optional connection UI, created by operatorUi() from /ui. */
135
+ ui?: OperatorSurface;
152
136
  /** Tool-catalog caching, persistence, stale fallback, and probe deadlines. */
153
137
  discovery?: ConnectaDiscoveryConfig;
154
138
  /** Deployment-wide call deadlines and result paging threshold. */
@@ -157,9 +141,8 @@ export interface ConnectaConfig {
157
141
  execute?: ConnectaExecuteConfig;
158
142
  /** Bounded MCP and fallback code-mode admission. */
159
143
  admission?: ConnectaAdmissionConfig;
160
- /** Optional browser UI and OAuth result-page labels. */
161
- branding?: ConnectaBranding;
162
- logger?: Logger;
144
+ /** Diagnostic output. Use "silent" to disable all diagnostic logging. */
145
+ logger?: Logger | "silent";
163
146
  serverInfo?: {
164
147
  name?: string;
165
148
  version?: string;
@@ -198,14 +181,10 @@ export { ConnectorCallError } from "./errors.js";
198
181
  export type { ConnectorCallErrorCode, CallErrorDetails } from "./errors.js";
199
182
  export { validateToolInput } from "./validate.js";
200
183
  export type { ValidateToolInputOptions } from "./validate.js";
201
- export { bearerToken } from "./auth/bearer.js";
202
- export type { BearerTokenOptions } from "./auth/bearer.js";
203
- export type { AccessTokenMetadata, CreatedAccessToken, } from "./access-tokens.js";
204
184
  export { memoryStorage } from "./storage/memory.js";
205
185
  export { CONNECTA_VERSION } from "./version.js";
206
186
  export type { Registry } from "./registry.js";
207
187
  export type { RemoteMcpOptions, RemoteMcpAuth, RemoteMcpRedirectPolicy, } from "./connectors/remote-mcp.js";
208
188
  export type { ApiOptions, ApiTool } from "./connectors/api.js";
209
- export type { CatalogDriftCounts, CatalogDriftReport, Connector, ConnectorCallAdmissionInput, ConnectorCallAdmissionPolicy, ConnectorCallAdmissionRule, ConnectorRollingWindowBudget, ConnectaBranding, ConnectorCredentialAccess, ConnectorCredentialConfig, ConnectorCredentialFieldConfig, ConnectorCredentialValues, ConnectorContext, ConnectorUsageGuide, ConnectorStatus, CredentialTestResult, AdmittingExecutor, AdmissionSnapshot, ExecuteResult, Executor, ExecutorLease, ExecutorProvider, InboundAuth, InboundAuthRuntimeContext, UiAuthConfig, AuthResult, AuthenticatedIdentity, IdentityReference, JsonSchema, KVStorage, Logger, ToolDef, ToolAnnotations, } from "./types.js";
189
+ export type { CatalogDriftCounts, CatalogDriftReport, ConnectaBranding, Connector, ConnectorCallAdmissionInput, ConnectorCallAdmissionPolicy, ConnectorCallAdmissionRule, ConnectorRollingWindowBudget, ConnectorCredentialAccess, ConnectorCredentialConfig, ConnectorCredentialFieldConfig, ConnectorCredentialValues, ConnectorContext, ConnectorUsageGuide, ConnectorStatus, CredentialTestResult, AdmittingExecutor, AdmissionSnapshot, ExecuteResult, Executor, ExecutorLease, ExecutorProvider, InboundAuth, InboundAuthRuntimeContext, UiAuthConfig, AuthResult, AuthenticatedIdentity, IdentityReference, JsonSchema, KVStorage, Logger, ToolDef, ToolAnnotations, } from "./types.js";
210
190
  export type { ActivityActor, ActivityCallSource, ActivityOutcome, ActivityPage, ActivityReadActor, ActivityReadEvent, ActivityReader, ActivityReadGate, ActivityReadPage, ActivitySink, ActivityStore, AgentFriction, CatalogDriftActivityEvent, ToolCallActivityEvent, } from "./activity.js";
211
- export { InvalidActivityCursorError } from "./activity.js";
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
- import { CredentialVault, credentialTestRule, describeCredentialTestMismatch, } from "./credentials.js";
2
- import { AccessTokenManager } from "./access-tokens.js";
1
+ import { credentialTestRule, describeCredentialTestMismatch, } from "./credential-rules.js";
3
2
  import { Registry } from "./registry.js";
4
3
  import { createFetchHandler } from "./server.js";
5
- import { droppedBrandingUrls, droppedUiAuthUrls } from "./ui.js";
4
+ import { droppedBrandingUrls, droppedUiAuthUrls } from "./branding.js";
6
5
  import { memoryStorage } from "./storage/memory.js";
7
6
  import { CONNECTA_VERSION } from "./version.js";
8
7
  import { AdmissionController, executorName, isAdmittingExecutor, withExecutorAdmission, } from "./executor-admission.js";
@@ -53,21 +52,15 @@ const CONFIG_SCHEMA = {
53
52
  auth: null,
54
53
  identity: {
55
54
  connectorAccess: null,
56
- operatorAccess: null,
55
+ activityAccess: null,
56
+ credentialAdministration: null,
57
+ personalConnection: null,
57
58
  },
58
59
  storage: null,
59
60
  publicUrl: null,
60
- activity: {
61
- store: null,
62
- readGate: null,
63
- deploymentId: null,
64
- },
65
- credentials: {
66
- encryptionKey: null,
67
- },
68
- accessTokens: {
69
- maxActive: null,
70
- },
61
+ activity: null,
62
+ vault: null,
63
+ ui: null,
71
64
  discovery: {
72
65
  concurrency: null,
73
66
  catalogTtlSeconds: null,
@@ -82,25 +75,13 @@ const CONFIG_SCHEMA = {
82
75
  execute: {
83
76
  maxEmittedBytes: null,
84
77
  maxEmittedBlocks: null,
78
+ maxHostCalls: null,
79
+ hostCallTimeoutMs: null,
85
80
  },
86
81
  admission: {
87
82
  requests: admissionPoolSchema,
88
83
  code: admissionPoolSchema,
89
84
  },
90
- branding: {
91
- productName: null,
92
- productUrl: null,
93
- ownerName: null,
94
- ownerUrl: null,
95
- description: null,
96
- pageTitle: null,
97
- favicon: {
98
- svg: null,
99
- ico: null,
100
- href: null,
101
- },
102
- themeColor: null,
103
- },
104
85
  logger: null,
105
86
  serverInfo: {
106
87
  name: null,
@@ -144,17 +125,24 @@ function rejectUnknownOptions(paths) {
144
125
  if (paths.length === 0)
145
126
  return;
146
127
  throw new Error(`Unknown Connecta configuration option${paths.length === 1 ? "" : "s"}:\n` +
147
- paths.map((path) => `- ${path}`).join("\n"));
128
+ paths.map((path) => `- ${path}`).join("\n") +
129
+ (paths.includes("ConnectaConfig.credentials") ? "\nUse vault: encryptedCredentialVault(storage, key) from @zackbart/connecta/credentials." : "") +
130
+ (paths.includes("ConnectaConfig.accessTokens") ? "\nConnecta-issued tokens were removed. Configure an inbound auth adapter instead." : "") +
131
+ (paths.includes("ConnectaConfig.branding") ? "\nMove branding into ui: operatorUi({ branding }) from @zackbart/connecta/ui." : ""));
148
132
  }
149
133
  /** Reject JavaScript typos and removed options before construction does work. */
150
134
  function assertKnownConfig(config) {
151
135
  rejectUnknownOptions(unknownOptionPaths(config, "ConnectaConfig", CONFIG_SCHEMA));
136
+ if (config.vault && ["get", "getAll", "set", "setAll", "metadata", "delete"].some(key => typeof config.vault[key] !== "function"))
137
+ throw new Error("ConnectaConfig.vault must implement CredentialVault");
138
+ if (config.ui && (typeof config.ui.handle !== "function" || typeof config.ui.credentialHandoffUrl !== "function" || !Array.isArray(config.ui.reservedPaths)))
139
+ throw new Error("ConnectaConfig.ui must be created with operatorUi(...)");
152
140
  const activity = config.activity;
153
141
  if (activity !== undefined &&
154
142
  (typeof activity !== "object" ||
155
143
  activity === null ||
156
- typeof activity.store?.record !== "function")) {
157
- throw new Error("ConnectaConfig.activity.store must implement record(event)");
144
+ typeof activity.recordTool !== "function")) {
145
+ throw new Error("ConnectaConfig.activity must be created with activityHistory(...)");
158
146
  }
159
147
  }
160
148
  /**
@@ -171,7 +159,7 @@ function warnInsecureConfig(config, inboundAuth, logger) {
171
159
  if (inboundAuth.length === 0 &&
172
160
  (hasCredentialConnector || oauthConnectors.length > 0)) {
173
161
  logger.warn("[connecta] running with no inbound authentication: any caller can " +
174
- "invoke every connector and read or overwrite stored credentials. " +
162
+ "invoke every shared connector. " +
175
163
  "Configure `auth` (for example bearerToken(...) or Clerk) to gate access.");
176
164
  }
177
165
  // Unset publicUrl with OAuth connectors: the downstream redirect_uri is
@@ -186,7 +174,7 @@ function warnInsecureConfig(config, inboundAuth, logger) {
186
174
  // Branding URLs that failed their scheme gate. Rendering silently falls back
187
175
  // (a bad URL must not take the page down), so this warning is the only way an
188
176
  // operator learns their value never reached the page.
189
- const dropped = droppedBrandingUrls(config.branding);
177
+ const dropped = droppedBrandingUrls(config.ui?.branding);
190
178
  if (dropped.length > 0) {
191
179
  logger.warn(`[connecta] branding ${dropped.join(", ")} dropped: a branding URL is ` +
192
180
  "used as an href, so it must be an absolute http(s) URL (favicon.href " +
@@ -250,26 +238,11 @@ export function createConnecta(config) {
250
238
  '"@cloudflare/codemode" on Workers.');
251
239
  }
252
240
  const storage = config.storage ?? memoryStorage();
253
- const logger = config.logger ?? defaultLogger();
254
- const credentialConnectors = config.connectors.filter((c) => c.credential);
255
- const encryptionKey = config.credentials?.encryptionKey;
256
- if (credentialConnectors.length > 0 && !encryptionKey) {
257
- logger.warn("Human-managed credentials are unavailable because " +
258
- "credentials.encryptionKey is not configured for connectors: " +
259
- credentialConnectors.map((c) => c.id).join(", "));
260
- }
261
- const credentialVault = encryptionKey
262
- ? new CredentialVault(storage, encryptionKey)
263
- : undefined;
241
+ const logger = config.logger === "silent"
242
+ ? { debug() { }, info() { }, warn() { }, error() { } }
243
+ : config.logger ?? defaultLogger();
244
+ const credentialVault = config.vault;
264
245
  const configuredAuth = normalizeAuth(config.auth);
265
- const accessTokens = config.accessTokens
266
- ? new AccessTokenManager(storage, config.accessTokens)
267
- : undefined;
268
- if (accessTokens &&
269
- !configuredAuth.some((provider) => provider.interactiveOperator)) {
270
- throw new Error("accessTokens requires an interactive operator auth provider: only an eligible " +
271
- "operator may create, rename, or revoke deployment access tokens");
272
- }
273
246
  const serverInfo = {
274
247
  ...config.serverInfo,
275
248
  name: config.serverInfo?.name ?? "connecta",
@@ -279,9 +252,11 @@ export function createConnecta(config) {
279
252
  storage,
280
253
  logger,
281
254
  credentialVault,
255
+ credentialUi: Boolean(config.ui),
282
256
  catalogDriftActivity: config.activity?.store
283
257
  ? {
284
258
  sink: config.activity.store,
259
+ recordDrift: config.activity.recordDrift,
285
260
  serverInfo,
286
261
  ...(config.activity.deploymentId !== undefined
287
262
  ? { deploymentId: config.activity.deploymentId }
@@ -293,7 +268,7 @@ export function createConnecta(config) {
293
268
  toolCatalogStaleSeconds: config.discovery?.staleCatalogSeconds,
294
269
  maxResultBytes: config.calls?.maxResultBytes,
295
270
  });
296
- const inboundAuth = normalizeAuth(accessTokens ? [accessTokens.auth, ...configuredAuth] : configuredAuth);
271
+ const inboundAuth = configuredAuth;
297
272
  warnInsecureConfig(config, inboundAuth, logger);
298
273
  const requestAdmission = admissionController(config.admission?.requests, REQUEST_ADMISSION_DEFAULTS);
299
274
  const configuredCodeAdmission = admissionController(config.admission?.code, CODE_ADMISSION_DEFAULTS);
@@ -319,6 +294,7 @@ export function createConnecta(config) {
319
294
  serverInfo,
320
295
  logger,
321
296
  activity: config.activity?.store,
297
+ activityModule: config.activity,
322
298
  activityReadGate: config.activity?.readGate,
323
299
  activityDeploymentId: config.activity?.deploymentId,
324
300
  executor,
@@ -329,10 +305,12 @@ export function createConnecta(config) {
329
305
  discoveryConcurrency: config.discovery?.concurrency,
330
306
  maxEmittedBytes: config.execute?.maxEmittedBytes,
331
307
  maxEmittedBlocks: config.execute?.maxEmittedBlocks,
308
+ maxHostCalls: config.execute?.maxHostCalls,
309
+ hostCallTimeoutMs: config.execute?.hostCallTimeoutMs,
332
310
  credentialVault,
333
- accessTokens,
311
+ ui: config.ui,
334
312
  deploymentInfo: config.deploymentInfo,
335
- branding: config.branding,
313
+ branding: config.ui?.branding,
336
314
  });
337
315
  let closePromise;
338
316
  return {
@@ -358,7 +336,5 @@ export { ConnectorCallError } from "./errors.js";
358
336
  // implement the Connector interface directly. Returns the error rather than
359
337
  // throwing so the caller decides what to do with it.
360
338
  export { validateToolInput } from "./validate.js";
361
- export { bearerToken } from "./auth/bearer.js";
362
339
  export { memoryStorage } from "./storage/memory.js";
363
340
  export { CONNECTA_VERSION } from "./version.js";
364
- export { InvalidActivityCursorError } from "./activity.js";
@@ -1,4 +1,3 @@
1
- import { recordToolActivity, } from "./activity.js";
2
1
  import { isCallAdmissionError } from "./call-admission.js";
3
2
  import { classifyCallError, ConnectorCallError, echoedCallArgs, framingError, } from "./errors.js";
4
3
  import { unwrapMcpResult } from "./mcp-result.js";
@@ -28,7 +27,7 @@ function callerCancelledDetails() {
28
27
  function recoveryMode(registry, connector, baseUrl) {
29
28
  if (connector.startAuth)
30
29
  return "oauth";
31
- if (connector.credential &&
30
+ if (registry.credentialUiAvailable() && connector.credential &&
32
31
  registry.contextFor(connector.id, baseUrl).credential) {
33
32
  return "operator_config";
34
33
  }
@@ -116,7 +115,7 @@ export class InvocationService {
116
115
  : attempted;
117
116
  if (!identity)
118
117
  return;
119
- recordToolActivity(this.activity, {
118
+ this.activity?.recordTool?.(this.activity, {
120
119
  connectorId: identity.connectorId,
121
120
  toolName: identity.toolName,
122
121
  address: `${identity.connectorId}.${identity.toolName}`,
@@ -185,7 +184,23 @@ export class InvocationService {
185
184
  };
186
185
  const failed = (error) => {
187
186
  const diagnostics = timing();
188
- const details = enrich(error, resolved ?? activityTarget);
187
+ const target = resolved ?? activityTarget;
188
+ const details = enrich(error, target);
189
+ // Activity rows stay payload-free by construction; the operator's log is
190
+ // where the downstream reason goes, bounded and without arguments.
191
+ if (target && details.code !== "destructive_tool_requires_approval") {
192
+ this.registry
193
+ .contextFor(target.connector.id, this.catalog.baseUrl, this.catalog.requestScope)
194
+ .logger.warn("[connecta] call failed", {
195
+ connector: target.connector.id,
196
+ tool: target.toolName,
197
+ source: context.source,
198
+ code: details.code,
199
+ attempts,
200
+ durationMs: Date.now() - started,
201
+ message: String(details.message ?? "").slice(0, 300),
202
+ });
203
+ }
189
204
  record(details.code === "timeout"
190
205
  ? "timeout"
191
206
  : details.code === "cancelled"
@@ -94,6 +94,8 @@ export declare function createMetaTools(registry: RegistryView, baseUrl: string,
94
94
  /** Maximum simultaneous connector discovery operations. Default 4. */
95
95
  discoveryConcurrency?: number | undefined;
96
96
  activity?: ActivityRequestContext | undefined;
97
+ canManageAuth?: ((id: string) => boolean) | undefined;
98
+ credentialHandoffUrl?: string | undefined;
97
99
  /** Inbound request cancellation shared by every call this request makes. */
98
100
  requestSignal?: AbortSignal | undefined;
99
101
  /** Runtime-owned tail for stale catalog refreshes. */
@@ -120,6 +122,8 @@ export declare function registerMetaTools(server: McpServer, registry: RegistryV
120
122
  probeTimeoutMs?: number | undefined;
121
123
  discoveryConcurrency?: number | undefined;
122
124
  activity?: ActivityRequestContext | undefined;
125
+ canManageAuth?: ((id: string) => boolean) | undefined;
126
+ credentialHandoffUrl?: string | undefined;
123
127
  requestSignal?: AbortSignal | undefined;
124
128
  defer?: DeferredWork | undefined;
125
129
  }): void;
@@ -448,12 +448,12 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
448
448
  });
449
449
  }
450
450
  const ctx = registry.contextFor(connector.id, baseUrl, requestScope);
451
- if (!ctx.credential) {
451
+ if (!ctx.credential || !opts.credentialHandoffUrl) {
452
452
  return jsonResult({
453
453
  connector: connector.id,
454
454
  recovery: "unavailable",
455
- message: "Credential storage is not configured. Configure " +
456
- "credentials.encryptionKey, redeploy, then call " +
455
+ message: "Credential recovery needs both a vault and the optional UI. Configure " +
456
+ "vault and ui in deployment code, then call " +
457
457
  "authorize_connector again.",
458
458
  });
459
459
  }
@@ -474,7 +474,7 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
474
474
  label: connector.credential.label,
475
475
  fields,
476
476
  },
477
- operatorUrl: new URL("/credentials", baseUrl).toString(),
477
+ operatorUrl: opts.credentialHandoffUrl,
478
478
  instructions: "Have the operator open operatorUrl, set and test the credential, " +
479
479
  "then retry the original call. No redeploy is needed. " +
480
480
  (connector.authScope === "personal"
@@ -482,6 +482,8 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
482
482
  : "Shared credential mutation requires a signed-in human with access to this connector."),
483
483
  });
484
484
  }
485
+ if (!opts.canManageAuth?.(connector.id))
486
+ return jsonResult({ connector: connector.id, recovery: "unavailable", message: "Your identity is not permitted to manage authentication for this connection." });
485
487
  const ctx = registry.contextFor(connector.id, baseUrl, requestScope);
486
488
  try {
487
489
  const status = await connector.startAuth(ctx, args.force !== undefined ? { force: args.force } : {});
@@ -581,6 +583,8 @@ export function registerMetaTools(server, registry, ctx) {
581
583
  probeTimeoutMs: ctx.probeTimeoutMs,
582
584
  discoveryConcurrency: ctx.discoveryConcurrency,
583
585
  activity: ctx.activity,
586
+ canManageAuth: ctx.canManageAuth,
587
+ credentialHandoffUrl: ctx.credentialHandoffUrl,
584
588
  requestSignal: ctx.requestSignal,
585
589
  defer: ctx.defer,
586
590
  });
@@ -0,0 +1,19 @@
1
+ import type { ConnectaBranding } from "./types.js";
2
+ import type { ActivityStore, ActivityReadGate, recordToolActivity, recordCatalogDriftActivity } from "./activity.js";
3
+ import type { RouteContext } from "./routes/shared.js";
4
+ /** Construction-time UI contract. Core never imports the implementation. */
5
+ export interface OperatorSurface {
6
+ readonly branding?: ConnectaBranding;
7
+ readonly reservedPaths: readonly string[];
8
+ credentialHandoffUrl(baseUrl: string): string;
9
+ handle(context: RouteContext): Promise<Response | null>;
10
+ }
11
+ /** Optional recording and reading callbacks supplied by /activity. */
12
+ export interface ActivityModule {
13
+ readonly store: ActivityStore;
14
+ readonly deploymentId?: string;
15
+ readonly readGate?: ActivityReadGate;
16
+ handle(context: RouteContext): Promise<Response | null>;
17
+ readonly recordTool: typeof recordToolActivity;
18
+ readonly recordDrift: typeof recordCatalogDriftActivity;
19
+ }
@@ -0,0 +1 @@
1
+ export {};