@zackbart/connecta 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +28 -15
- package/CHANGELOG.md +493 -0
- package/README.md +19 -4
- package/bin/connecta.mjs +44 -3
- package/dist/access-tokens.d.ts +0 -1
- package/dist/access-tokens.js +0 -1
- package/dist/activity.d.ts +50 -2
- package/dist/activity.js +34 -1
- package/dist/apps-shell.d.ts +0 -1
- package/dist/apps-shell.js +0 -1
- package/dist/auth/bearer.d.ts +0 -1
- package/dist/auth/bearer.js +0 -1
- package/dist/auth/clerk.d.ts +0 -1
- package/dist/auth/clerk.js +45 -9
- package/dist/auth/downstream-oauth.d.ts +0 -1
- package/dist/auth/downstream-oauth.js +0 -1
- package/dist/call-admission.d.ts +0 -1
- package/dist/call-admission.js +0 -1
- package/dist/catalog-drift.d.ts +99 -0
- package/dist/catalog-drift.js +251 -0
- package/dist/catalog-fingerprint.d.ts +0 -1
- package/dist/catalog-fingerprint.js +0 -1
- package/dist/catalog-limits.d.ts +0 -1
- package/dist/catalog-limits.js +0 -1
- package/dist/catalog-service.d.ts +0 -1
- package/dist/catalog-service.js +0 -1
- package/dist/catalog.d.ts +0 -1
- package/dist/catalog.js +0 -1
- package/dist/concurrency.d.ts +0 -1
- package/dist/concurrency.js +0 -1
- package/dist/connector-scope.d.ts +0 -1
- package/dist/connector-scope.js +0 -1
- package/dist/connectors/api.d.ts +29 -21
- package/dist/connectors/api.js +45 -19
- package/dist/connectors/guarded-fetch.d.ts +106 -0
- package/dist/connectors/guarded-fetch.js +289 -0
- package/dist/connectors/remote-mcp.d.ts +0 -1
- package/dist/connectors/remote-mcp.js +0 -1
- package/dist/credentials.d.ts +0 -1
- package/dist/credentials.js +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js +0 -1
- package/dist/execute.d.ts +0 -1
- package/dist/execute.js +0 -1
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +28 -1
- package/dist/executor-result.d.ts +0 -1
- package/dist/executor-result.js +0 -1
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-child.js +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -1
- package/dist/executors/quickjs-protocol.js +0 -1
- package/dist/executors/quickjs-runtime.d.ts +0 -1
- package/dist/executors/quickjs-runtime.js +0 -1
- package/dist/executors/quickjs.d.ts +0 -1
- package/dist/executors/quickjs.js +2 -1
- package/dist/favicon.d.ts +0 -1
- package/dist/favicon.js +0 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -7
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +0 -1
- package/dist/json-schema.d.ts +0 -1
- package/dist/json-schema.js +0 -1
- package/dist/mcp-result.d.ts +0 -1
- package/dist/mcp-result.js +0 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/operator-ui/generated.d.ts +2 -3
- package/dist/operator-ui/generated.js +3 -4
- package/dist/operator-ui/model.d.ts +10 -1
- package/dist/operator-ui/model.js +0 -1
- package/dist/operator-ui/view.d.ts +167 -0
- package/dist/operator-ui/view.js +270 -0
- package/dist/providers/cloudflare.d.ts +5 -2
- package/dist/providers/cloudflare.js +435 -328
- package/dist/providers/linear.d.ts +26 -5
- package/dist/providers/linear.js +32 -60
- package/dist/providers/mixpanel.d.ts +35 -4
- package/dist/providers/mixpanel.js +53 -78
- package/dist/providers/notion.d.ts +0 -1
- package/dist/providers/notion.js +87 -42
- package/dist/providers/stripe.d.ts +13 -1
- package/dist/providers/stripe.js +31 -59
- package/dist/registry.d.ts +34 -2
- package/dist/registry.js +85 -5
- package/dist/retired-toolkits.d.ts +0 -1
- package/dist/retired-toolkits.js +0 -1
- package/dist/routes/access-tokens.d.ts +0 -1
- package/dist/routes/access-tokens.js +0 -1
- package/dist/routes/activity.d.ts +0 -1
- package/dist/routes/activity.js +0 -1
- package/dist/routes/credentials.d.ts +0 -1
- package/dist/routes/credentials.js +0 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +0 -1
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/ui.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +11 -1
- package/dist/skills.d.ts +0 -1
- package/dist/skills.js +0 -1
- package/dist/storage/file.d.ts +0 -1
- package/dist/storage/file.js +0 -1
- package/dist/storage/memory.d.ts +0 -1
- package/dist/storage/memory.js +0 -1
- package/dist/timeout.d.ts +0 -1
- package/dist/timeout.js +0 -1
- package/dist/tool-safety.d.ts +0 -1
- package/dist/tool-safety.js +0 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +13 -137
- package/dist/validate.d.ts +13 -15
- package/dist/validate.js +24 -11
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/documentation/architecture.md +186 -5
- package/documentation/auth.md +11 -0
- package/documentation/call-admission.md +142 -6
- package/documentation/cloudflare.md +95 -27
- package/documentation/code-mode.md +12 -12
- package/documentation/connector-guides.md +172 -5
- package/documentation/connectors.md +98 -7
- package/documentation/linear.md +26 -9
- package/documentation/mcp-ui-design.md +2 -2
- package/documentation/mixpanel.md +48 -16
- package/documentation/notion.md +16 -0
- package/documentation/operations.md +283 -5
- package/documentation/operator-ui.md +120 -5
- package/documentation/provider-audit.md +160 -0
- package/documentation/provider-conventions.md +631 -0
- package/documentation/request-admission.md +167 -5
- package/documentation/rich-output-design.md +1 -2
- package/documentation/stripe.md +27 -12
- package/ethos.md +14 -9
- package/examples/worker/README.md +96 -12
- package/examples/worker/src/index.ts +46 -1
- package/examples/worker/wrangler.jsonc +14 -1
- package/package.json +4 -5
- package/templates/node/.dockerignore +10 -0
- package/templates/node/.env.example +40 -3
- package/templates/node/AGENTS.md +11 -1
- package/templates/node/Dockerfile +49 -0
- package/templates/node/README.md +117 -2
- package/templates/node/docker-compose.yml +39 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +182 -0
- package/templates/node/src/index.ts +80 -4
- package/assets/connecta-clay-hero.png +0 -0
- package/dist/access-tokens.d.ts.map +0 -1
- package/dist/access-tokens.js.map +0 -1
- package/dist/activity.d.ts.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/apps-shell.d.ts.map +0 -1
- package/dist/apps-shell.js.map +0 -1
- package/dist/auth/bearer.d.ts.map +0 -1
- package/dist/auth/bearer.js.map +0 -1
- package/dist/auth/clerk.d.ts.map +0 -1
- package/dist/auth/clerk.js.map +0 -1
- package/dist/auth/downstream-oauth.d.ts.map +0 -1
- package/dist/auth/downstream-oauth.js.map +0 -1
- package/dist/call-admission.d.ts.map +0 -1
- package/dist/call-admission.js.map +0 -1
- package/dist/catalog-fingerprint.d.ts.map +0 -1
- package/dist/catalog-fingerprint.js.map +0 -1
- package/dist/catalog-limits.d.ts.map +0 -1
- package/dist/catalog-limits.js.map +0 -1
- package/dist/catalog-service.d.ts.map +0 -1
- package/dist/catalog-service.js.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/catalog.js.map +0 -1
- package/dist/concurrency.d.ts.map +0 -1
- package/dist/concurrency.js.map +0 -1
- package/dist/connector-scope.d.ts.map +0 -1
- package/dist/connector-scope.js.map +0 -1
- package/dist/connectors/api.d.ts.map +0 -1
- package/dist/connectors/api.js.map +0 -1
- package/dist/connectors/remote-mcp.d.ts.map +0 -1
- package/dist/connectors/remote-mcp.js.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js.map +0 -1
- package/dist/executor-admission.d.ts.map +0 -1
- package/dist/executor-admission.js.map +0 -1
- package/dist/executor-result.d.ts.map +0 -1
- package/dist/executor-result.js.map +0 -1
- package/dist/executors/quickjs-child.d.ts.map +0 -1
- package/dist/executors/quickjs-child.js.map +0 -1
- package/dist/executors/quickjs-protocol.d.ts.map +0 -1
- package/dist/executors/quickjs-protocol.js.map +0 -1
- package/dist/executors/quickjs-runtime.d.ts.map +0 -1
- package/dist/executors/quickjs-runtime.js.map +0 -1
- package/dist/executors/quickjs.d.ts.map +0 -1
- package/dist/executors/quickjs.js.map +0 -1
- package/dist/favicon.d.ts.map +0 -1
- package/dist/favicon.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-schema.d.ts.map +0 -1
- package/dist/json-schema.js.map +0 -1
- package/dist/mcp-result.d.ts.map +0 -1
- package/dist/mcp-result.js.map +0 -1
- package/dist/meta-tools.d.ts.map +0 -1
- package/dist/meta-tools.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/operator-ui/generated.d.ts.map +0 -1
- package/dist/operator-ui/generated.js.map +0 -1
- package/dist/operator-ui/model.d.ts.map +0 -1
- package/dist/operator-ui/model.js.map +0 -1
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/linear.d.ts.map +0 -1
- package/dist/providers/linear.js.map +0 -1
- package/dist/providers/mixpanel.d.ts.map +0 -1
- package/dist/providers/mixpanel.js.map +0 -1
- package/dist/providers/notion.d.ts.map +0 -1
- package/dist/providers/notion.js.map +0 -1
- package/dist/providers/stripe.d.ts.map +0 -1
- package/dist/providers/stripe.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/retired-toolkits.d.ts.map +0 -1
- package/dist/retired-toolkits.js.map +0 -1
- package/dist/routes/access-tokens.d.ts.map +0 -1
- package/dist/routes/access-tokens.js.map +0 -1
- package/dist/routes/activity.d.ts.map +0 -1
- package/dist/routes/activity.js.map +0 -1
- package/dist/routes/credentials.d.ts.map +0 -1
- package/dist/routes/credentials.js.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/mcp.js.map +0 -1
- package/dist/routes/oauth.d.ts.map +0 -1
- package/dist/routes/oauth.js.map +0 -1
- package/dist/routes/shared.d.ts.map +0 -1
- package/dist/routes/shared.js.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/storage/file.d.ts.map +0 -1
- package/dist/storage/file.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js.map +0 -1
- package/dist/tool-safety.d.ts.map +0 -1
- package/dist/tool-safety.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/examples/node/README.md +0 -52
- package/examples/node/src/index.ts +0 -71
- package/src/access-tokens.ts +0 -289
- package/src/activity.ts +0 -234
- package/src/apps-shell.ts +0 -370
- package/src/auth/bearer.ts +0 -61
- package/src/auth/clerk.ts +0 -553
- package/src/auth/downstream-oauth.ts +0 -575
- package/src/call-admission.ts +0 -521
- package/src/catalog-fingerprint.ts +0 -52
- package/src/catalog-limits.ts +0 -11
- package/src/catalog-service.ts +0 -1153
- package/src/catalog.ts +0 -790
- package/src/concurrency.ts +0 -39
- package/src/connector-scope.ts +0 -66
- package/src/connectors/api.ts +0 -159
- package/src/connectors/remote-mcp.ts +0 -1090
- package/src/credentials.ts +0 -428
- package/src/errors.ts +0 -364
- package/src/execute.ts +0 -1406
- package/src/executor-admission.ts +0 -328
- package/src/executor-result.ts +0 -107
- package/src/executors/quickjs-child.ts +0 -172
- package/src/executors/quickjs-protocol.ts +0 -99
- package/src/executors/quickjs-runtime.ts +0 -470
- package/src/executors/quickjs.ts +0 -776
- package/src/favicon.ts +0 -53
- package/src/index.ts +0 -666
- package/src/invocation.ts +0 -655
- package/src/json-schema.ts +0 -11
- package/src/mcp-result.ts +0 -43
- package/src/meta-tools.ts +0 -1607
- package/src/node.ts +0 -206
- package/src/operator-ui/browser.css +0 -423
- package/src/operator-ui/browser.ts +0 -1281
- package/src/operator-ui/generated.ts +0 -4
- package/src/operator-ui/model.ts +0 -111
- package/src/providers/cloudflare.ts +0 -3803
- package/src/providers/linear.ts +0 -301
- package/src/providers/mixpanel.ts +0 -228
- package/src/providers/notion.ts +0 -1879
- package/src/providers/stripe.ts +0 -306
- package/src/registry.ts +0 -1085
- package/src/retired-toolkits.ts +0 -14
- package/src/routes/access-tokens.ts +0 -115
- package/src/routes/activity.ts +0 -215
- package/src/routes/credentials.ts +0 -254
- package/src/routes/mcp.ts +0 -429
- package/src/routes/oauth.ts +0 -322
- package/src/routes/shared.ts +0 -247
- package/src/routes/ui.ts +0 -152
- package/src/server.ts +0 -195
- package/src/skills.ts +0 -271
- package/src/storage/file.ts +0 -123
- package/src/storage/memory.ts +0 -44
- package/src/timeout.ts +0 -84
- package/src/tool-safety.ts +0 -15
- package/src/types.ts +0 -523
- package/src/ui.ts +0 -744
- package/src/validate.ts +0 -387
- package/src/version.ts +0 -7
package/bin/connecta.mjs
CHANGED
|
@@ -64,7 +64,7 @@ async function init() {
|
|
|
64
64
|
// Restore both conventions explicitly in the generated project.
|
|
65
65
|
await writeFile(
|
|
66
66
|
join(stage, ".gitignore"),
|
|
67
|
-
".connecta-state.json\n.env\nnode_modules/\n",
|
|
67
|
+
".connecta-state.json\n.connecta-activity.jsonl\n.env\nnode_modules/\n",
|
|
68
68
|
);
|
|
69
69
|
await rm(join(stage, "CLAUDE.md"), { force: true });
|
|
70
70
|
try {
|
|
@@ -186,6 +186,20 @@ async function doctor() {
|
|
|
186
186
|
if (health.status !== "ok") {
|
|
187
187
|
throw new Error(`Unexpected health status: ${String(health.status)}`);
|
|
188
188
|
}
|
|
189
|
+
// Whatever ran the program is what doctor names. The deployment reports its
|
|
190
|
+
// own sandbox and a deployment that reports none gets an executor-neutral
|
|
191
|
+
// line — anything else is doctor asserting a sandbox it never saw (#368).
|
|
192
|
+
// The string arrives from a server and lands in a terminal, so it is
|
|
193
|
+
// sanitized here too rather than trusted twice.
|
|
194
|
+
const executorName =
|
|
195
|
+
typeof health.executor?.name === "string"
|
|
196
|
+
? health.executor.name
|
|
197
|
+
.replace(/[^\w .+-]+/g, " ")
|
|
198
|
+
.replace(/\s+/g, " ")
|
|
199
|
+
.trim()
|
|
200
|
+
.slice(0, 40)
|
|
201
|
+
.trim()
|
|
202
|
+
: "";
|
|
189
203
|
let requestId = 0;
|
|
190
204
|
const mcp = async (method, params) =>
|
|
191
205
|
jsonResponse(
|
|
@@ -240,13 +254,40 @@ async function doctor() {
|
|
|
240
254
|
JSON.parse(executed.result?.content?.[0]?.text ?? "null");
|
|
241
255
|
if (executed.result?.isError || executionResult?.result !== 42) {
|
|
242
256
|
throw new Error(
|
|
243
|
-
|
|
257
|
+
`${executorName ? `${executorName} execution` : "execute_code"} check ` +
|
|
258
|
+
`failed: ${JSON.stringify(executed.result)}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Drift is reported, never failed on. A downstream that grew a tool nobody
|
|
263
|
+
// has classified is a maintainer's next task, not a broken deployment: the
|
|
264
|
+
// unclassified tool already fails closed onto call_destructive_tool. These
|
|
265
|
+
// counts come from refreshes the deployment already served, so a deployment
|
|
266
|
+
// that has answered no catalog request yet reports nothing here (#343).
|
|
267
|
+
const drifted = Object.entries(health.catalogDrift ?? {}).filter(
|
|
268
|
+
([, report]) =>
|
|
269
|
+
report.unclassifiedTools ||
|
|
270
|
+
report.unservedTools ||
|
|
271
|
+
report.annotationConflicts ||
|
|
272
|
+
report.schemaChanges,
|
|
273
|
+
);
|
|
274
|
+
for (const [connectorId, report] of drifted) {
|
|
275
|
+
console.warn(
|
|
276
|
+
`[connecta] catalog drift on "${connectorId}" (observed ` +
|
|
277
|
+
`${report.observedAt}): ${report.unclassifiedTools} unclassified, ` +
|
|
278
|
+
`${report.unservedTools} no longer served, ` +
|
|
279
|
+
`${report.annotationConflicts} annotation conflict(s), ` +
|
|
280
|
+
`${report.schemaChanges} schema change(s).`,
|
|
244
281
|
);
|
|
245
282
|
}
|
|
246
283
|
|
|
247
284
|
console.log(
|
|
248
285
|
`Connecta doctor passed: ${health.connectors} connector(s), ` +
|
|
249
|
-
|
|
286
|
+
`${executorName ? `${executorName} executed` : "code executed"}, ` +
|
|
287
|
+
"prescribed seven-tool surface" +
|
|
288
|
+
(drifted.length > 0
|
|
289
|
+
? `, catalog drift on ${drifted.length} connector(s).`
|
|
290
|
+
: "."),
|
|
250
291
|
);
|
|
251
292
|
}
|
|
252
293
|
|
package/dist/access-tokens.d.ts
CHANGED
package/dist/access-tokens.js
CHANGED
package/dist/activity.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Logger } from "./types.js";
|
|
1
|
+
import type { CatalogDriftCounts, Logger } from "./types.js";
|
|
2
2
|
export type ActivityCallSource = "call_tool" | "call_destructive_tool" | "batch_call" | "execute_code";
|
|
3
3
|
export type ActivityOutcome = "success" | "error" | "timeout" | "cancelled";
|
|
4
4
|
export type AgentFriction = "tool_not_found" | "schema_retry" | "destructive_reroute" | "auth_required" | "result_too_large";
|
|
@@ -51,6 +51,26 @@ export interface ToolCallActivityEvent {
|
|
|
51
51
|
serverVersion: string;
|
|
52
52
|
deploymentId?: string;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* A downstream catalog moved away from the manifest a release reviewed.
|
|
56
|
+
*
|
|
57
|
+
* Four integers and an id: the type has nowhere to put a tool name, a schema,
|
|
58
|
+
* an argument, a result, or a downstream error string, which is the same
|
|
59
|
+
* construction guarantee `ToolCallActivityEvent` makes. Which tool drifted is
|
|
60
|
+
* deliberately absent — the runtime reports that something did, and the
|
|
61
|
+
* maintainer-run check with a live catalog in front of it names names
|
|
62
|
+
* ([#343](https://github.com/zackbart/connecta/issues/343),
|
|
63
|
+
* [#351](https://github.com/zackbart/connecta/issues/351)).
|
|
64
|
+
*/
|
|
65
|
+
export interface CatalogDriftActivityEvent extends CatalogDriftCounts {
|
|
66
|
+
schemaVersion: 1;
|
|
67
|
+
id: string;
|
|
68
|
+
occurredAt: string;
|
|
69
|
+
connectorId: string;
|
|
70
|
+
serverName: string;
|
|
71
|
+
serverVersion: string;
|
|
72
|
+
deploymentId?: string;
|
|
73
|
+
}
|
|
54
74
|
export interface ActivityPage {
|
|
55
75
|
events: ToolCallActivityEvent[];
|
|
56
76
|
nextCursor?: string;
|
|
@@ -69,6 +89,13 @@ export interface ActivityReadPage {
|
|
|
69
89
|
/** Write-only deployments can implement only this small, vendor-neutral seam. */
|
|
70
90
|
export interface ActivitySink {
|
|
71
91
|
record(event: ToolCallActivityEvent): void | Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Optional catalog-drift channel. Optional rather than a widened `record`,
|
|
94
|
+
* because a store written before drift existed already decided what a row
|
|
95
|
+
* looks like: a sink that does not implement this simply never hears about
|
|
96
|
+
* drift, and the operator still reads the same counts from connector status.
|
|
97
|
+
*/
|
|
98
|
+
recordCatalogDrift?(event: CatalogDriftActivityEvent): void | Promise<void>;
|
|
72
99
|
}
|
|
73
100
|
/** Optional read side used by Connecta's authenticated Activity UI. */
|
|
74
101
|
export interface ActivityReader {
|
|
@@ -86,6 +113,20 @@ export declare class InvalidActivityCursorError extends Error {
|
|
|
86
113
|
constructor();
|
|
87
114
|
}
|
|
88
115
|
export type ActivityReadGate = (actor: ActivityActor) => boolean | Promise<boolean>;
|
|
116
|
+
/**
|
|
117
|
+
* Deployment-scoped destination for drift observations. Not request-scoped:
|
|
118
|
+
* a drifting catalog is something the deployment saw, not something a caller
|
|
119
|
+
* did, so it carries no actor and no request id to attribute it to one.
|
|
120
|
+
*/
|
|
121
|
+
export interface CatalogDriftActivityContext {
|
|
122
|
+
sink: ActivitySink;
|
|
123
|
+
serverInfo: {
|
|
124
|
+
name: string;
|
|
125
|
+
version: string;
|
|
126
|
+
};
|
|
127
|
+
deploymentId?: string;
|
|
128
|
+
logger: Logger;
|
|
129
|
+
}
|
|
89
130
|
/** Request-scoped context shared by direct, batch, and code-mode call paths. */
|
|
90
131
|
export interface ActivityRequestContext {
|
|
91
132
|
sink: ActivitySink;
|
|
@@ -106,4 +147,11 @@ export type ActivityEventInput = Pick<ToolCallActivityEvent, "connectorId" | "to
|
|
|
106
147
|
* Engine complete inline; Node promises remain detached from the response.
|
|
107
148
|
*/
|
|
108
149
|
export declare function recordToolActivity(context: ActivityRequestContext | undefined, input: ActivityEventInput): void;
|
|
109
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Record one catalog-drift observation, best-effort like every other activity
|
|
152
|
+
* write: a store that is down, or a sink that never implemented the channel,
|
|
153
|
+
* can never change what a refresh returned to the caller who paid for it.
|
|
154
|
+
*/
|
|
155
|
+
export declare function recordCatalogDriftActivity(context: CatalogDriftActivityContext | undefined, input: {
|
|
156
|
+
connectorId: string;
|
|
157
|
+
} & CatalogDriftCounts): void;
|
package/dist/activity.js
CHANGED
|
@@ -89,4 +89,37 @@ export function recordToolActivity(context, input) {
|
|
|
89
89
|
context.logger.warn("[connecta] activity record failed", error);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Record one catalog-drift observation, best-effort like every other activity
|
|
94
|
+
* write: a store that is down, or a sink that never implemented the channel,
|
|
95
|
+
* can never change what a refresh returned to the caller who paid for it.
|
|
96
|
+
*/
|
|
97
|
+
export function recordCatalogDriftActivity(context, input) {
|
|
98
|
+
if (!context?.sink.recordCatalogDrift)
|
|
99
|
+
return;
|
|
100
|
+
const event = {
|
|
101
|
+
schemaVersion: 1,
|
|
102
|
+
id: crypto.randomUUID(),
|
|
103
|
+
occurredAt: new Date().toISOString(),
|
|
104
|
+
connectorId: boundedEchoText(input.connectorId, MAX_ACTIVITY_NAME_BYTES),
|
|
105
|
+
unclassifiedTools: input.unclassifiedTools,
|
|
106
|
+
unservedTools: input.unservedTools,
|
|
107
|
+
annotationConflicts: input.annotationConflicts,
|
|
108
|
+
schemaChanges: input.schemaChanges,
|
|
109
|
+
serverName: context.serverInfo.name,
|
|
110
|
+
serverVersion: context.serverInfo.version,
|
|
111
|
+
...(context.deploymentId ? { deploymentId: context.deploymentId } : {}),
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
const result = context.sink.recordCatalogDrift(event);
|
|
115
|
+
if (!result || typeof result.then !== "function") {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
void Promise.resolve(result).catch((error) => {
|
|
119
|
+
context.logger.warn("[connecta] catalog drift record failed", error);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
context.logger.warn("[connecta] catalog drift record failed", error);
|
|
124
|
+
}
|
|
125
|
+
}
|
package/dist/apps-shell.d.ts
CHANGED
|
@@ -37,4 +37,3 @@ export declare const MCP_APPS_EXTENSION = "io.modelcontextprotocol/ui";
|
|
|
37
37
|
* no direct network.
|
|
38
38
|
*/
|
|
39
39
|
export declare const PROGRAM_UI_SHELL_HTML = "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>connecta program view</title>\n <style>\n html,\n body {\n margin: 0;\n padding: 0;\n background: transparent;\n }\n #program-view {\n display: block;\n width: 100%;\n min-height: 480px;\n border: 0;\n }\n </style>\n </head>\n <body>\n <iframe\n id=\"program-view\"\n title=\"Program-rendered view\"\n sandbox=\"allow-scripts\"\n srcdoc=\"\"\n ></iframe>\n <script>\n (function () {\n \"use strict\";\n // The outer shell is the only host peer. The payload frame is\n // sandboxed to scripts alone, with no same-origin escape. Its one\n // optional message dialect is handled below and translated into\n // bounded call_tool requests; raw JSON-RPC is never forwarded.\n var host = window.parent;\n var view = document.getElementById(\"program-view\");\n var initializeId = \"connecta-ui-initialize\";\n var lastWidth = 0;\n var lastHeight = 0;\n var reads = null;\n var hostCanCallTools = false;\n var nextHostRequestId = 0;\n var pendingHostReads = Object.create(null);\n var activeHostReads = 0;\n var maxActiveHostReads = 8;\n\n function send(message) {\n if (!host || host === window) return;\n host.postMessage(message, \"*\");\n }\n\n function notify(method, params) {\n send({ jsonrpc: \"2.0\", method: method, params: params });\n }\n\n // Program views are fixed-height by construction. The shell has no\n // content-height bridge to the payload frame, so it can never learn\n // the payload's content height, and\n // what it reports here is its own box: the min-height above, unless\n // the host has given it more. Taller content scrolls inside the inner\n // frame rather than growing the view. Raising the min-height is the\n // only lever; a content-height signal would cost the isolation.\n function reportSize() {\n var width = Math.ceil(document.documentElement.clientWidth);\n var height = Math.ceil(document.documentElement.scrollHeight);\n if (width === lastWidth && height === lastHeight) return;\n lastWidth = width;\n lastHeight = height;\n notify(\"ui/notifications/size-changed\", {\n width: width,\n height: height\n });\n }\n\n function payload(result) {\n if (!result || typeof result !== \"object\") return null;\n var meta = result._meta;\n if (!meta || typeof meta !== \"object\") return null;\n var value = meta[\"connecta/ui\"];\n if (!value || typeof value !== \"object\") return null;\n return typeof value.html === \"string\" && value.html.length > 0\n ? value\n : null;\n }\n\n // This function is serialized into the opaque-origin payload frame.\n // It knows no addresses and has no host channel of its own: one named\n // read request goes to the trusted outer shell and one correlated\n // result comes back.\n function payloadReadBridge() {\n \"use strict\";\n var pending = Object.create(null);\n var nextId = 0;\n\n function read(name, args) {\n return new Promise(function (resolve, reject) {\n var id = String(++nextId);\n pending[id] = { resolve: resolve, reject: reject };\n try {\n window.parent.postMessage({\n type: \"connecta/read\",\n id: id,\n name: name,\n args: args === undefined ? {} : args\n }, \"*\");\n } catch (error) {\n delete pending[id];\n reject(error);\n }\n });\n }\n\n Object.defineProperty(globalThis, \"connecta\", {\n value: Object.freeze({ read: read }),\n configurable: false,\n enumerable: true,\n writable: false\n });\n\n window.addEventListener(\"message\", function (event) {\n if (event.source !== window.parent) return;\n var message = event.data;\n if (!message || message.type !== \"connecta/read-result\") return;\n var waiter = pending[message.id];\n if (!waiter) return;\n delete pending[message.id];\n if (message.ok) waiter.resolve(message.value);\n else waiter.reject(new Error(message.error || \"Read failed\"));\n });\n }\n\n function htmlWithReadBridge(html) {\n var script =\n \"<scr\" + \"ipt>(\" + payloadReadBridge.toString() + \")();</scr\" + \"ipt>\";\n var head = /<head(?:\\s[^>]*)?>/i.exec(html);\n if (head) {\n var at = (head.index || 0) + head[0].length;\n return html.slice(0, at) + script + html.slice(at);\n }\n var document = /<html(?:\\s[^>]*)?>/i.exec(html);\n if (document) {\n var afterHtml = (document.index || 0) + document[0].length;\n return html.slice(0, afterHtml) + \"<head>\" + script + \"</head>\" + html.slice(afterHtml);\n }\n return script + html;\n }\n\n function render(params) {\n var value =\n payload(params) ||\n payload(params && params.result) ||\n payload(params && params.toolResult);\n if (value === null) return;\n reads = value.reads && typeof value.reads === \"object\"\n ? value.reads\n : null;\n view.srcdoc = reads\n ? htmlWithReadBridge(value.html)\n : value.html;\n reportSize();\n }\n\n function readError(message, fallback) {\n if (message && typeof message.message === \"string\") return message.message;\n if (message && message.data && typeof message.data.message === \"string\") {\n return message.data.message;\n }\n return fallback;\n }\n\n function finishInnerRead(innerId, ok, value) {\n if (!view.contentWindow) return;\n view.contentWindow.postMessage(ok\n ? { type: \"connecta/read-result\", id: innerId, ok: true, value: value }\n : { type: \"connecta/read-result\", id: innerId, ok: false, error: value }, \"*\");\n }\n\n function beginInnerRead(message) {\n if (!hostCanCallTools) {\n finishInnerRead(message && message.id, false, \"This host does not support app-initiated server tool calls\");\n return;\n }\n if (!message || typeof message.id !== \"string\" || typeof message.name !== \"string\") return;\n if (!reads || !Object.prototype.hasOwnProperty.call(reads, message.name)) {\n finishInnerRead(message.id, false, \"Unknown read binding\");\n return;\n }\n if (activeHostReads >= maxActiveHostReads) {\n finishInnerRead(message.id, false, \"Too many concurrent reads\");\n return;\n }\n var binding = reads[message.name];\n if (!binding || typeof binding !== \"object\" || typeof binding.address !== \"string\") {\n finishInnerRead(message.id, false, \"Invalid read binding\");\n return;\n }\n var supplied = message.args;\n if (!supplied || typeof supplied !== \"object\" || Array.isArray(supplied)) {\n finishInnerRead(message.id, false, \"Read arguments must be an object\");\n return;\n }\n var allowed = Array.isArray(binding.viewArgs) ? binding.viewArgs : [];\n var suppliedKeys = Object.keys(supplied);\n for (var i = 0; i < suppliedKeys.length; i++) {\n var key = suppliedKeys[i];\n if (allowed.indexOf(key) === -1) {\n finishInnerRead(message.id, false, \"Undeclared read argument \" + JSON.stringify(key));\n return;\n }\n }\n var args = Object.create(null);\n var fixed = binding.fixedArgs && typeof binding.fixedArgs === \"object\"\n ? binding.fixedArgs\n : {};\n Object.keys(fixed).forEach(function (key) { args[key] = fixed[key]; });\n suppliedKeys.forEach(function (key) { args[key] = supplied[key]; });\n\n var hostId = \"connecta-ui-read-\" + String(++nextHostRequestId);\n pendingHostReads[hostId] = { innerId: message.id };\n activeHostReads++;\n send({\n jsonrpc: \"2.0\",\n id: hostId,\n method: \"tools/call\",\n params: {\n name: \"call_tool\",\n arguments: {\n address: binding.address,\n args: args,\n resultMode: \"value\"\n }\n }\n });\n }\n\n function finishHostRead(message) {\n var pending = pendingHostReads[message.id];\n if (!pending) return false;\n delete pendingHostReads[message.id];\n activeHostReads--;\n if (message.error) {\n finishInnerRead(pending.innerId, false, readError(message.error, \"Host rejected read\"));\n return true;\n }\n var toolResult = message.result;\n if (!toolResult || typeof toolResult !== \"object\") {\n finishInnerRead(pending.innerId, false, \"Host returned an invalid tool result\");\n return true;\n }\n var structured = toolResult.structuredContent;\n if (toolResult.isError || (structured && structured.ok === false)) {\n var detail = structured && structured.error;\n var content = Array.isArray(toolResult.content)\n ? toolResult.content.find(function (block) { return block && block.type === \"text\"; })\n : null;\n finishInnerRead(\n pending.innerId,\n false,\n readError(detail, content && content.text ? content.text : \"Read failed\")\n );\n return true;\n }\n var value = structured && structured.ok === true &&\n Object.prototype.hasOwnProperty.call(structured, \"data\")\n ? structured.data\n : structured !== undefined\n ? structured\n : toolResult;\n finishInnerRead(pending.innerId, true, value);\n return true;\n }\n\n window.addEventListener(\"message\", function (event) {\n var message = event.data;\n if (event.source === view.contentWindow) {\n if (message && message.type === \"connecta/read\") beginInnerRead(message);\n return;\n }\n if (event.source !== host) return;\n if (!message || message.jsonrpc !== \"2.0\") return;\n if (message.id !== undefined && finishHostRead(message)) return;\n if (message.method === \"ui/notifications/tool-result\") {\n render(message.params);\n return;\n }\n if (message.method === \"ui/resource-teardown\") {\n // A host->view request, not a notification: the host waits for\n // this reply before it tears the view down. There is nothing to\n // release, so answer immediately rather than make it time out.\n if (message.id !== undefined && message.id !== null) {\n send({ jsonrpc: \"2.0\", id: message.id, result: {} });\n }\n return;\n }\n // Only a completed handshake earns \"initialized\". A JSON-RPC error\n // response carries the same id, and announcing initialization on one\n // would assert a handshake that never happened.\n if (message.id === initializeId && message.result !== undefined) {\n var capabilities = message.result.hostCapabilities;\n hostCanCallTools = Boolean(capabilities && capabilities.serverTools);\n notify(\"ui/notifications/initialized\", {});\n }\n });\n\n window.addEventListener(\"resize\", reportSize);\n view.addEventListener(\"load\", reportSize);\n\n // Every field here is required by the Apps initialize schema, and a\n // conforming host rejects the request outright when one is missing \u2014\n // which would strand the shell before any tool result arrives.\n send({\n jsonrpc: \"2.0\",\n id: initializeId,\n method: \"ui/initialize\",\n params: {\n appInfo: { name: \"connecta program view\", version: \"1\" },\n appCapabilities: {},\n protocolVersion: \"2026-01-26\"\n }\n });\n reportSize();\n })();\n </script>\n </body>\n</html>\n";
|
|
40
|
-
//# sourceMappingURL=apps-shell.d.ts.map
|
package/dist/apps-shell.js
CHANGED
package/dist/auth/bearer.d.ts
CHANGED
package/dist/auth/bearer.js
CHANGED
package/dist/auth/clerk.d.ts
CHANGED
package/dist/auth/clerk.js
CHANGED
|
@@ -20,11 +20,45 @@ function tokenShape(request) {
|
|
|
20
20
|
return "jwt";
|
|
21
21
|
return "other";
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Clerk Frontend API origin, derived from pk_(test|live)_<b64 domain>.
|
|
25
|
+
*
|
|
26
|
+
* The key is operator config, and a key that cannot yield an origin is a
|
|
27
|
+
* structural mistake: it throws here, at construction, in the same voice as the
|
|
28
|
+
* `allowedDomains` checks. Left unvalidated, `atob` raises a bare
|
|
29
|
+
* `InvalidCharacterError` from inside the returned object — which, on a
|
|
30
|
+
* deployment that builds per request (the Workers shape), turns every route
|
|
31
|
+
* including `/health` into a 500 with a stack that names base64 rather than the
|
|
32
|
+
* environment variable the operator has to fix.
|
|
33
|
+
*
|
|
34
|
+
* The rejected value is never quoted back. A publishable key is public, but the
|
|
35
|
+
* commonest way to land here is pasting the *secret* key into the publishable
|
|
36
|
+
* slot, and a startup error is a log line.
|
|
37
|
+
*/
|
|
24
38
|
function fapiUrl(publishableKey) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
const shape = " Copy the publishable key from the Clerk dashboard: `pk_test_` or " +
|
|
40
|
+
"`pk_live_` followed by the base64-encoded Frontend API domain.";
|
|
41
|
+
if (typeof publishableKey !== "string" || publishableKey === "") {
|
|
42
|
+
throw new Error("clerkAuth: `publishableKey` is missing or not a string." + shape);
|
|
43
|
+
}
|
|
44
|
+
const encoded = /^pk_(?:test|live)_([A-Za-z0-9+/=]+)$/.exec(publishableKey)?.[1];
|
|
45
|
+
if (encoded === undefined) {
|
|
46
|
+
throw new Error("clerkAuth: `publishableKey` is not a Clerk publishable key." + shape);
|
|
47
|
+
}
|
|
48
|
+
let decoded;
|
|
49
|
+
try {
|
|
50
|
+
decoded = atob(encoded);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new Error("clerkAuth: `publishableKey` does not carry decodable base64." + shape);
|
|
54
|
+
}
|
|
55
|
+
// Clerk terminates the encoded domain with `$`; everything else is the host.
|
|
56
|
+
const domain = decoded.replace(/\$$/, "");
|
|
57
|
+
if (!isDomain(domain)) {
|
|
58
|
+
throw new Error("clerkAuth: `publishableKey` does not decode to a Frontend API domain." +
|
|
59
|
+
shape);
|
|
60
|
+
}
|
|
61
|
+
return `https://${domain}`;
|
|
28
62
|
}
|
|
29
63
|
const GATE_ALLOWED_TTL_MS = 60 * 1000;
|
|
30
64
|
const GATE_FORBIDDEN_TTL_MS = 30 * 1000;
|
|
@@ -175,6 +209,9 @@ function emailDomain(email) {
|
|
|
175
209
|
*/
|
|
176
210
|
export function clerkAuth(opts) {
|
|
177
211
|
assertNoRetiredToolkitOptions("clerkAuth", opts);
|
|
212
|
+
// Before the Clerk client, so a malformed key fails as a connecta
|
|
213
|
+
// configuration error rather than however the SDK happens to treat it.
|
|
214
|
+
const frontendApiUrl = fapiUrl(opts.publishableKey);
|
|
178
215
|
const clerk = createClerkClient({
|
|
179
216
|
secretKey: opts.secretKey,
|
|
180
217
|
publishableKey: opts.publishableKey,
|
|
@@ -329,12 +366,12 @@ export function clerkAuth(opts) {
|
|
|
329
366
|
};
|
|
330
367
|
return {
|
|
331
368
|
kind: "clerk",
|
|
332
|
-
activityActorNamespace:
|
|
369
|
+
activityActorNamespace: frontendApiUrl,
|
|
333
370
|
activityActorLabel: resolveActivityLabel,
|
|
334
371
|
uiAuth: {
|
|
335
372
|
kind: "clerk",
|
|
336
373
|
publishableKey: opts.publishableKey,
|
|
337
|
-
frontendApiUrl
|
|
374
|
+
frontendApiUrl,
|
|
338
375
|
...(opts.signInUrl ? { signInUrl: opts.signInUrl } : {}),
|
|
339
376
|
...(opts.signUpUrl ? { signUpUrl: opts.signUpUrl } : {}),
|
|
340
377
|
},
|
|
@@ -350,14 +387,14 @@ export function clerkAuth(opts) {
|
|
|
350
387
|
pathname === "/.well-known/oauth-protected-resource/mcp") {
|
|
351
388
|
return Response.json({
|
|
352
389
|
resource: `${base}/mcp`,
|
|
353
|
-
authorization_servers: [
|
|
390
|
+
authorization_servers: [frontendApiUrl],
|
|
354
391
|
bearer_methods_supported: ["header"],
|
|
355
392
|
scopes_supported: scopes,
|
|
356
393
|
}, { headers: CORS_HEADERS });
|
|
357
394
|
}
|
|
358
395
|
if (pathname === "/.well-known/oauth-authorization-server") {
|
|
359
396
|
try {
|
|
360
|
-
const upstream = await fetch(`${
|
|
397
|
+
const upstream = await fetch(`${frontendApiUrl}/.well-known/oauth-authorization-server`);
|
|
361
398
|
if (!upstream.ok) {
|
|
362
399
|
return Response.json({ error: "upstream authorization server metadata unavailable" }, { status: 502, headers: CORS_HEADERS });
|
|
363
400
|
}
|
|
@@ -426,4 +463,3 @@ export function clerkAuth(opts) {
|
|
|
426
463
|
},
|
|
427
464
|
};
|
|
428
465
|
}
|
|
429
|
-
//# sourceMappingURL=clerk.js.map
|
|
@@ -121,4 +121,3 @@ export declare class KvOAuthProvider implements OAuthClientProvider {
|
|
|
121
121
|
resetAuthorization(operatorDisconnected?: boolean): Promise<void>;
|
|
122
122
|
invalidateCredentials(scope: "all" | "client" | "tokens" | "verifier" | "discovery"): Promise<void>;
|
|
123
123
|
}
|
|
124
|
-
//# sourceMappingURL=downstream-oauth.d.ts.map
|
package/dist/call-admission.d.ts
CHANGED
package/dist/call-admission.js
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { CatalogDriftCounts, CatalogDriftReport, Connector, ToolDef } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Rebuild a drift report as four counts and a bounded timestamp.
|
|
4
|
+
*
|
|
5
|
+
* `Connector.catalogDrift()` sits on the open plugin seam, and what it returns
|
|
6
|
+
* lands in the body of unauthenticated `/health` and on connector status.
|
|
7
|
+
* TypeScript constrains neither an extra enumerable property nor the length of
|
|
8
|
+
* `observedAt` at runtime, so "counts and nothing else" is *made* true here —
|
|
9
|
+
* at the boundary where third-party output becomes a response — rather than
|
|
10
|
+
* trusted. The activity path reconstructs its five fields for the same reason.
|
|
11
|
+
*/
|
|
12
|
+
export declare function boundedCatalogDrift(report: CatalogDriftReport | undefined): CatalogDriftReport | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* What a release decided a tool does. `"read-only"` is observational;
|
|
15
|
+
* `"destructive"` modifies or removes state that already exists; `"additive"`
|
|
16
|
+
* only brings something new into being. Both writes leave the read-only path
|
|
17
|
+
* — the distinction decides whether the connection asserts `destructiveHint`,
|
|
18
|
+
* which shapes the approval copy a human reads.
|
|
19
|
+
*/
|
|
20
|
+
type VettedVerdict = "read-only" | "additive" | "destructive";
|
|
21
|
+
/** One tool as a release reviewed it. */
|
|
22
|
+
interface VettedToolRecord {
|
|
23
|
+
verdict: VettedVerdict;
|
|
24
|
+
/**
|
|
25
|
+
* Digest of the input and output schemas that release read, or undefined
|
|
26
|
+
* when no release has recorded them. Undefined is not "unchanged": a
|
|
27
|
+
* manifest with no digest cannot report a schema change, and says so by
|
|
28
|
+
* counting none. `npm run drift:check -- --record` reads them from a live
|
|
29
|
+
* catalog and prints the block a release pastes in
|
|
30
|
+
* ([#351](https://github.com/zackbart/connecta/issues/351)).
|
|
31
|
+
*/
|
|
32
|
+
schemaDigest?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The vetted manifest one hosted-MCP proxy ships: every tool name a release
|
|
36
|
+
* reviewed, what it reviewed it as, and — where a release recorded them — the
|
|
37
|
+
* schemas it read. This is the single per-provider source P13 asks for, so the
|
|
38
|
+
* classification the connector applies and the classification a drift check
|
|
39
|
+
* compares against cannot disagree.
|
|
40
|
+
*/
|
|
41
|
+
export interface VettedCatalog {
|
|
42
|
+
/** Manifest format. Bumped when the comparison itself changes shape. */
|
|
43
|
+
version: 1;
|
|
44
|
+
tools: ReadonlyMap<string, VettedToolRecord>;
|
|
45
|
+
}
|
|
46
|
+
export interface VettedCatalogInput {
|
|
47
|
+
/** Names whose contract is observational rather than mutating. */
|
|
48
|
+
reads: ReadonlySet<string>;
|
|
49
|
+
/** Names that write, with the verdict that shapes the approval copy. */
|
|
50
|
+
writes: ReadonlyMap<string, "additive" | "destructive">;
|
|
51
|
+
/**
|
|
52
|
+
* Schemas this release reviewed, as `name` → digest from
|
|
53
|
+
* {@link vettedSchemaDigest}. Omit entirely until a release has actually
|
|
54
|
+
* read them; an invented digest reports drift that never happened.
|
|
55
|
+
*/
|
|
56
|
+
schemaDigests?: Readonly<Record<string, string>>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Digest the schemas of one downstream tool.
|
|
60
|
+
*
|
|
61
|
+
* Canonical rather than literal, unlike the catalog fingerprint in
|
|
62
|
+
* `src/catalog-fingerprint.ts`: that one is deliberately conservative because a
|
|
63
|
+
* spurious cache write is cheap, while a spurious drift finding spends a
|
|
64
|
+
* maintainer's attention on a downstream that reordered its JSON keys.
|
|
65
|
+
* Description and annotations are excluded — a reworded description is P1's
|
|
66
|
+
* business, and an annotation change is already its own drift category.
|
|
67
|
+
*/
|
|
68
|
+
export declare function vettedSchemaDigest(tool: ToolDef): Promise<string>;
|
|
69
|
+
/**
|
|
70
|
+
* Build a provider's manifest from the lists it already maintains.
|
|
71
|
+
*
|
|
72
|
+
* Throws when a name is classified twice, because a tool that is both a read
|
|
73
|
+
* and a write is a review mistake that must not boot: the annotation the
|
|
74
|
+
* connector would apply depends on which branch runs first, and "safe by
|
|
75
|
+
* default" cannot be decided by ordering.
|
|
76
|
+
*/
|
|
77
|
+
export declare function vettedCatalog(input: VettedCatalogInput): VettedCatalog;
|
|
78
|
+
/**
|
|
79
|
+
* Compare a live catalog with the manifest and count what moved.
|
|
80
|
+
*
|
|
81
|
+
* Counts only, and by construction: there is nowhere here to put a tool name,
|
|
82
|
+
* a schema, or a downstream string, so no later surface has to remember to
|
|
83
|
+
* strip one. Reads nothing but the tools it was handed — the caller already
|
|
84
|
+
* fetched them to serve a request, and this function never fetches anything.
|
|
85
|
+
*/
|
|
86
|
+
export declare function detectCatalogDrift(catalog: VettedCatalog, tools: readonly ToolDef[]): Promise<CatalogDriftCounts>;
|
|
87
|
+
/**
|
|
88
|
+
* Wrap a hosted-MCP connector in its vetted manifest: the classification the
|
|
89
|
+
* catalog is normalized with, and the drift check that rides the same listing.
|
|
90
|
+
*
|
|
91
|
+
* The check happens where the tools are already in hand and still unmodified —
|
|
92
|
+
* after the downstream answered, before the classification is applied. It adds
|
|
93
|
+
* no request of its own, which is the whole boundary: connecta watches a
|
|
94
|
+
* contract while it is serving a refresh the deployment asked for, and never
|
|
95
|
+
* initiates one to go looking ([#179](https://github.com/zackbart/connecta/issues/179),
|
|
96
|
+
* [#343](https://github.com/zackbart/connecta/issues/343)).
|
|
97
|
+
*/
|
|
98
|
+
export declare function withVettedCatalog(connector: Connector, catalog: VettedCatalog): Connector;
|
|
99
|
+
export {};
|