@fungi.computer/veil 0.4.0 → 0.5.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 (35) hide show
  1. package/dist/connections/cleanup.d.ts +21 -0
  2. package/dist/connections/cleanup.js +66 -0
  3. package/dist/connections/contract.d.ts +153 -7
  4. package/dist/connections/contract.js +42 -2
  5. package/dist/connections/core.d.ts +55 -5
  6. package/dist/connections/core.js +365 -106
  7. package/dist/connections/discover.d.ts +3 -0
  8. package/dist/connections/discover.js +23 -0
  9. package/dist/connections/egress.js +3 -2
  10. package/dist/connections/index.d.ts +1 -0
  11. package/dist/connections/index.js +1 -0
  12. package/dist/connections/internal.d.ts +78 -0
  13. package/dist/connections/internal.js +66 -0
  14. package/dist/connections/mcp-agent.d.ts +37 -0
  15. package/dist/connections/mcp-agent.js +75 -0
  16. package/dist/connections/nango.d.ts +30 -0
  17. package/dist/connections/nango.js +136 -0
  18. package/dist/connections/oauth-flow.d.ts +81 -0
  19. package/dist/connections/oauth-flow.js +322 -0
  20. package/dist/connections/oauth-provider.d.ts +31 -0
  21. package/dist/connections/oauth-provider.js +96 -0
  22. package/dist/connections/oauth.d.ts +93 -0
  23. package/dist/connections/oauth.js +239 -0
  24. package/dist/connections/openapi.d.ts +36 -0
  25. package/dist/connections/openapi.js +120 -0
  26. package/dist/connections/sealed.js +23 -4
  27. package/dist/connections/source.d.ts +17 -0
  28. package/dist/connections/source.js +111 -0
  29. package/dist/connections/update.d.ts +41 -0
  30. package/dist/connections/update.js +81 -0
  31. package/dist/gatekeeper.d.ts +21 -1
  32. package/dist/gatekeeper.js +74 -12
  33. package/dist/nango.d.ts +41 -0
  34. package/dist/nango.js +6 -6
  35. package/package.json +11 -1
