@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 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 >=0.90.0 <0.91.0**. Development and consumer
8
- checks use real Eve 0.90.0. The range covers its patch line; newer minor releases
9
- need a compatibility check. Helper releases share Connecta's version and tag;
10
- choose the helper version matching your server release.
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.90.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.90.0 with a packed, installed helper against
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 two
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/connections").DynamicSentinel<import("eve/instructions").DynamicInstructionsResult, DynamicContext>;
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
- const selected = await pool(ctx);
39
- validatePool(selected);
40
- return connectionDefinition(options, connectaEndpoint({ ...rest, pool: selected }), selected);
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.39.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": ">=0.90.0 <0.91.0"
31
+ "eve": "1.0.0-rc.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^24.19.2",
35
- "eve": "0.90.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"