@zackbart/connecta-eve 0.41.0 → 0.42.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 +98 -19
- package/dist/index.d.ts +21 -3
- package/dist/index.js +132 -9
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ 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 1.0.0-rc.
|
|
7
|
+
Requires **Node 24+** and **Eve 1.0.0-rc.4**. Development and consumer
|
|
8
8
|
checks use that exact release candidate. The peer stays pinned during the RC
|
|
9
9
|
period; later candidates and stable 1.0 need a compatibility check. Helper
|
|
10
10
|
releases share Connecta's version and tag; choose the helper version matching
|
|
@@ -13,7 +13,7 @@ your server release.
|
|
|
13
13
|
## Install and add two files
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install @zackbart/connecta-eve eve@1.0.0-rc.
|
|
16
|
+
npm install @zackbart/connecta-eve eve@1.0.0-rc.4
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
The server must declare your agent, admit it to the named pool and issue its
|
|
@@ -31,10 +31,7 @@ Create `agent/connections/connecta.ts`:
|
|
|
31
31
|
```ts
|
|
32
32
|
import { connectaConnection } from "@zackbart/connecta-eve";
|
|
33
33
|
|
|
34
|
-
export default connectaConnection({
|
|
35
|
-
pool: "assistant",
|
|
36
|
-
description: "Shared CRM and tickets for the assistant.",
|
|
37
|
-
});
|
|
34
|
+
export default connectaConnection({ pool: "assistant" });
|
|
38
35
|
```
|
|
39
36
|
|
|
40
37
|
Create `agent/instructions/connecta.ts`:
|
|
@@ -45,6 +42,25 @@ import { connectaInstructions } from "@zackbart/connecta-eve";
|
|
|
45
42
|
export default connectaInstructions({ pool: "assistant" });
|
|
46
43
|
```
|
|
47
44
|
|
|
45
|
+
`description` is optional. Eve shows it in its catalog listing and matches its
|
|
46
|
+
words against every search query for every tool of the connection, so the default
|
|
47
|
+
is a fixed neutral line. If you set one, describe the domain in prose, such as
|
|
48
|
+
`"Member records, giving and event registration."`. Do not list connectors or
|
|
49
|
+
operations: on a three-connector meta pool, `"CRM, invoices and tickets"` made the
|
|
50
|
+
query `"invoices"` return all six routing tools, 9.4 KB measured, and tells the
|
|
51
|
+
model nothing about the operations behind them. The instructions carry the roster.
|
|
52
|
+
A string such as
|
|
53
|
+
`"Company tools through Connecta"` tells the model nothing, so the helper warns
|
|
54
|
+
at startup.
|
|
55
|
+
|
|
56
|
+
Eve names a connection after its file. If yours is not `connecta.ts`, say so in
|
|
57
|
+
the instructions, or the model is told tool names that do not exist:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// agent/connections/workspace.ts gives the model workspace__* tools.
|
|
61
|
+
export default connectaInstructions({ pool: "assistant", connection: "workspace" });
|
|
62
|
+
```
|
|
63
|
+
|
|
48
64
|
Both factories accept `url` to override `CONNECTA_URL`. With `pool`, the URL is
|
|
49
65
|
`<url>/agent/<pool>`; without `pool`, supply the complete endpoint URL. Pool
|
|
50
66
|
names must match `[a-z0-9_-]+`. `connectaEndpoint({ url, pool })` returns the same
|
|
@@ -56,6 +72,16 @@ again for each request, so rotation requires no process restart. A `getToken`
|
|
|
56
72
|
callback can return a string or a promise of one; it overrides the environment.
|
|
57
73
|
No user's identity is forwarded.
|
|
58
74
|
|
|
75
|
+
`headers` adds request headers beside the bearer, such as a service token for an
|
|
76
|
+
edge in front of Connecta. Pass the same value to every factory. A function runs
|
|
77
|
+
for each request, so a rotated secret needs no restart. On guidance and skill
|
|
78
|
+
requests, the helper's own `Authorization` and MCP protocol headers always win.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const headers = () => ({ "X-Edge-Token": process.env.EDGE_TOKEN ?? "" });
|
|
82
|
+
export default connectaConnection({ pool: "assistant", headers });
|
|
83
|
+
```
|
|
84
|
+
|
|
59
85
|
## Choosing the pool at runtime
|
|
60
86
|
|
|
61
87
|
Pass the same selector to both factories when the pool depends on the chat.
|
|
@@ -112,16 +138,40 @@ use consistent trusted policy data as with a dynamic pool selector. This policy
|
|
|
112
138
|
controls what the app offers; Connecta still enforces endpoint authorization.
|
|
113
139
|
No Eve dynamic-value unwrapping is required.
|
|
114
140
|
|
|
115
|
-
## Upgrading to Eve 1.0
|
|
141
|
+
## Upgrading to Eve 1.0.0-rc.4
|
|
116
142
|
|
|
117
|
-
Update the app to `eve@1.0.0-rc.
|
|
118
|
-
The helper's connection, dynamic pool,
|
|
119
|
-
stable entry points and require no
|
|
143
|
+
Update the app to `eve@1.0.0-rc.4`, reinstall to refresh its lockfile, then
|
|
144
|
+
rebuild and test it before deploying. The helper's connection, dynamic pool,
|
|
145
|
+
instructions and approval APIs use Eve's stable entry points and require no
|
|
146
|
+
authoring changes for this RC. Eve's
|
|
120
147
|
[1.0 stability policy](https://eve.dev/docs/reference/typescript-api#stability-policy)
|
|
121
148
|
does not cover `experimental` fields or internal modules.
|
|
122
149
|
|
|
123
|
-
From
|
|
124
|
-
[
|
|
150
|
+
From rc.1, review Eve's
|
|
151
|
+
[rc.2](https://github.com/vercel/eve/releases/tag/eve%401.0.0-rc.2),
|
|
152
|
+
[rc.3](https://github.com/vercel/eve/releases/tag/eve%401.0.0-rc.3) and
|
|
153
|
+
[rc.4](https://github.com/vercel/eve/releases/tag/eve%401.0.0-rc.4) release notes.
|
|
154
|
+
These change app-owned code and deployments the helper does not manage:
|
|
155
|
+
|
|
156
|
+
- **Rollback.** Session checkpoints move to version 19 (rc.3). A deployment from
|
|
157
|
+
before rc.3 refuses to take over a session rc.3 or later wrote, so that session
|
|
158
|
+
stays on the deployment that wrote it. New runs also use Workflow spec 9; set
|
|
159
|
+
`WORKFLOW_SPEC_VERSION=8` to keep creating spec 8 runs.
|
|
160
|
+
- **File tools.** Every agent gets a default `edit_file` tool (rc.4) that changes
|
|
161
|
+
sandbox files without `write_file`. If you disable, wrap or gate `write_file`,
|
|
162
|
+
do the same for `edit_file`. Connecta's own tools are unaffected.
|
|
163
|
+
- **Builds.** Eve moves to a Nitro 3 release candidate and declares Rolldown
|
|
164
|
+
itself (rc.3). Importing `nitro/storage`, `nitro/kv` or `nitro/database` from
|
|
165
|
+
agent code now fails the build, as does an asset import that leaves the authored
|
|
166
|
+
package.
|
|
167
|
+
- **Code extension.** `code__grep`, `code__worker` and `code__apply_patch` are gone
|
|
168
|
+
(rc.3, rc.4). Follow Eve's notes if your app mounts `eve/extensions/code`.
|
|
169
|
+
- **Dynamic tools.** A dynamic tool no longer replaces a static tool of the same
|
|
170
|
+
name (rc.3). Dynamic connections, including this helper's, still override a
|
|
171
|
+
static connection of the same name.
|
|
172
|
+
|
|
173
|
+
From 0.90, also review the
|
|
174
|
+
[rc.1 release notes](https://github.com/vercel/eve/blob/eve%401.0.0-rc.1/packages/eve/CHANGELOG.md):
|
|
125
175
|
`session.clear()` now cancels running turns and tasks, and object-form connection
|
|
126
176
|
approvals are now preserved at runtime. RC.1 also requires `@vercel/connect`
|
|
127
177
|
`^2.7.0` when that optional integration is installed; older Connect releases
|
|
@@ -165,12 +215,38 @@ any tool whose `readOnlyHint` is not `true`, including writable `execute_code`.
|
|
|
165
215
|
`approval`, either a policy or an approval configuration, takes precedence over
|
|
166
216
|
`gateWrites`.
|
|
167
217
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
218
|
+
An agent-wide `approval` in Eve's `agent.ts` runs beside the connection's policy
|
|
219
|
+
and the stricter result wins. It can add a gate to Connecta tools, including under
|
|
220
|
+
the default `never()`, and cannot remove one that `gateWrites` set.
|
|
221
|
+
|
|
222
|
+
The gate uses the host's cached annotations. If a native tool becomes a write, its cached read name is refused before dispatch. Refresh `tools/list` and use the write's name; approval follows the host's policy. Read-only pools refuse writes.
|
|
171
223
|
|
|
172
224
|
## Endpoint instructions
|
|
173
225
|
|
|
226
|
+
Eve hides MCP tools behind `eve__tool` under a `<connection>__` prefix, and
|
|
227
|
+
`eve__search` covers listed tools only. Connecta's text is host-neutral, so the
|
|
228
|
+
helper places an Eve guide before it, built from the endpoint's
|
|
229
|
+
[facts](https://github.com/zackbart/connecta/blob/main/documentation/meta-tools.md#endpoint-facts):
|
|
230
|
+
|
|
231
|
+
- **Meta pools:** call `eve__tool({ name: "connecta__execute_code", input })`
|
|
232
|
+
directly, with a short example program. `eve__search` sees the routing tools and
|
|
233
|
+
never a downstream operation, so its empty result does not mean a capability is
|
|
234
|
+
missing. Reachable connectors are listed by id.
|
|
235
|
+
- **Native pools:** a read is `connecta__<connector>__<tool>` and a write
|
|
236
|
+
`connecta__<connector>__WRITE__<tool>`. Scope a search with
|
|
237
|
+
`"connecta__<connector>__"`, use `execute_code` for several, dependent or reduced
|
|
238
|
+
calls (omitted when the pool has none), and when tools are missing get a link from
|
|
239
|
+
`connecta__authorize_connector` for the user or operator. Eve lists tools again on
|
|
240
|
+
each step, so the repair needs no reconnect, unlike a host that keeps its first
|
|
241
|
+
list.
|
|
242
|
+
- **Older servers without facts:** only the naming rule.
|
|
243
|
+
|
|
244
|
+
The guide adds about 600 bytes with three connectors. Its roster stays under 512
|
|
245
|
+
bytes: titles go first, then whole entries give way to a `+N more` count. Names
|
|
246
|
+
in Connecta's text and in `nextAction` stay unprefixed, and the guide says so.
|
|
247
|
+
The helper warns once per endpoint when a server named `connecta` is on a
|
|
248
|
+
different major or minor release from the helper.
|
|
249
|
+
|
|
174
250
|
Instructions use `server/discover` (MCP 2026-07-28), falling back to legacy
|
|
175
251
|
`initialize` when discovery returns a response without guidance, including a
|
|
176
252
|
non-OK status or an unsupported-method response. Network errors and timeouts
|
|
@@ -225,7 +301,7 @@ custom audience, even when Vercel signed them.
|
|
|
225
301
|
|
|
226
302
|
In a source checkout with dependencies installed and both packages built, run
|
|
227
303
|
`pnpm --filter @zackbart/connecta-eve run probe:catalog --output /absolute/path/outside/checkout`
|
|
228
|
-
on Node 24. This boots real Eve 1.0.0-rc.
|
|
304
|
+
on Node 24. This boots real Eve 1.0.0-rc.4 with a packed, installed helper against
|
|
229
305
|
seeded local catalogs of 40, 100, 250, 500, and 1,000 downstream tools on the
|
|
230
306
|
meta, native, and native-plus-code surfaces. Native listings include three
|
|
231
307
|
Connecta tools beyond the downstream count. A 1,001-tool native catalog must
|
|
@@ -235,8 +311,11 @@ The probe uses Eve's public language-model interface with a deterministic local
|
|
|
235
311
|
transport, without model credentials or paid calls. It measures construction,
|
|
236
312
|
cold and warm discovery, schema and result bytes, call counts, concurrent-call
|
|
237
313
|
isolation, bounded-result paging, and process memory. Fresh child processes
|
|
238
|
-
isolate each matrix cell; Eve startup is separate from turn latency.
|
|
239
|
-
|
|
314
|
+
isolate each matrix cell; Eve startup is separate from turn latency. Each turn
|
|
315
|
+
also checks what the helper's guide claims about Eve: guidance arrives as its own
|
|
316
|
+
system message on every model call, `eve__search` on a meta pool reaches only
|
|
317
|
+
routing tools, native writes are named `connecta__<connector>__WRITE__<tool>`, and
|
|
318
|
+
every step lists tools again. Reports retain actual Eve events and transport inputs. Token usage and model task
|
|
240
319
|
quality remain unmeasured; these results establish local runtime contracts,
|
|
241
320
|
not live Vercel workload admission or production model performance.
|
|
242
321
|
|
|
@@ -262,7 +341,7 @@ export default connectaSkill({
|
|
|
262
341
|
});
|
|
263
342
|
```
|
|
264
343
|
|
|
265
|
-
This uses the pinned Eve 1.0.0-rc.
|
|
344
|
+
This uses the pinned Eve 1.0.0-rc.4 public `defineSkill` and `defineDynamic`
|
|
266
345
|
APIs. Eve's deferred discovery searches the description; loading adds only
|
|
267
346
|
instructions. It never installs tools. Use the same caller predicate and pool
|
|
268
347
|
selector as the connection. A false or throwing predicate skips selection,
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,15 @@ export interface ConnectaEndpointOptions {
|
|
|
14
14
|
* no restart. Defaults to the `CONNECTA_ACCESS_TOKEN` environment variable.
|
|
15
15
|
*/
|
|
16
16
|
getToken?: () => string | Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Extra request headers, such as a service token for an edge in front of
|
|
19
|
+
* Connecta. A function is resolved on every request. These never replace
|
|
20
|
+
* the helper's bearer or MCP protocol headers on guidance requests.
|
|
21
|
+
*/
|
|
22
|
+
headers?: ConnectaHeaders;
|
|
17
23
|
}
|
|
24
|
+
/** Fixed headers, or a resolver called on every request. */
|
|
25
|
+
export type ConnectaHeaders = Readonly<Record<string, string>> | (() => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>);
|
|
18
26
|
/** App-owned pool policy; use `ctx.memo` when its result should stay fixed. */
|
|
19
27
|
export type ConnectaPoolSelector = (ctx: DynamicContext) => string | Promise<string>;
|
|
20
28
|
/** App-owned caller/turn policy, evaluated before selecting a pool or fetching guidance. */
|
|
@@ -24,8 +32,13 @@ export interface ConnectaConnectionOptions extends Omit<ConnectaEndpointOptions,
|
|
|
24
32
|
pool?: string | ConnectaPoolSelector;
|
|
25
33
|
/** Omit this value when false or when the policy throws. Evaluated every model call. */
|
|
26
34
|
when?: ConnectaPredicate;
|
|
27
|
-
/**
|
|
28
|
-
|
|
35
|
+
/**
|
|
36
|
+
* What the connection offers, in Eve's catalog listing and in the words every
|
|
37
|
+
* tool of this connection matches in `eve__search`. Defaults to a fixed
|
|
38
|
+
* neutral line. Describe the domain in prose; do not list connectors or
|
|
39
|
+
* operations here, because a query naming one then matches every tool.
|
|
40
|
+
*/
|
|
41
|
+
description?: string;
|
|
29
42
|
/** Eve's approval for every Connecta tool: a policy or configuration. Defaults to `never()`. */
|
|
30
43
|
approval?: Approval;
|
|
31
44
|
/**
|
|
@@ -40,6 +53,11 @@ export interface ConnectaInstructionsOptions extends Omit<ConnectaEndpointOption
|
|
|
40
53
|
pool?: string | ConnectaPoolSelector;
|
|
41
54
|
/** Omit this value when false or when the policy throws. Evaluated every model call. */
|
|
42
55
|
when?: ConnectaPredicate;
|
|
56
|
+
/**
|
|
57
|
+
* The Eve connection's name, which is its file under `agent/connections/`.
|
|
58
|
+
* The model calls `<connection>__<tool>`. Default `connecta`.
|
|
59
|
+
*/
|
|
60
|
+
connection?: string;
|
|
43
61
|
/** MCP client name for instruction requests. Default `connecta-eve-instructions`. */
|
|
44
62
|
clientName?: string;
|
|
45
63
|
/** How long fetched instructions are reused. Default ten minutes. */
|
|
@@ -79,7 +97,7 @@ export declare function connectaConnection(options: ConnectaConnectionOptions):
|
|
|
79
97
|
*/
|
|
80
98
|
export declare function connectaInstructions(options?: ConnectaInstructionsOptions): import("eve/skills").DynamicSentinel<import("eve/instructions").DynamicInstructionsResult, DynamicContext>;
|
|
81
99
|
/** One explicitly selected Connecta guide projected as a deferred Eve skill. */
|
|
82
|
-
export interface ConnectaSkillOptions extends ConnectaInstructionsOptions {
|
|
100
|
+
export interface ConnectaSkillOptions extends Omit<ConnectaInstructionsOptions, "connection"> {
|
|
83
101
|
/** Exact skills-tool name, for example `connector:crm`. */
|
|
84
102
|
name: string;
|
|
85
103
|
/** Short task selector Eve can search without loading the guide. */
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { defineDynamic as defineDynamicSkill, defineSkill } from "eve/skills";
|
|
|
2
2
|
import { defineDynamic as defineDynamicConnection, defineMcpClientConnection, } from "eve/connections";
|
|
3
3
|
import { defineDynamic, defineInstructions } from "eve/instructions";
|
|
4
4
|
import { never } from "eve/tools/approval";
|
|
5
|
+
import { CONNECTA_VERSION } from "./version.js";
|
|
5
6
|
function environment(name) {
|
|
6
7
|
const value = globalThis.process?.env?.[name];
|
|
7
8
|
return value?.trim() || undefined;
|
|
@@ -33,6 +34,7 @@ async function tokenFor(options) {
|
|
|
33
34
|
*/
|
|
34
35
|
export const gateConnectaWrites = ({ toolAnnotations }) => toolAnnotations?.readOnlyHint === true ? "not-applicable" : "user-approval";
|
|
35
36
|
export function connectaConnection(options) {
|
|
37
|
+
warnOnGenericDescription(options.description);
|
|
36
38
|
const { pool, ...rest } = options;
|
|
37
39
|
if (typeof pool === "function" || options.when) {
|
|
38
40
|
return defineDynamicConnection(async (ctx) => {
|
|
@@ -50,11 +52,25 @@ export function connectaConnection(options) {
|
|
|
50
52
|
}
|
|
51
53
|
return connectionDefinition(options, connectaEndpoint(pool === undefined ? rest : { ...rest, pool }));
|
|
52
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Eve matches these words against every search query, and a short word also
|
|
57
|
+
* matches longer query words ("use" hits "users"), so this avoids common stems.
|
|
58
|
+
*/
|
|
59
|
+
const DEFAULT_DESCRIPTION = "Connecta integration gateway.";
|
|
60
|
+
/** "Company tools through Connecta" and other strings any Connecta connection could claim. */
|
|
61
|
+
const GENERIC_DESCRIPTION = /\b(?:tools?|services?|integrations?|apps?)\s+(?:through|via|from|using|on|in|with)\s+connecta\b|^\W*(?:(?:the|our|all|my|company|connecta|mcp|tools?|services?|integrations?|connections?|gateway|server|access|through|via|using)\W*)*$/i;
|
|
62
|
+
function warnOnGenericDescription(description) {
|
|
63
|
+
if (description === undefined || !GENERIC_DESCRIPTION.test(description))
|
|
64
|
+
return;
|
|
65
|
+
console.warn(`[connecta-eve] Connection description ${JSON.stringify(description)} gives the model no domain information. Omit it, or say what this agent reaches in prose without listing connectors.`);
|
|
66
|
+
}
|
|
53
67
|
function connectionDefinition(options, endpoint, instanceKey) {
|
|
68
|
+
const { headers } = options;
|
|
54
69
|
return defineMcpClientConnection({
|
|
55
70
|
url: endpoint,
|
|
56
71
|
...(instanceKey === undefined ? {} : { instanceKey }),
|
|
57
|
-
description: options.description,
|
|
72
|
+
description: options.description ?? DEFAULT_DESCRIPTION,
|
|
73
|
+
...(headers === undefined ? {} : { headers: typeof headers === "function" ? () => headers() : headers }),
|
|
58
74
|
auth: {
|
|
59
75
|
principalType: "app",
|
|
60
76
|
getToken: async () => ({ token: await tokenFor(options) }),
|
|
@@ -70,18 +86,108 @@ async function enabled(when, ctx) {
|
|
|
70
86
|
return false;
|
|
71
87
|
}
|
|
72
88
|
}
|
|
73
|
-
/**
|
|
89
|
+
/** Older servers omit the extension; anything malformed reads the same way. */
|
|
90
|
+
function endpointFacts(value) {
|
|
91
|
+
const facts = value;
|
|
92
|
+
if (facts?.surface !== "meta" && facts?.surface !== "native")
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof facts.executeCode !== "boolean" || !Array.isArray(facts.connectors))
|
|
95
|
+
return undefined;
|
|
96
|
+
const text = (field) => (typeof field === "string" ? field.replace(/\s+/g, " ").trim() : "");
|
|
97
|
+
return {
|
|
98
|
+
surface: facts.surface,
|
|
99
|
+
executeCode: facts.executeCode,
|
|
100
|
+
readOnly: facts.readOnly === true,
|
|
101
|
+
connectors: facts.connectors.flatMap((connector) => {
|
|
102
|
+
const id = text(connector?.id);
|
|
103
|
+
if (!id)
|
|
104
|
+
return [];
|
|
105
|
+
const title = text(connector.title);
|
|
106
|
+
const readFirst = text(connector.readFirst);
|
|
107
|
+
return [{ id, ...(title ? { title } : {}), ...(readFirst ? { readFirst } : {}) }];
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const ROSTER_MAX_BYTES = 512;
|
|
112
|
+
/** Detail degrades before names do, as the server's own inventory does. */
|
|
113
|
+
function connectorRoster(facts, prefix) {
|
|
114
|
+
const fits = (line) => new TextEncoder().encode(line).length <= ROSTER_MAX_BYTES;
|
|
115
|
+
const entry = ({ id, title, readFirst }, withTitle) => {
|
|
116
|
+
const notes = [
|
|
117
|
+
...(withTitle && title ? [title] : []),
|
|
118
|
+
// A native tool's description already carries its guide pointer.
|
|
119
|
+
...(readFirst && facts.surface === "meta" ? [`read first: ${prefix}skills { name: "${readFirst}" }`] : []),
|
|
120
|
+
];
|
|
121
|
+
return notes.length ? `${id} (${notes.join("; ")})` : id;
|
|
122
|
+
};
|
|
123
|
+
for (const withTitle of [true, false]) {
|
|
124
|
+
const line = facts.connectors.map((connector) => entry(connector, withTitle)).join(", ");
|
|
125
|
+
if (fits(line))
|
|
126
|
+
return line;
|
|
127
|
+
}
|
|
128
|
+
const entries = facts.connectors.map((connector) => entry(connector, false));
|
|
129
|
+
let shown = 0;
|
|
130
|
+
while (fits(`${entries.slice(0, shown + 1).join(", ")}; +${entries.length - shown - 1} more`))
|
|
131
|
+
shown++;
|
|
132
|
+
return `${entries.slice(0, shown).join(", ")}${shown ? "; " : ""}+${entries.length - shown} more`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* How Eve's deferred catalog reaches this endpoint's tools. Connecta's own text
|
|
136
|
+
* names tools as MCP lists them; Eve adds the connection prefix and hides them
|
|
137
|
+
* behind `eve__tool`, and its search covers listed tools only.
|
|
138
|
+
*/
|
|
139
|
+
function eveGuide(connection, facts) {
|
|
140
|
+
const prefix = `${connection}__`;
|
|
141
|
+
if (!facts) {
|
|
142
|
+
return `Connecta tools are deferred: call eve__tool({ name: "${prefix}<tool>", input }). Names in this guidance and in nextAction omit the "${prefix}" prefix.`;
|
|
143
|
+
}
|
|
144
|
+
const roster = facts.connectors.length ? connectorRoster(facts, prefix) : undefined;
|
|
145
|
+
if (facts.surface === "meta") {
|
|
146
|
+
return `Connecta tools are deferred: call eve__tool({ name: "${prefix}<tool>", input }). Names in this guidance and in nextAction omit the "${prefix}" prefix. Go straight to ${prefix}execute_code without eve__search, input { code: "async () => { const { tools } = await connecta.search({ query: 'list invoices', safety: 'readOnly' }); const r = await connecta.call(tools[0].address, {}); return r.data; }" }. eve__search sees only Connecta's routing tools, never downstream operations, so empty results do not mean a capability is missing; search inside the program.${roster ? ` Reachable: ${roster}.` : ""}`;
|
|
147
|
+
}
|
|
148
|
+
return `Connecta tools are deferred: call eve__tool({ name: "${prefix}<connector>__<tool>", input })${facts.readOnly ? "" : `, with writes named "${prefix}<connector>__WRITE__<tool>"`}. Find them with eve__search; scope one connector with "${prefix}<connector>__", not "<connector>__". Names in this guidance and in nextAction omit "${prefix}".${facts.executeCode ? ` For several, dependent or reduced calls, go straight to ${prefix}execute_code with { code: "async () => { ... }" }, using each tool's Program address.` : ""} Tools missing: ${prefix}authorize_connector returns a link; give it to the user or operator once and stop. Eve lists tools again every step, so a repair needs no reconnect.${roster ? ` Connectors: ${roster}.` : ""}`;
|
|
149
|
+
}
|
|
150
|
+
/** Release lines a helper and server must share; a patch difference is compatible. */
|
|
151
|
+
const releaseLine = (version) => typeof version === "string" ? /^(\d+\.\d+)\./.exec(version)?.[1] : undefined;
|
|
152
|
+
/** An endpoint may embed credentials or a token in its URL, path included; logs get only its origin. */
|
|
153
|
+
function loggableEndpoint(endpoint) {
|
|
154
|
+
try {
|
|
155
|
+
return new URL(endpoint).origin;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return "the configured endpoint";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** A deployment that renamed its server may version it independently, so only `connecta` is compared. */
|
|
162
|
+
function warnOnVersionSkew(endpoint, server) {
|
|
163
|
+
if (server?.name !== "connecta")
|
|
164
|
+
return;
|
|
165
|
+
const line = releaseLine(server.version);
|
|
166
|
+
if (line === undefined || line === releaseLine(CONNECTA_VERSION))
|
|
167
|
+
return;
|
|
168
|
+
// The version is remote text, so only its validated numeric line reaches logs.
|
|
169
|
+
console.warn(`[connecta-eve] Helper ${CONNECTA_VERSION} and Connecta ${line}.x at ${loggableEndpoint(endpoint)} are different releases. Install the helper matching your server.`);
|
|
170
|
+
}
|
|
171
|
+
/** Headers for a guidance request. The caller's extras can never replace what the request itself needs. */
|
|
172
|
+
async function guidanceHeaders(options, own) {
|
|
173
|
+
const extra = typeof options.headers === "function" ? await options.headers() : options.headers;
|
|
174
|
+
const headers = new Headers(extra);
|
|
175
|
+
for (const [name, value] of Object.entries(own))
|
|
176
|
+
headers.set(name, value);
|
|
177
|
+
return Object.fromEntries(headers);
|
|
178
|
+
}
|
|
179
|
+
/** Connecta's MCP `instructions` and endpoint facts for one endpoint, or null when unavailable. */
|
|
74
180
|
async function instructionRequest(options, endpoint, method, signal) {
|
|
75
181
|
const modern = method === "server/discover";
|
|
76
182
|
const clientInfo = { name: options.clientName ?? "connecta-eve-instructions", version: "1" };
|
|
77
183
|
const response = await (options.fetch ?? fetch)(endpoint, {
|
|
78
184
|
method: "POST",
|
|
79
|
-
headers: {
|
|
185
|
+
headers: await guidanceHeaders(options, {
|
|
80
186
|
Authorization: `Bearer ${await tokenFor(options)}`,
|
|
81
187
|
"Content-Type": "application/json",
|
|
82
188
|
Accept: "application/json, text/event-stream",
|
|
83
189
|
...(modern ? { "MCP-Protocol-Version": "2026-07-28", "Mcp-Method": method } : {}),
|
|
84
|
-
},
|
|
190
|
+
}),
|
|
85
191
|
body: JSON.stringify({
|
|
86
192
|
jsonrpc: "2.0",
|
|
87
193
|
id: 1,
|
|
@@ -114,8 +220,15 @@ async function instructionRequest(options, endpoint, method, signal) {
|
|
|
114
220
|
?.slice("data:".length);
|
|
115
221
|
if (!json)
|
|
116
222
|
return null;
|
|
117
|
-
const
|
|
118
|
-
|
|
223
|
+
const { result } = JSON.parse(json);
|
|
224
|
+
const instructions = result?.instructions;
|
|
225
|
+
if (typeof instructions !== "string" || !instructions.trim())
|
|
226
|
+
return null;
|
|
227
|
+
return {
|
|
228
|
+
instructions: instructions.trim(),
|
|
229
|
+
facts: endpointFacts(result?.capabilities?.extensions?.["dev.connecta/endpoint"]),
|
|
230
|
+
server: result?._meta?.["io.modelcontextprotocol/serverInfo"] ?? result?.serverInfo,
|
|
231
|
+
};
|
|
119
232
|
}
|
|
120
233
|
async function fetchConnectaInstructions(options, endpoint, signal) {
|
|
121
234
|
// Only a completed discovery response can justify legacy negotiation.
|
|
@@ -133,6 +246,10 @@ async function fetchConnectaInstructions(options, endpoint, signal) {
|
|
|
133
246
|
*/
|
|
134
247
|
export function connectaInstructions(options = {}) {
|
|
135
248
|
const ttlMs = options.ttlMs ?? 600_000;
|
|
249
|
+
const connection = options.connection ?? "connecta";
|
|
250
|
+
if (!/^[A-Za-z0-9._-]+$/.test(connection)) {
|
|
251
|
+
throw new Error(`Connecta connection "${connection}" must be the file name under agent/connections.`);
|
|
252
|
+
}
|
|
136
253
|
const endpoints = new Map();
|
|
137
254
|
const current = async (endpoint, signal) => {
|
|
138
255
|
signal.throwIfAborted();
|
|
@@ -146,7 +263,13 @@ export function connectaInstructions(options = {}) {
|
|
|
146
263
|
return cached?.content ?? null;
|
|
147
264
|
let fresh;
|
|
148
265
|
try {
|
|
149
|
-
|
|
266
|
+
const guidance = await fetchConnectaInstructions(options, endpoint, signal);
|
|
267
|
+
if (guidance && !state.versionChecked) {
|
|
268
|
+
state.versionChecked = true;
|
|
269
|
+
warnOnVersionSkew(endpoint, guidance.server);
|
|
270
|
+
}
|
|
271
|
+
// Eve's half comes first: it says how to read every name in Connecta's own text.
|
|
272
|
+
fresh = guidance && `${eveGuide(connection, guidance.facts)}\n${guidance.instructions}`;
|
|
150
273
|
}
|
|
151
274
|
catch {
|
|
152
275
|
// Eve's cancellation is not an endpoint failure: rethrowing keeps it out
|
|
@@ -203,14 +326,14 @@ export function connectaSkill(options) {
|
|
|
203
326
|
try {
|
|
204
327
|
const response = await (options.fetch ?? fetch)(endpoint, {
|
|
205
328
|
method: "POST",
|
|
206
|
-
headers: {
|
|
329
|
+
headers: await guidanceHeaders(options, {
|
|
207
330
|
Authorization: `Bearer ${await tokenFor(options)}`,
|
|
208
331
|
"Content-Type": "application/json",
|
|
209
332
|
Accept: "application/json, text/event-stream",
|
|
210
333
|
"MCP-Protocol-Version": "2026-07-28",
|
|
211
334
|
"Mcp-Method": "tools/call",
|
|
212
335
|
"Mcp-Name": "skills",
|
|
213
|
-
},
|
|
336
|
+
}),
|
|
214
337
|
body: JSON.stringify({
|
|
215
338
|
jsonrpc: "2.0",
|
|
216
339
|
id: 1,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CONNECTA_VERSION = "0.42.0";
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta-eve",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.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": "1.0.0-rc.
|
|
31
|
+
"eve": "1.0.0-rc.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^24.19.2",
|
|
35
|
-
"eve": "1.0.0-rc.
|
|
35
|
+
"eve": "1.0.0-rc.4",
|
|
36
36
|
"typescript": "^6.0.3",
|
|
37
37
|
"vite": "^8.3.4",
|
|
38
38
|
"vitest": "^5.0.4"
|