@@ -0,0 +1,21 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type * as Schema from "effect/Schema";
3
+ import type { ConnectionsOptions } from "./contract.js";
4
+ import { type AttemptRow, type ConnectionRow } from "./internal.js";
5
+ /** The cleanup outbox, attempt retirement and reconnect marking over the owner's SQLite. */
6
+ export declare function createCleanup(deps: {
7
+ ownerId: string;
8
+ custody: ConnectionsOptions["custody"];
9
+ clock: () => number;
10
+ sql: ConnectionsOptions["store"]["sql"];
11
+ transactionSync: <A>(body: () => A) => A;
12
+ rows: <S extends Schema.Decoder<unknown>>(schema: S, statement: string, ...bindings: (string | number | null)[]) => S["Type"][];
13
+ bump: () => void;
14
+ }): {
15
+ cleanupPending: () => number;
16
+ sweep: () => Effect.Effect<void, import("./contract.js").ConnectionsError, never>;
17
+ enqueueLocator: (connectionId: string, locator: string, credential: ConnectionRow["credential"]) => void;
18
+ enqueueCleanup: (row: ConnectionRow) => void;
19
+ markReconnect: (id: string) => void;
20
+ retireAttempt: (attempt: AttemptRow, status: string) => void;
21
+ };
@@ -0,0 +1,66 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { attemptRow, cleanupRow, connectionRow, count, sync, } from "./internal.js";
3
+ const cleanupLimit = 5;
4
+ /** The cleanup outbox, attempt retirement and reconnect marking over the owner's SQLite. */
5
+ export function createCleanup(deps) {
6
+ const { sql, rows, bump, transactionSync } = deps;
7
+ /** Sealed credentials still waiting to be deleted; hosts wake their alarm while this is positive. */
8
+ const cleanupPending = () => rows(count, "SELECT COUNT(*) AS n FROM veil_connection_cleanup WHERE attempts < ?", cleanupLimit)[0]?.n ?? 0;
9
+ const sweep = () => Effect.gen(function* () {
10
+ yield* sync(expireAttempts);
11
+ const due = yield* sync(() => rows(cleanupRow, "SELECT * FROM veil_connection_cleanup WHERE attempts < ?", cleanupLimit));
12
+ for (const job of due) {
13
+ const outcome = yield* deps.custody
14
+ .remove({
15
+ ownerId: deps.ownerId,
16
+ connectionId: job.connectionId,
17
+ credential: job.credential,
18
+ }, job.locator)
19
+ .pipe(Effect.result);
20
+ yield* sync(() => {
21
+ if (outcome._tag === "Success")
22
+ sql.exec("DELETE FROM veil_connection_cleanup WHERE id = ?", job.id);
23
+ else
24
+ sql.exec("UPDATE veil_connection_cleanup SET attempts = attempts + 1 WHERE id = ?", job.id);
25
+ });
26
+ }
27
+ });
28
+ const enqueueLocator = (connectionId, locator, credential) => {
29
+ sql.exec("INSERT INTO veil_connection_cleanup (connectionId, locator, credential, attempts) VALUES (?, ?, ?, 0)", connectionId, locator, JSON.stringify(credential));
30
+ };
31
+ const enqueueCleanup = (row) => {
32
+ if (row.locator !== null)
33
+ enqueueLocator(row.id, row.locator, row.credential);
34
+ };
35
+ const markReconnect = (id) => {
36
+ sql.exec("UPDATE veil_connections SET state = 'reconnect_required', authorityVersion = authorityVersion + 1 WHERE id = ? AND state = 'active'", id);
37
+ bump();
38
+ };
39
+ /** An attempt ends: its sealed verifier goes to cleanup and a still-pending connection is withdrawn. */
40
+ const retireAttempt = (attempt, status) => {
41
+ sql.exec("UPDATE veil_connection_attempts SET status = ?, pkceVerifierSealed = NULL WHERE state = ?", status, attempt.state);
42
+ const row = rows(connectionRow, "SELECT * FROM veil_connections WHERE id = ?", attempt.connectionId)[0];
43
+ if (row === undefined)
44
+ return;
45
+ if (attempt.pkceVerifierSealed !== null)
46
+ enqueueLocator(row.id, attempt.pkceVerifierSealed, row.credential);
47
+ if (row.state === "pending") {
48
+ sql.exec("UPDATE veil_connections SET state = 'revoked', authorityVersion = authorityVersion + 1 WHERE id = ?", row.id);
49
+ bump();
50
+ }
51
+ };
52
+ const expireAttempts = () => {
53
+ transactionSync(() => {
54
+ for (const attempt of rows(attemptRow, "SELECT * FROM veil_connection_attempts WHERE status IN ('pending', 'consumed') AND expiresAt <= ?", deps.clock()))
55
+ retireAttempt(attempt, "expired");
56
+ });
57
+ };
58
+ return {
59
+ cleanupPending,
60
+ sweep,
61
+ enqueueLocator,
62
+ enqueueCleanup,
63
+ markReconnect,
64
+ retireAttempt,
65
+ };
66
+ }
@@ -1,7 +1,7 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as Schema from "effect/Schema";
3
3
  import type { PublicEgressPolicy } from "../public-egress-policy.js";
4
- export type ConnectionsRefusal = "invalid_input" | "denied" | "revoked" | "conflict" | "unsupported" | "too_large" | "reconnect_required" | "invalid_response" | "unavailable";
4
+ export type ConnectionsRefusal = "invalid_input" | "denied" | "revoked" | "conflict" | "unsupported" | "too_large" | "reconnect_required" | "pending" | "invalid_response" | "unavailable";
5
5
  /** Fixed codes only: upstream bodies, URLs and credential bytes never enter errors. */
6
6
  export declare class ConnectionsError extends Error {
7
7
  readonly code: ConnectionsRefusal;
@@ -15,7 +15,11 @@ declare const placementSchema: Schema.Union<readonly [Schema.Literal<"bearer">,
15
15
  readonly query: Schema.String;
16
16
  }>]>;
17
17
  export type Placement = typeof placementSchema.Type;
