@zackbart/connecta-eve 0.39.0 → 0.41.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/README.md +77 -7
- package/dist/index.d.ts +25 -1
- package/dist/index.js +94 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,15 +4,16 @@ Connect an Eve agent to a Connecta agent pool and load its endpoint instructions
|
|
|
4
4
|
once per turn. The helper runs in the Eve app; the Connecta server
|
|
5
5
|
runs separately. No `@zackbart/connecta` dependency is installed by this package.
|
|
6
6
|
|
|
7
|
-
Requires **Node 24+** and **Eve
|
|
8
|
-
checks use
|
|
9
|
-
need a compatibility check. Helper
|
|
10
|
-
choose the helper version matching
|
|
7
|
+
Requires **Node 24+** and **Eve 1.0.0-rc.1**. Development and consumer
|
|
8
|
+
checks use that exact release candidate. The peer stays pinned during the RC
|
|
9
|
+
period; later candidates and stable 1.0 need a compatibility check. Helper
|
|
10
|
+
releases share Connecta's version and tag; choose the helper version matching
|
|
11
|
+
your server release.
|
|
11
12
|
|
|
12
13
|
## Install and add two files
|
|
13
14
|
|
|
14
15
|
```sh
|
|
15
|
-
npm install @zackbart/connecta-eve eve@0.
|
|
16
|
+
npm install @zackbart/connecta-eve eve@1.0.0-rc.1
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
The server must declare your agent, admit it to the named pool and issue its
|
|
@@ -89,6 +90,48 @@ Each selected name must match `[a-z0-9_-]+`. A throwing selector or an invalid
|
|
|
89
90
|
name fails the turn, even when guidance is cached. Handle lookup failures in
|
|
90
91
|
your selector if it should fall back to a least-privileged pool.
|
|
91
92
|
|
|
93
|
+
## Gating guidance and connections
|
|
94
|
+
|
|
95
|
+
Pass `when(ctx)` to load guidance only for eligible callers or turns. The helper
|
|
96
|
+
runs this predicate inside Eve's dynamic definition before selecting a pool,
|
|
97
|
+
reading a token, or using cached guidance. False or a thrown/rejected predicate
|
|
98
|
+
returns no guidance and performs no guidance fetch. It is evaluated before
|
|
99
|
+
every model call, including repeated evaluations within a turn.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
export default connectaInstructions({
|
|
103
|
+
pool: "assistant",
|
|
104
|
+
when: (ctx) => ctx.session.auth.current?.principalId === "alice",
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`connectaConnection` accepts the same predicate and returns a dynamic connection
|
|
109
|
+
when it is provided. False or a policy failure removes that connection for the
|
|
110
|
+
current evaluation. The two factories evaluate their predicates independently;
|
|
111
|
+
use consistent trusted policy data as with a dynamic pool selector. This policy
|
|
112
|
+
controls what the app offers; Connecta still enforces endpoint authorization.
|
|
113
|
+
No Eve dynamic-value unwrapping is required.
|
|
114
|
+
|
|
115
|
+
## Upgrading to Eve 1.0 RC
|
|
116
|
+
|
|
117
|
+
Update the app to `eve@1.0.0-rc.1` and reinstall to refresh its lockfile.
|
|
118
|
+
The helper's connection, dynamic pool, instructions and approval APIs use Eve's
|
|
119
|
+
stable entry points and require no authoring changes for this RC. Eve's
|
|
120
|
+
[1.0 stability policy](https://eve.dev/docs/reference/typescript-api#stability-policy)
|
|
121
|
+
does not cover `experimental` fields or internal modules.
|
|
122
|
+
|
|
123
|
+
From 0.90, review the intervening
|
|
124
|
+
[release notes](https://github.com/vercel/eve/blob/eve%401.0.0-rc.1/packages/eve/CHANGELOG.md):
|
|
125
|
+
`session.clear()` now cancels running turns and tasks, and object-form connection
|
|
126
|
+
approvals are now preserved at runtime. RC.1 also requires `@vercel/connect`
|
|
127
|
+
`^2.7.0` when that optional integration is installed; older Connect releases
|
|
128
|
+
reject the prerelease Eve peer.
|
|
129
|
+
|
|
130
|
+
For older apps, follow Eve's [Upgrade to 1.0](https://eve.dev/docs/guides/upgrade)
|
|
131
|
+
guide for event consumers, saved history, extensions and client APIs.
|
|
132
|
+
Build and test the app before deploying it. The helper does not migrate those
|
|
133
|
+
app-owned surfaces.
|
|
134
|
+
|
|
92
135
|
## Upgrading from Eve 0.86
|
|
93
136
|
|
|
94
137
|
Eve 0.88.1 and later pin undici 8.12.3, so the app no longer needs its own undici
|
|
@@ -182,9 +225,9 @@ custom audience, even when Vercel signed them.
|
|
|
182
225
|
|
|
183
226
|
In a source checkout with dependencies installed and both packages built, run
|
|
184
227
|
`pnpm --filter @zackbart/connecta-eve run probe:catalog --output /absolute/path/outside/checkout`
|
|
185
|
-
on Node 24. This boots real Eve 0.
|
|
228
|
+
on Node 24. This boots real Eve 1.0.0-rc.1 with a packed, installed helper against
|
|
186
229
|
seeded local catalogs of 40, 100, 250, 500, and 1,000 downstream tools on the
|
|
187
|
-
meta, native, and native-plus-code surfaces. Native listings include
|
|
230
|
+
meta, native, and native-plus-code surfaces. Native listings include three
|
|
188
231
|
Connecta tools beyond the downstream count. A 1,001-tool native catalog must
|
|
189
232
|
refuse listing. `--counts 40,1000` selects a smaller matrix.
|
|
190
233
|
|
|
@@ -203,3 +246,30 @@ checkout. Its preparation command packs and installs the current core/helper,
|
|
|
203
246
|
checks local signed controls and cancellation, and creates no live resources.
|
|
204
247
|
Live execution requires separate owner authorization for the concrete account,
|
|
205
248
|
project, bounds, cost ceiling and cleanup. Keep its evidence outside the checkout.
|
|
249
|
+
|
|
250
|
+
### Deferred connector guide (opt-in)
|
|
251
|
+
|
|
252
|
+
In `agent/skills/crm.ts`:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import { connectaSkill } from "@zackbart/connecta-eve";
|
|
256
|
+
|
|
257
|
+
export default connectaSkill({
|
|
258
|
+
pool: "crm",
|
|
259
|
+
name: "connector:crm",
|
|
260
|
+
description: "Use when working with CRM person identifiers and conventions.",
|
|
261
|
+
when: (ctx) => ctx.session.auth !== null,
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
This uses the pinned Eve 1.0.0-rc.1 public `defineSkill` and `defineDynamic`
|
|
266
|
+
APIs. Eve's deferred discovery searches the description; loading adds only
|
|
267
|
+
instructions. It never installs tools. Use the same caller predicate and pool
|
|
268
|
+
selector as the connection. A false or throwing predicate skips selection,
|
|
269
|
+
token resolution and fetching. The public API accepts content rather than a
|
|
270
|
+
load callback, so fetching happens once per endpoint/guide/turn through Eve's
|
|
271
|
+
session memo; inclusion in model context is lazy. Failed responses omit the
|
|
272
|
+
skill, including network, timeout and malformed-response failures; turn
|
|
273
|
+
cancellation still propagates. A successful guide can be reused while the same
|
|
274
|
+
endpoint/guide/turn memo is retained. A new turn fetches again; failure removes
|
|
275
|
+
the skill rather than serving the prior turn's guide. The guide must already be visible through that endpoint's `skills` tool.
|
package/dist/index.d.ts
CHANGED
|
@@ -17,9 +17,13 @@ export interface ConnectaEndpointOptions {
|
|
|
17
17
|
}
|
|
18
18
|
/** App-owned pool policy; use `ctx.memo` when its result should stay fixed. */
|
|
19
19
|
export type ConnectaPoolSelector = (ctx: DynamicContext) => string | Promise<string>;
|
|
20
|
+
/** App-owned caller/turn policy, evaluated before selecting a pool or fetching guidance. */
|
|
21
|
+
export type ConnectaPredicate = (ctx: DynamicContext) => boolean | Promise<boolean>;
|
|
20
22
|
export interface ConnectaConnectionOptions extends Omit<ConnectaEndpointOptions, "pool"> {
|
|
21
23
|
/** A fixed pool, or a policy Eve evaluates before every model call. */
|
|
22
24
|
pool?: string | ConnectaPoolSelector;
|
|
25
|
+
/** Omit this value when false or when the policy throws. Evaluated every model call. */
|
|
26
|
+
when?: ConnectaPredicate;
|
|
23
27
|
/** What the connection offers, for the model choosing which connection to search. */
|
|
24
28
|
description: string;
|
|
25
29
|
/** Eve's approval for every Connecta tool: a policy or configuration. Defaults to `never()`. */
|
|
@@ -34,6 +38,8 @@ export interface ConnectaConnectionOptions extends Omit<ConnectaEndpointOptions,
|
|
|
34
38
|
export interface ConnectaInstructionsOptions extends Omit<ConnectaEndpointOptions, "pool"> {
|
|
35
39
|
/** A fixed pool, or the same policy used by the connection. */
|
|
36
40
|
pool?: string | ConnectaPoolSelector;
|
|
41
|
+
/** Omit this value when false or when the policy throws. Evaluated every model call. */
|
|
42
|
+
when?: ConnectaPredicate;
|
|
37
43
|
/** MCP client name for instruction requests. Default `connecta-eve-instructions`. */
|
|
38
44
|
clientName?: string;
|
|
39
45
|
/** How long fetched instructions are reused. Default ten minutes. */
|
|
@@ -52,11 +58,15 @@ export declare const gateConnectaWrites: ApprovalPolicy;
|
|
|
52
58
|
* The Eve connection for a Connecta agent endpoint, with app-owned auth:
|
|
53
59
|
* one agent token, sent as a bearer and re-read on every request.
|
|
54
60
|
*/
|
|
61
|
+
export declare function connectaConnection(options: ConnectaConnectionOptions & {
|
|
62
|
+
when: ConnectaPredicate;
|
|
63
|
+
}): ReturnType<typeof defineDynamicConnection>;
|
|
55
64
|
export declare function connectaConnection(options: ConnectaConnectionOptions & {
|
|
56
65
|
pool: ConnectaPoolSelector;
|
|
57
66
|
}): ReturnType<typeof defineDynamicConnection>;
|
|
58
67
|
export declare function connectaConnection(options: ConnectaConnectionOptions & {
|
|
59
68
|
pool?: string;
|
|
69
|
+
when?: undefined;
|
|
60
70
|
}): McpClientConnectionDefinition;
|
|
61
71
|
export declare function connectaConnection(options: ConnectaConnectionOptions): McpClientConnectionDefinition | ReturnType<typeof defineDynamicConnection>;
|
|
62
72
|
/**
|
|
@@ -67,4 +77,18 @@ export declare function connectaConnection(options: ConnectaConnectionOptions):
|
|
|
67
77
|
* model call; a memo keyed on the endpoint and turn keeps one result for each.
|
|
68
78
|
* Pool policy runs independently of the connection and owns its memo lifetime.
|
|
69
79
|
*/
|
|
70
|
-
export declare function connectaInstructions(options?: ConnectaInstructionsOptions): import("eve/
|
|
80
|
+
export declare function connectaInstructions(options?: ConnectaInstructionsOptions): import("eve/skills").DynamicSentinel<import("eve/instructions").DynamicInstructionsResult, DynamicContext>;
|
|
81
|
+
/** One explicitly selected Connecta guide projected as a deferred Eve skill. */
|
|
82
|
+
export interface ConnectaSkillOptions extends ConnectaInstructionsOptions {
|
|
83
|
+
/** Exact skills-tool name, for example `connector:crm`. */
|
|
84
|
+
name: string;
|
|
85
|
+
/** Short task selector Eve can search without loading the guide. */
|
|
86
|
+
description: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Opt-in deferred instructions, installed from an agent/skills module.
|
|
90
|
+
* Resolves only after caller policy, with no tools added by loading the skill.
|
|
91
|
+
* Eve's public API takes content, not a load callback: fetching is per turn;
|
|
92
|
+
* only model-context inclusion is lazy.
|
|
93
|
+
*/
|
|
94
|
+
export declare function connectaSkill(options: ConnectaSkillOptions): import("eve/skills").DynamicSentinel<import("eve/skills").DynamicSkillResult, DynamicContext>;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defineDynamic as defineDynamicSkill, defineSkill } from "eve/skills";
|
|
1
2
|
import { defineDynamic as defineDynamicConnection, defineMcpClientConnection, } from "eve/connections";
|
|
2
3
|
import { defineDynamic, defineInstructions } from "eve/instructions";
|
|
3
4
|
import { never } from "eve/tools/approval";
|
|
@@ -33,11 +34,18 @@ async function tokenFor(options) {
|
|
|
33
34
|
export const gateConnectaWrites = ({ toolAnnotations }) => toolAnnotations?.readOnlyHint === true ? "not-applicable" : "user-approval";
|
|
34
35
|
export function connectaConnection(options) {
|
|
35
36
|
const { pool, ...rest } = options;
|
|
36
|
-
if (typeof pool === "function") {
|
|
37
|
+
if (typeof pool === "function" || options.when) {
|
|
37
38
|
return defineDynamicConnection(async (ctx) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
if (!(await enabled(options.when, ctx)))
|
|
40
|
+
return null;
|
|
41
|
+
// Only a static `pool` may be omitted; connectaEndpoint validates it. A
|
|
42
|
+
// selector must name a valid pool, or the turn fails instead of using the base URL.
|
|
43
|
+
let selected = pool;
|
|
44
|
+
if (typeof pool === "function") {
|
|
45
|
+
selected = await pool(ctx);
|
|
46
|
+
validatePool(selected);
|
|
47
|
+
}
|
|
48
|
+
return connectionDefinition(options, connectaEndpoint({ ...rest, ...(selected === undefined ? {} : { pool: selected }) }), selected);
|
|
41
49
|
});
|
|
42
50
|
}
|
|
43
51
|
return connectionDefinition(options, connectaEndpoint(pool === undefined ? rest : { ...rest, pool }));
|
|
@@ -54,6 +62,14 @@ function connectionDefinition(options, endpoint, instanceKey) {
|
|
|
54
62
|
approval: options.approval ?? (options.gateWrites ? gateConnectaWrites : never()),
|
|
55
63
|
});
|
|
56
64
|
}
|
|
65
|
+
async function enabled(when, ctx) {
|
|
66
|
+
try {
|
|
67
|
+
return when ? (await when(ctx)) === true : true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
57
73
|
/** Connecta's MCP `instructions` for one endpoint, or null when unavailable. */
|
|
58
74
|
async function instructionRequest(options, endpoint, method, signal) {
|
|
59
75
|
const modern = method === "server/discover";
|
|
@@ -144,6 +160,8 @@ export function connectaInstructions(options = {}) {
|
|
|
144
160
|
return fresh ?? cached?.content ?? null;
|
|
145
161
|
};
|
|
146
162
|
return defineDynamic(async (ctx) => {
|
|
163
|
+
if (!(await enabled(options.when, ctx)))
|
|
164
|
+
return null;
|
|
147
165
|
const { pool, ...rest } = options;
|
|
148
166
|
let endpoint;
|
|
149
167
|
if (typeof pool === "function") {
|
|
@@ -164,3 +182,75 @@ export function connectaInstructions(options = {}) {
|
|
|
164
182
|
return content ? defineInstructions({ content: `Connecta guidance:\n${content}` }) : null;
|
|
165
183
|
});
|
|
166
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Opt-in deferred instructions, installed from an agent/skills module.
|
|
187
|
+
* Resolves only after caller policy, with no tools added by loading the skill.
|
|
188
|
+
* Eve's public API takes content, not a load callback: fetching is per turn;
|
|
189
|
+
* only model-context inclusion is lazy.
|
|
190
|
+
*/
|
|
191
|
+
export function connectaSkill(options) {
|
|
192
|
+
if (!options.name.trim() || !options.description.trim())
|
|
193
|
+
throw new Error("A skill needs a name and description.");
|
|
194
|
+
return defineDynamicSkill(async (ctx) => {
|
|
195
|
+
if (!(await enabled(options.when, ctx)))
|
|
196
|
+
return null;
|
|
197
|
+
const { pool, ...endpointOptions } = options;
|
|
198
|
+
const selected = typeof pool === "function" ? await pool(ctx) : pool;
|
|
199
|
+
if (typeof options.pool === "function")
|
|
200
|
+
validatePool(selected);
|
|
201
|
+
const endpoint = connectaEndpoint({ ...endpointOptions, ...(selected === undefined ? {} : { pool: selected }) });
|
|
202
|
+
const content = await ctx.memo(["connecta-skill", endpoint, options.name, ctx.turn?.id ?? null], async ({ abortSignal }) => {
|
|
203
|
+
try {
|
|
204
|
+
const response = await (options.fetch ?? fetch)(endpoint, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
headers: {
|
|
207
|
+
Authorization: `Bearer ${await tokenFor(options)}`,
|
|
208
|
+
"Content-Type": "application/json",
|
|
209
|
+
Accept: "application/json, text/event-stream",
|
|
210
|
+
"MCP-Protocol-Version": "2026-07-28",
|
|
211
|
+
"Mcp-Method": "tools/call",
|
|
212
|
+
"Mcp-Name": "skills",
|
|
213
|
+
},
|
|
214
|
+
body: JSON.stringify({
|
|
215
|
+
jsonrpc: "2.0",
|
|
216
|
+
id: 1,
|
|
217
|
+
method: "tools/call",
|
|
218
|
+
params: {
|
|
219
|
+
name: "skills",
|
|
220
|
+
arguments: { name: options.name },
|
|
221
|
+
_meta: {
|
|
222
|
+
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
|
|
223
|
+
"io.modelcontextprotocol/clientCapabilities": {},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
signal: AbortSignal.any([abortSignal, AbortSignal.timeout(5_000)]),
|
|
228
|
+
});
|
|
229
|
+
if (!response.ok)
|
|
230
|
+
return null;
|
|
231
|
+
const text = await response.text();
|
|
232
|
+
const json = text.trimStart().startsWith("{")
|
|
233
|
+
? text
|
|
234
|
+
: text
|
|
235
|
+
.split("\n")
|
|
236
|
+
.find((line) => line.startsWith("data:"))
|
|
237
|
+
?.slice(5);
|
|
238
|
+
if (!json)
|
|
239
|
+
return null;
|
|
240
|
+
const reply = JSON.parse(json);
|
|
241
|
+
if (!reply.result || reply.result.isError)
|
|
242
|
+
return null;
|
|
243
|
+
return (reply.result.content
|
|
244
|
+
?.filter((block) => block.type === "text")
|
|
245
|
+
.map((block) => block.text ?? "")
|
|
246
|
+
.join("\n") || null);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Endpoint failures omit optional guidance; turn cancellation still propagates.
|
|
250
|
+
abortSignal.throwIfAborted();
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
return content ? defineSkill({ description: options.description, content, deferred: true }) : null;
|
|
255
|
+
});
|
|
256
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta-eve",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Eve connection and instructions helpers for Connecta agent endpoints.",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"eve": "
|
|
31
|
+
"eve": "1.0.0-rc.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^24.19.2",
|
|
35
|
-
"eve": "0.
|
|
35
|
+
"eve": "1.0.0-rc.1",
|
|
36
36
|
"typescript": "^6.0.3",
|
|
37
37
|
"vite": "^8.3.4",
|
|
38
38
|
"vitest": "^5.0.4"
|