18
- /** How the final request is authorized. OAuth joins this union in slice 3. */
18
+ /**
19
+ * How the final request is authorized. For `oauth` the owner may bring its own
20
+ * client (`clientId`, with the client secret as the draft's `secret`); without
21
+ * one the server's dynamic registration is used.
22
+ */
19
23
  export declare const credentialSchema: Schema.Union<readonly [Schema.Struct<{
20
24
  readonly kind: Schema.Literal<"none">;
21
25
  }>, Schema.Struct<{
@@ -25,6 +29,12 @@ export declare const credentialSchema: Schema.Union<readonly [Schema.Struct<{
25
29
  }>, Schema.Struct<{
26
30
  readonly query: Schema.String;
27
31
  }>]>;
32
+ }>, Schema.Struct<{
33
+ readonly kind: Schema.Literal<"oauth">;
34
+ readonly scope: Schema.optional<Schema.String>;
35
+ readonly clientId: Schema.optional<Schema.String>;
36
+ /** An operator-registered REST provider; absent for an MCP server's own sign-in. */
37
+ readonly provider: Schema.optional<Schema.String>;
28
38
  }>]>;
29
39
  export type Credential = typeof credentialSchema.Type;
30
40
  export type SurfaceView = Readonly<{
@@ -33,21 +43,27 @@ export type SurfaceView = Readonly<{
33
43
  }> | Readonly<{
34
44
  kind: "openapi";
35
45
  baseUrl: string;
36
- table: unknown;
46
+ table: readonly unknown[];
47
+ /** Digest of the decoded operation table. */
48
+ digest: string;
37
49
  }>;
38
- export declare const toolsSchema: Schema.Union<readonly [Schema.Literal<"all">, Schema.$Array<Schema.String>]>;
50
+ export declare const toolsSchema: Schema.Union<readonly [Schema.Literal<"all">, Schema.Struct<{
51
+ readonly effect: Schema.Literal<"read">;
52
+ }>, Schema.$Array<Schema.String>]>;
39
53
  export type Tools = typeof toolsSchema.Type;
40
54
  export declare const draftSchema: Schema.Struct<{
41
55
  /** Idempotency key: a replay with the same request returns the same connection. */
42
56
  readonly requestId: Schema.String;
43
57
  readonly name: Schema.String;
58
+ /** Display text for owners; defaults to the name. */
59
+ readonly label: Schema.optional<Schema.String>;
44
60
  readonly surface: Schema.Union<readonly [Schema.Struct<{
45
61
  readonly kind: Schema.Literal<"mcp">;
46
62
  readonly url: Schema.String;
47
63
  }>, Schema.Struct<{
48
64
  readonly kind: Schema.Literal<"openapi">;
49
65
  readonly baseUrl: Schema.String;
50
- readonly table: Schema.Unknown;
66
+ readonly table: Schema.$Array<Schema.Unknown>;
51
67
  }>]>;
52
68
  readonly credential: Schema.Union<readonly [Schema.Struct<{
53
69
  readonly kind: Schema.Literal<"none">;
@@ -58,6 +74,12 @@ export declare const draftSchema: Schema.Struct<{
58
74
  }>, Schema.Struct<{
59
75
  readonly query: Schema.String;
60
76
  }>]>;
77
+ }>, Schema.Struct<{
78
+ readonly kind: Schema.Literal<"oauth">;
79
+ readonly scope: Schema.optional<Schema.String>;
80
+ readonly clientId: Schema.optional<Schema.String>;
81
+ /** An operator-registered REST provider; absent for an MCP server's own sign-in. */
82
+ readonly provider: Schema.optional<Schema.String>;
61
83
  }>]>;
62
84
  readonly secret: Schema.optional<Schema.String>;
63
85
  }>;
@@ -65,9 +87,54 @@ export type ConnectionDraft = typeof draftSchema.Type;
65
87
  export declare const grantInputSchema: Schema.Struct<{
66
88
  readonly connectionId: Schema.String;
67
89
  readonly agentId: Schema.String;
68
- readonly tools: Schema.Union<readonly [Schema.Literal<"all">, Schema.$Array<Schema.String>]>;
90
+ readonly tools: Schema.Union<readonly [Schema.Literal<"all">, Schema.Struct<{
91
+ readonly effect: Schema.Literal<"read">;
92
+ }>, Schema.$Array<Schema.String>]>;
69
93
  }>;
70
94
  export type GrantInput = typeof grantInputSchema.Type;
95
+ /** The authorization server's redirect back to the owner. */
96
+ export declare const completeInputSchema: Schema.Struct<{
97
+ readonly state: Schema.String;
98
+ readonly code: Schema.String;
99
+ readonly iss: Schema.optional<Schema.String>;
100
+ }>;
101
+ export type CompleteInput = typeof completeInputSchema.Type;
102
+ /** Owner edits to a live connection, guarded by the version the owner last saw. */
103
+ export declare const updateInputSchema: Schema.Struct<{
104
+ readonly requestId: Schema.String;
105
+ readonly connectionId: Schema.String;
106
+ readonly expectedVersion: Schema.Number;
107
+ readonly label: Schema.optional<Schema.String>;
108
+ readonly name: Schema.optional<Schema.String>;
109
+ /** Replacement OpenAPI operation table, decoded by Forage. */
110
+ readonly table: Schema.optional<Schema.$Array<Schema.Unknown>>;
111
+ readonly baseUrl: Schema.optional<Schema.String>;
112
+ /** Credential swap; `none` and `static` only. */
113
+ readonly credential: Schema.optional<Schema.Union<readonly [Schema.Struct<{
114
+ readonly kind: Schema.Literal<"none">;
115
+ }>, Schema.Struct<{
116
+ readonly kind: Schema.Literal<"static">;
117
+ readonly placement: Schema.Union<readonly [Schema.Literal<"bearer">, Schema.Struct<{
118
+ readonly header: Schema.String;
119
+ }>, Schema.Struct<{
120
+ readonly query: Schema.String;
121
+ }>]>;
122
+ }>, Schema.Struct<{
123
+ readonly kind: Schema.Literal<"oauth">;
124
+ readonly scope: Schema.optional<Schema.String>;
125
+ readonly clientId: Schema.optional<Schema.String>;
126
+ /** An operator-registered REST provider; absent for an MCP server's own sign-in. */
127
+ readonly provider: Schema.optional<Schema.String>;
128
+ }>]>>;
129
+ readonly secret: Schema.optional<Schema.String>;
130
+ }>;
131
+ export type UpdateInput = typeof updateInputSchema.Type;
132
+ /** One typed OpenAPI operation call. */
133
+ export declare const callInputSchema: Schema.Struct<{
134
+ readonly operation: Schema.String;
135
+ readonly input: Schema.Unknown;
136
+ }>;
137
+ export type CallInput = typeof callInputSchema.Type;
71
138
  export declare const egressRefSchema: Schema.Struct<{
72
139
  readonly connectionId: Schema.String;
73
140
  readonly grantVersion: Schema.Number;
@@ -87,6 +154,35 @@ export type CustodyAdapter = Readonly<{
87
154
  put: (address: CustodyAddress, secret: string) => Effect.Effect<string, ConnectionsError>;
88
155
  inject: (address: CustodyAddress, locator: string, request: Request) => Effect.Effect<Response, ConnectionsError>;
89
156
  remove: (address: CustodyAddress, locator: string) => Effect.Effect<"deleted" | "absent", ConnectionsError>;
157
+ /**
158
+ * Release a sealed OAuth bundle to the owner's connections core, which runs
159
+ * the authorization flow and refreshes tokens. Never reaches an Agent.
160
+ */
161
+ open?: (address: CustodyAddress, locator: string) => Effect.Effect<string, ConnectionsError>;
162
+ /**
163
+ * A custodian that runs the authorization itself (Nango's Connect) returns
164
+ * the page to send the owner to; it settles through a signed completion
165
+ * (`complete`) carrying the `state` given here, not through our callback.
166
+ */
167
+ connect?: (address: CustodyAddress, attempt: Readonly<{
168
+ state: string;
169
+ actor: string;
170
+ integrationId: string;
171
+ }>) => Effect.Effect<Readonly<{
172
+ connectUrl: string;
173
+ expiresAt: number;
174
+ }>, ConnectionsError>;
175
+ }>;
176
+ /**
177
+ * What a custodian proves after it ran an authorization: the attempt's state
178
+ * and where it keeps the credential. The host verifies the custodian's
179
+ * signature before it builds one.
180
+ */
181
+ export type CustodyCompletion = Readonly<{
182
+ state: string;
183
+ locator: string;
184
+ /** The integration the custodian ran it through; it must be the one the attempt began with. */
185
+ integrationId: string;
90
186
  }>;
91
187
  export type SqlValue = string | number | null;
92
188
  /** The owner's SQLite in Cloudflare `ctx.storage` shape. */
@@ -98,13 +194,54 @@ export type SqlStore = Readonly<{
98
194
  }>;
99
195
  transactionSync: <A>(operation: () => A) => A;
100
196
  }>;
197
+ /**
198
+ * A REST provider the operator registered an app with. Such providers publish
199
+ * no metadata the SDK can discover, and many require a pre-registered client,
200
+ * so the host names the endpoints and holds the client. Only the operator's
201
+ * configuration reaches this type; neither an Agent nor an owner supplies one.
202
+ */
203
+ export type StaticOAuthProvider = Readonly<{
204
+ issuer: string;
205
+ authorizationEndpoint: string;
206
+ tokenEndpoint: string;
207
+ /** Scopes requested when the owner names none. */
208
+ scope?: string;
209
+ client: {
210
+ client_id: string;
211
+ client_secret?: string | undefined;
212
+ };
213
+ }>;
101
214
  export type ConnectionsOptions = Readonly<{
102
215
  ownerId: string;
103
216
  store: SqlStore;
104
217
  custody: CustodyAdapter;
105
218
  /** Host authority read: may this actor manage connections, or still stand as a grantor? */
106
219
  authorize: (actorId: string, purpose: "manage" | "grantor") => boolean;
220
+ /**
221
+ * Asked right before every send, with the grantor who stands behind the
222
+ * grant. The host keeps its own live recheck here (membership, write
223
+ * rights); the core never learns what the host's policy is.
224
+ */
225
+ check?: (grantor: string, grant: Readonly<{
226
+ agentId: string;
227
+ connectionId: string;
228
+ tools: Tools;
229
+ }>, operation: Readonly<{
230
+ name: string;
231
+ effect: "read" | "write" | null;
232
+ }> | null) => Promise<boolean>;
107
233
  egressPolicy?: PublicEgressPolicy;
234
+ /**
235
+ * Needed only for `oauth` connections: where the authorization server sends
236
+ * the owner back, and the checked public transport for discovery, token and
237
+ * refresh requests. Custody must also implement `open`.
238
+ */
239
+ oauth?: Readonly<{
240
+ callbackUrl: string;
241
+ fetch: (request: Request) => Promise<Response>;
242
+ /** REST providers the operator registered an app with, by id. */
243
+ providers?: Readonly<Record<string, StaticOAuthProvider>>;
244
+ }> | undefined;
108
245
  clock: () => number;
109
246
  ids: () => string;
110
247
  }>;
@@ -116,6 +253,8 @@ export type ConnectionProjection = Readonly<{
116
253
  name: string;
117
254
  surface: SurfaceView;
118
255
  tools: Tools;
256
+ /** Bumped by every `update`. */
257
+ version: number;
119
258
  authorityVersion: number;
120
259
  grantVersion: number;
121
260
  }>[];
@@ -123,17 +262,24 @@ export type ConnectionProjection = Readonly<{
123
262
  export type ConnectionView = Readonly<{
124
263
  connectionId: string;
125
264
  name: string;
265
+ label: string;
126
266
  surface: Readonly<{
127
267
  kind: "mcp" | "openapi";
128
268
  origin: string;
269
+ url: string;
270
+ digest: string | null;
129
271
  }>;
130
272
  credentialKind: Credential["kind"];
131
- state: "active" | "reconnect_required" | "revoked";
273
+ /** How the secret is placed; the secret itself never appears. */
274
+ credential: Credential;
275
+ state: "pending" | "active" | "reconnect_required" | "revoked";
276
+ version: number;
132
277
  authorityVersion: number;
133
278
  grants: readonly Readonly<{
134
279
  agentId: string;
135
280
  tools: Tools;
136
281
  version: number;
282
+ grantor: string;
137
283
  }>[];
138
284
  }>;
139
285
  /** The Agent's narrowed capability: only the owner opens the credential. */
@@ -18,10 +18,21 @@ const placementSchema = Schema.Union([
18
18
  Schema.Struct({ header: headerName }),
19
19
  Schema.Struct({ query: queryName }),
20
20
  ]);
21
- /** How the final request is authorized. OAuth joins this union in slice 3. */
21
+ /**
22
+ * How the final request is authorized. For `oauth` the owner may bring its own
23
+ * client (`clientId`, with the client secret as the draft's `secret`); without
24
+ * one the server's dynamic registration is used.
25
+ */
22
26
  export const credentialSchema = Schema.Union([
23
27
  Schema.Struct({ kind: Schema.Literal("none") }),
24
28
  Schema.Struct({ kind: Schema.Literal("static"), placement: placementSchema }),
29
+ Schema.Struct({
30
+ kind: Schema.Literal("oauth"),
31
+ scope: Schema.optional(Schema.String),
32
+ clientId: Schema.optional(Schema.String),
33
+ /** An operator-registered REST provider; absent for an MCP server's own sign-in. */
34
+ provider: Schema.optional(Schema.String),
35
+ }),
25
36
  ]);
26
37
  /** What the Agent can call. The OpenAPI table is decoded by Forage before it is stored. */
27
38
  const surfaceDraftSchema = Schema.Union([
@@ -29,17 +40,21 @@ const surfaceDraftSchema = Schema.Union([
29
40
  Schema.Struct({
30
41
  kind: Schema.Literal("openapi"),
31
42
  baseUrl: Schema.String,
32
- table: Schema.Unknown,
43
+ table: Schema.Array(Schema.Unknown),
33
44
  }),
34
45
  ]);
35
46
  export const toolsSchema = Schema.Union([
36
47
  Schema.Literal("all"),
48
+ /** OpenAPI only: every operation whose stored effect is `read`. */
49
+ Schema.Struct({ effect: Schema.Literal("read") }),
37
50
  Schema.Array(Schema.String.pipe(Schema.check(Schema.isMinLength(1), Schema.isMaxLength(256)))).pipe(Schema.check(Schema.isMaxLength(256))),
38
51
  ]);
39
52
  export const draftSchema = Schema.Struct({
40
53
  /** Idempotency key: a replay with the same request returns the same connection. */
41
54
  requestId: identifier,
42
55
  name: Schema.String.pipe(Schema.check(Schema.isMinLength(1), Schema.isMaxLength(128))),
56
+ /** Display text for owners; defaults to the name. */
57
+ label: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMinLength(1), Schema.isMaxLength(128)))),
43
58
  surface: surfaceDraftSchema,
44
59
  credential: credentialSchema,
45
60
  secret: Schema.optional(Schema.String.pipe(Schema.check(Schema.isPattern(/^[\x21-\x7e]{1,16384}$/u)))),
@@ -49,6 +64,31 @@ export const grantInputSchema = Schema.Struct({
49
64
  agentId: identifier,
50
65
  tools: toolsSchema,
51
66
  });
67
+ /** The authorization server's redirect back to the owner. */
68
+ export const completeInputSchema = Schema.Struct({
69
+ state: Schema.String.pipe(Schema.check(Schema.isMaxLength(512))),
70
+ code: Schema.String.pipe(Schema.check(Schema.isMaxLength(4096))),
71
+ iss: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMaxLength(2048)))),
72
+ });
73
+ /** Owner edits to a live connection, guarded by the version the owner last saw. */
74
+ export const updateInputSchema = Schema.Struct({
75
+ requestId: identifier,
76
+ connectionId: identifier,
77
+ expectedVersion: version,
78
+ label: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMinLength(1), Schema.isMaxLength(128)))),
79
+ name: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMinLength(1), Schema.isMaxLength(128)))),
80
+ /** Replacement OpenAPI operation table, decoded by Forage. */
81
+ table: Schema.optional(Schema.Array(Schema.Unknown)),
82
+ baseUrl: Schema.optional(Schema.String),
83
+ /** Credential swap; `none` and `static` only. */
84
+ credential: Schema.optional(credentialSchema),
85
+ secret: Schema.optional(Schema.String.pipe(Schema.check(Schema.isPattern(/^[\x21-\x7e]{1,16384}$/u)))),
86
+ });
87
+ /** One typed OpenAPI operation call. */
88
+ export const callInputSchema = Schema.Struct({
89
+ operation: Schema.String.pipe(Schema.check(Schema.isMaxLength(256))),
90
+ input: Schema.Unknown,
91
+ });
52
92
  export const egressRefSchema = Schema.Struct({
53
93
  connectionId: identifier,
54
94
  grantVersion: version,
@@ -1,10 +1,16 @@
1
1
  import * as Effect from "effect/Effect";
2
- import { ConnectionsError, type ConnectionDraft, type GrantInput, type ConnectionsOptions, type EgressRef, type NarrowFetch, type SurfaceView } from "./contract.js";
2
+ import { ConnectionsError, type CallInput, type ConnectionDraft, type Tools, type GrantInput, type ConnectionsOptions, type EgressRef, type NarrowFetch, type SurfaceView } from "./contract.js";
3
3
  /** One owner's connections: attempts, connections, grants, receipts and cleanup in its SQLite. */
4
4
  export declare function createConnections(options: ConnectionsOptions): {
5
5
  begin: (actor: string, input: ConnectionDraft) => Effect.Effect<{
6
6
  connectionId: string;
7
- authorizeUrl: null;
7
+ authorizeUrl: string | null;
8
+ }, ConnectionsError, never>;
9
+ complete: (actor: string, input: import("./contract.js").CompleteInput) => Effect.Effect<{
10
+ connectionId: string;
11
+ }, ConnectionsError, never>;
12
+ completeCustody: (input: import("./contract.js").CustodyCompletion) => Effect.Effect<{
13
+ connectionId: string;
8
14
  }, ConnectionsError, never>;
9
15
  grant: (actor: string, input: GrantInput) => Effect.Effect<{
10
16
  grantVersion: number;
@@ -22,7 +28,10 @@ export declare function createConnections(options: ConnectionsOptions): {
22
28
  connectionId: string;
23
29
  name: string;
24
30
  surface: SurfaceView;
25
- tools: readonly string[] | "all";
31
+ tools: readonly string[] | "all" | {
32
+ readonly effect: "read";
33
+ };
34
+ version: number;
26
35
  authorityVersion: number;
27
36
  grantVersion: number;
28
37
  }[];
@@ -30,19 +39,60 @@ export declare function createConnections(options: ConnectionsOptions): {
30
39
  list: (actor: string) => Effect.Effect<Readonly<{
31
40
  connectionId: string;
32
41
  name: string;
42
+ label: string;
33
43
  surface: Readonly<{
34
44
  kind: "mcp" | "openapi";
35
45
  origin: string;
46
+ url: string;
47
+ digest: string | null;
36
48
  }>;
37
49
  credentialKind: import("./contract.js").Credential["kind"];
38
- state: "active" | "reconnect_required" | "revoked";
50
+ credential: import("./contract.js").Credential;
51
+ state: "pending" | "active" | "reconnect_required" | "revoked";
52
+ version: number;
39
53
  authorityVersion: number;
40
54
  grants: readonly Readonly<{
41
55
  agentId: string;
42
- tools: import("./contract.js").Tools;
56
+ tools: Tools;
43
57
  version: number;
58
+ grantor: string;
44
59
  }>[];
45
60
  }>[], ConnectionsError, never>;
46
61
  egress: (agentId: string, input: EgressRef) => NarrowFetch;
62
+ call: (agentId: string, input: EgressRef, raw: CallInput) => Effect.Effect<import("zod").JSONType, ConnectionsError, never>;
63
+ ownerCall: (actor: string, at: Readonly<{
64
+ connectionId: string;
65
+ expectedVersion: number;
66
+ }>, raw: CallInput) => Effect.Effect<import("zod").JSONType, ConnectionsError, never>;
67
+ tools: (actor: string, connectionId: string) => Effect.Effect<readonly string[], ConnectionsError, never>;
68
+ operations: (actor: string, id: string) => Effect.Effect<{
69
+ operations: {
70
+ status: "supported";
71
+ name: string;
72
+ method: "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT" | "PATCH" | "DELETE";
73
+ path: string;
74
+ effect: "write" | "read";
75
+ description: string & import("zod").$brand<"ThirdPartyText">;
76
+ textSource: "third_party";
77
+ tags: (string & import("zod").$brand<"ThirdPartyText">)[];
78
+ parameters: {
79
+ name: string;
80
+ in: "header" | "path" | "query";
81
+ required: boolean;
82
+ schema: boolean | Record<string, import("zod").JSONType>;
83
+ style?: string | undefined;
84
+ explode?: boolean | undefined;
85
+ allowReserved?: boolean | undefined;
86
+ }[];
87
+ input: boolean | Record<string, import("zod").JSONType>;
88
+ output: boolean | Record<string, import("zod").JSONType>;
89
+ }[];
90
+ digest: string;
91
+ }, ConnectionsError, never>;
92
+ update: (actor: string, raw: import("./contract.js").UpdateInput) => Effect.Effect<{
93
+ connectionId: string;
94
+ version: number;
95
+ }, ConnectionsError, never>;
47
96
  sweep: () => Effect.Effect<void, ConnectionsError, never>;
97
+ cleanupPending: () => number;
48
98
  };