@stackbone/sdk 0.1.0-alpha.6 → 0.1.0-alpha.8
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/CHANGELOG.md +96 -0
- package/README.md +266 -471
- package/agent-registry-BNXuj88Q.d.cts +198 -0
- package/agent-registry-BNXuj88Q.d.ts +198 -0
- package/call-connector-CYDw_yG5.d.cts +118 -0
- package/call-connector-CYDw_yG5.d.ts +118 -0
- package/connect.cjs +270 -0
- package/connect.cjs.map +1 -0
- package/connect.d.cts +94 -0
- package/connect.d.ts +94 -0
- package/connect.js +257 -0
- package/connect.js.map +1 -0
- package/eve.cjs +52 -0
- package/eve.cjs.map +1 -0
- package/eve.d.cts +41 -0
- package/eve.d.ts +41 -0
- package/eve.js +47 -0
- package/eve.js.map +1 -0
- package/index.cjs +435 -60
- package/index.cjs.map +1 -1
- package/index.d.cts +389 -81
- package/index.d.ts +389 -81
- package/index.js +430 -62
- package/index.js.map +1 -1
- package/observability/index.cjs +1 -268
- package/observability/index.cjs.map +1 -1
- package/observability/index.d.cts +1 -96
- package/observability/index.d.ts +1 -96
- package/observability/index.js +2 -264
- package/observability/index.js.map +1 -1
- package/package.json +47 -1
- package/stackbone-sdk-0.1.0-alpha.8.tgz +0 -0
- package/workflow.cjs +17444 -0
- package/workflow.cjs.map +1 -0
- package/workflow.d.cts +128 -0
- package/workflow.d.ts +128 -0
- package/workflow.js +17419 -0
- package/workflow.js.map +1 -0
- package/stackbone-sdk-0.1.0-alpha.6.tgz +0 -0
package/connect.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { ConnectionAuthorizationFailedError, ConnectionAuthorizationRequiredError } from 'eve/connections';
|
|
2
|
+
export { ConnectionAuthorizationFailedError, ConnectionAuthorizationRequiredError } from 'eve/connections';
|
|
3
|
+
import { createHmac } from 'crypto';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var TIMESTAMP_HEADER = "x-stackbone-timestamp";
|
|
8
|
+
var SIGNATURE_HEADER = "x-stackbone-workflow-signature";
|
|
9
|
+
function signBrokerHeaders() {
|
|
10
|
+
const secret = process.env["HMAC_SECRET"] ?? "";
|
|
11
|
+
const timestamp = String(Date.now());
|
|
12
|
+
const signature = createHmac("sha256", secret).update(timestamp).digest("hex");
|
|
13
|
+
return {
|
|
14
|
+
[TIMESTAMP_HEADER]: timestamp,
|
|
15
|
+
[SIGNATURE_HEADER]: signature
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
__name(signBrokerHeaders, "signBrokerHeaders");
|
|
19
|
+
function brokerBaseUrl() {
|
|
20
|
+
const url = process.env["STACKBONE_API_URL"];
|
|
21
|
+
if (!url) {
|
|
22
|
+
throw new Error("STACKBONE_API_URL is not set. The Stackbone runtime injects the broker base URL; run this agent through `stackbone dev`.");
|
|
23
|
+
}
|
|
24
|
+
return url.replace(/\/+$/, "");
|
|
25
|
+
}
|
|
26
|
+
__name(brokerBaseUrl, "brokerBaseUrl");
|
|
27
|
+
function installationId() {
|
|
28
|
+
const id = process.env["STACKBONE_INSTALLATION_ID"];
|
|
29
|
+
if (!id) {
|
|
30
|
+
throw new Error("STACKBONE_INSTALLATION_ID is not set. The Stackbone runtime injects it; run this agent through `stackbone dev`.");
|
|
31
|
+
}
|
|
32
|
+
return id;
|
|
33
|
+
}
|
|
34
|
+
__name(installationId, "installationId");
|
|
35
|
+
|
|
36
|
+
// src/surfaces/external/connect/connect.ts
|
|
37
|
+
var TOKEN_PATH = "/api/connect/token";
|
|
38
|
+
function brokerCodeToEveError(connectorId, code, message) {
|
|
39
|
+
switch (code) {
|
|
40
|
+
case "no_token":
|
|
41
|
+
case "user_authorization_required":
|
|
42
|
+
return new ConnectionAuthorizationRequiredError(connectorId, message ? {
|
|
43
|
+
message
|
|
44
|
+
} : void 0);
|
|
45
|
+
case "connector_installation_required":
|
|
46
|
+
case "app_not_installed":
|
|
47
|
+
return new ConnectionAuthorizationFailedError(connectorId, {
|
|
48
|
+
...message ? {
|
|
49
|
+
message
|
|
50
|
+
} : {},
|
|
51
|
+
reason: "app_not_installed",
|
|
52
|
+
retryable: false
|
|
53
|
+
});
|
|
54
|
+
case "principal_required":
|
|
55
|
+
return new ConnectionAuthorizationFailedError(connectorId, {
|
|
56
|
+
...message ? {
|
|
57
|
+
message
|
|
58
|
+
} : {},
|
|
59
|
+
reason: "principal_required",
|
|
60
|
+
retryable: false
|
|
61
|
+
});
|
|
62
|
+
default:
|
|
63
|
+
return new ConnectionAuthorizationFailedError(connectorId, {
|
|
64
|
+
message: message ?? `Stackbone Connect broker rejected the token request (code "${code}").`,
|
|
65
|
+
reason: code,
|
|
66
|
+
retryable: false
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
__name(brokerCodeToEveError, "brokerCodeToEveError");
|
|
71
|
+
async function readBrokerError(response) {
|
|
72
|
+
const body = await response.json().catch(() => null);
|
|
73
|
+
if (body && typeof body.code === "string") {
|
|
74
|
+
return {
|
|
75
|
+
code: body.code,
|
|
76
|
+
...body.message !== void 0 ? {
|
|
77
|
+
message: body.message
|
|
78
|
+
} : {}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
code: "credential_error",
|
|
83
|
+
message: `Broker responded ${response.status}.`
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
__name(readBrokerError, "readBrokerError");
|
|
87
|
+
async function fetchBrokerToken(connectorId) {
|
|
88
|
+
const body = JSON.stringify({
|
|
89
|
+
connector: connectorId,
|
|
90
|
+
principal: {
|
|
91
|
+
type: "app"
|
|
92
|
+
},
|
|
93
|
+
installationId: installationId()
|
|
94
|
+
});
|
|
95
|
+
let response;
|
|
96
|
+
try {
|
|
97
|
+
response = await fetch(`${brokerBaseUrl()}${TOKEN_PATH}`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: {
|
|
100
|
+
"content-type": "application/json",
|
|
101
|
+
...signBrokerHeaders()
|
|
102
|
+
},
|
|
103
|
+
body
|
|
104
|
+
});
|
|
105
|
+
} catch (cause) {
|
|
106
|
+
throw new ConnectionAuthorizationFailedError(connectorId, {
|
|
107
|
+
message: `Stackbone Connect broker is unreachable: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
108
|
+
reason: "execute_failed",
|
|
109
|
+
retryable: true
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
const error = await readBrokerError(response);
|
|
114
|
+
throw brokerCodeToEveError(connectorId, error.code, error.message);
|
|
115
|
+
}
|
|
116
|
+
const minted = await response.json().catch(() => null);
|
|
117
|
+
if (!minted || typeof minted.token !== "string") {
|
|
118
|
+
throw new ConnectionAuthorizationFailedError(connectorId, {
|
|
119
|
+
message: "Stackbone Connect broker returned a malformed token response.",
|
|
120
|
+
reason: "invalid_output",
|
|
121
|
+
retryable: false
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return minted.expiresAt !== void 0 ? {
|
|
125
|
+
token: minted.token,
|
|
126
|
+
expiresAt: minted.expiresAt
|
|
127
|
+
} : {
|
|
128
|
+
token: minted.token
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
__name(fetchBrokerToken, "fetchBrokerToken");
|
|
132
|
+
async function evictBrokerToken(connectorId) {
|
|
133
|
+
try {
|
|
134
|
+
await fetch(`${brokerBaseUrl()}${TOKEN_PATH.replace(/\/token$/, "/evict")}`, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: {
|
|
137
|
+
"content-type": "application/json",
|
|
138
|
+
...signBrokerHeaders()
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({
|
|
141
|
+
connector: connectorId,
|
|
142
|
+
principal: {
|
|
143
|
+
type: "app"
|
|
144
|
+
},
|
|
145
|
+
installationId: process.env["STACKBONE_INSTALLATION_ID"]
|
|
146
|
+
})
|
|
147
|
+
});
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
__name(evictBrokerToken, "evictBrokerToken");
|
|
152
|
+
function connect(connectorId) {
|
|
153
|
+
return {
|
|
154
|
+
principalType: "app",
|
|
155
|
+
stackboneConnect: {
|
|
156
|
+
connector: connectorId
|
|
157
|
+
},
|
|
158
|
+
getToken(_opts) {
|
|
159
|
+
return fetchBrokerToken(connectorId);
|
|
160
|
+
},
|
|
161
|
+
evict(_opts) {
|
|
162
|
+
return evictBrokerToken(connectorId);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
__name(connect, "connect");
|
|
167
|
+
|
|
168
|
+
// src/surfaces/external/connect/channel-helpers.ts
|
|
169
|
+
function withConnect(definition, connectorId) {
|
|
170
|
+
return {
|
|
171
|
+
...definition,
|
|
172
|
+
auth: connect(connectorId)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
__name(withConnect, "withConnect");
|
|
176
|
+
function connectHeaders(connectorId, headerName = "X-Api-Key") {
|
|
177
|
+
const auth = connect(connectorId);
|
|
178
|
+
return async () => {
|
|
179
|
+
const { token } = await auth.getToken({
|
|
180
|
+
principal: {
|
|
181
|
+
type: "app"
|
|
182
|
+
},
|
|
183
|
+
connection: {
|
|
184
|
+
url: ""
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
[headerName]: token
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
__name(connectHeaders, "connectHeaders");
|
|
193
|
+
|
|
194
|
+
// src/surfaces/external/connect/call-connector.ts
|
|
195
|
+
var EXECUTE_PATH = "/api/connect/execute";
|
|
196
|
+
function connectorCallError(code, message) {
|
|
197
|
+
const error = new Error(message);
|
|
198
|
+
error.code = code;
|
|
199
|
+
return error;
|
|
200
|
+
}
|
|
201
|
+
__name(connectorCallError, "connectorCallError");
|
|
202
|
+
function errorFromBody(status, body) {
|
|
203
|
+
if (body && body.ok === false && body.error && typeof body.error.code === "string") {
|
|
204
|
+
return connectorCallError(body.error.code, body.error.message ?? `Stackbone Connect broker rejected the call (code "${body.error.code}").`);
|
|
205
|
+
}
|
|
206
|
+
return connectorCallError("credential_error", `Stackbone Connect broker responded ${status}.`);
|
|
207
|
+
}
|
|
208
|
+
__name(errorFromBody, "errorFromBody");
|
|
209
|
+
async function callConnector(connector, operation, args, opts) {
|
|
210
|
+
const principal = opts?.principal ?? {
|
|
211
|
+
type: "app"
|
|
212
|
+
};
|
|
213
|
+
const url = `${brokerBaseUrl()}${EXECUTE_PATH}`;
|
|
214
|
+
const body = JSON.stringify({
|
|
215
|
+
connector,
|
|
216
|
+
operation,
|
|
217
|
+
args: args ?? {},
|
|
218
|
+
principal,
|
|
219
|
+
installationId: installationId()
|
|
220
|
+
});
|
|
221
|
+
let response;
|
|
222
|
+
try {
|
|
223
|
+
response = await fetch(url, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: {
|
|
226
|
+
"content-type": "application/json",
|
|
227
|
+
...signBrokerHeaders()
|
|
228
|
+
},
|
|
229
|
+
body
|
|
230
|
+
});
|
|
231
|
+
} catch (cause) {
|
|
232
|
+
throw connectorCallError("execute_failed", `Stackbone Connect broker is unreachable: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
233
|
+
}
|
|
234
|
+
const parsed = await response.json().catch(() => null);
|
|
235
|
+
if (!response.ok || parsed && parsed.ok === false) {
|
|
236
|
+
throw errorFromBody(response.status, parsed ?? null);
|
|
237
|
+
}
|
|
238
|
+
if (!parsed || parsed.ok !== true) {
|
|
239
|
+
throw connectorCallError("invalid_output", "Stackbone Connect broker returned a malformed execute response.");
|
|
240
|
+
}
|
|
241
|
+
return parsed.output;
|
|
242
|
+
}
|
|
243
|
+
__name(callConnector, "callConnector");
|
|
244
|
+
var connectorHandle = /* @__PURE__ */ __name((id) => new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
245
|
+
get(_target, prop) {
|
|
246
|
+
if (typeof prop !== "string" || prop === "then") return void 0;
|
|
247
|
+
if (prop === "call") {
|
|
248
|
+
return (operation, args, opts) => callConnector(id, operation, args, opts);
|
|
249
|
+
}
|
|
250
|
+
return (args, opts) => callConnector(id, prop, args, opts);
|
|
251
|
+
}
|
|
252
|
+
}), "connectorHandle");
|
|
253
|
+
var connection = /* @__PURE__ */ __name((id) => connectorHandle(id), "connection");
|
|
254
|
+
|
|
255
|
+
export { callConnector, connect, connectHeaders, connection, withConnect };
|
|
256
|
+
//# sourceMappingURL=connect.js.map
|
|
257
|
+
//# sourceMappingURL=connect.js.map
|
package/connect.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../libs/sdk/src/surfaces/external/connect/connect-transport.ts","../../../libs/sdk/src/surfaces/external/connect/connect.ts","../../../libs/sdk/src/surfaces/external/connect/channel-helpers.ts","../../../libs/sdk/src/surfaces/external/connect/call-connector.ts"],"names":["TIMESTAMP_HEADER","SIGNATURE_HEADER","signBrokerHeaders","secret","process","env","timestamp","String","Date","now","signature","createHmac","update","digest","brokerBaseUrl","url","Error","replace","installationId","id","TOKEN_PATH","brokerCodeToEveError","connectorId","code","message","ConnectionAuthorizationRequiredError","undefined","ConnectionAuthorizationFailedError","reason","retryable","readBrokerError","response","body","json","catch","status","fetchBrokerToken","JSON","stringify","connector","principal","type","fetch","method","headers","cause","ok","error","minted","token","expiresAt","evictBrokerToken","connect","principalType","stackboneConnect","getToken","_opts","evict","withConnect","definition","auth","connectHeaders","headerName","connection","EXECUTE_PATH","connectorCallError","errorFromBody","callConnector","operation","args","opts","parsed","output","connectorHandle","Proxy","Object","create","get","_target","prop"],"mappings":"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","file":"connect.js","sourcesContent":["// Shared transport primitives for the Stackbone Connect runtime calls (feature\n// 74). Both the connection-auth ADAPTER (`connect.ts` — the eve `getToken`/`evict`\n// strategy) and the direct workflow→connector CALL helper (`call-connector.ts`)\n// hit the SAME agent-facing broker under `STACKBONE_API_URL` with the SAME HMAC\n// scheme-A signature, so the signing algorithm, header names, and env contract\n// live here ONCE. Duplicating the HMAC in two places risks the two diverging\n// (one signing the raw string, the other a number/Buffer) and the broker's\n// constant-time verifier would then reject one of them.\n//\n// This module is a PLAIN node module: it statically imports nothing but\n// `node:crypto`, so it is safe for the eve-importing `connect.ts` AND the\n// eve-free `call-connector.ts` to share.\n\nimport { createHmac } from 'node:crypto';\n\n/**\n * WHO a connector call is made on behalf of — the scoping principal the broker\n * keys its credential cache/grant against. A STRUCTURAL mirror of the broker\n * contract's `@stackbone/connect-contract` `Principal` union (app / user /\n * client-credentials / jwt-bearer); defined locally rather than imported so the\n * SDK connect surface stays a self-contained leaf (the sibling eve adapter follows\n * the same discipline — it never pulls the platform contract into the published\n * SDK tarball). It is structurally compatible, so a value typed against the\n * contract's `Principal` is assignable here and vice-versa.\n */\nexport type Principal =\n | { readonly type: 'app' }\n | {\n readonly type: 'user';\n readonly id: string;\n readonly issuer: string;\n readonly attributes?: Readonly<Record<string, string | readonly string[]>>;\n }\n | { readonly type: 'client-credentials'; readonly serviceAccountId: string }\n | { readonly type: 'jwt-bearer'; readonly subject: string; readonly issuer: string };\n\n/** The header the agent-facing broker request stamps the raw timestamp under. */\nexport const TIMESTAMP_HEADER = 'x-stackbone-timestamp';\n/** The header the agent-facing broker request stamps the HMAC signature under. */\nexport const SIGNATURE_HEADER = 'x-stackbone-workflow-signature';\n\n/**\n * Sign an agent-facing broker request so the broker's HMAC verifier accepts it.\n * The signature is `hex(HMAC-SHA256(HMAC_SECRET, timestamp))` over the RAW\n * timestamp string. This MUST stay byte-identical to the agent's\n * `hmacWorkflowAuth` verifier and the emulator's `verifyTokenRequestSignature`\n * (same algorithm, same raw-string input, same header names, 5-minute drift\n * window on the verifier) — do not sign a number/Buffer, bind the body, or\n * rename the headers. An empty secret produces a signature the fail-closed\n * verifier rejects, which is the correct behaviour for a misconfigured runtime.\n */\nexport function signBrokerHeaders(): Record<string, string> {\n const secret = process.env['HMAC_SECRET'] ?? '';\n const timestamp = String(Date.now());\n const signature = createHmac('sha256', secret).update(timestamp).digest('hex');\n return {\n [TIMESTAMP_HEADER]: timestamp,\n [SIGNATURE_HEADER]: signature,\n };\n}\n\n/**\n * Resolve the base URL of the Stackbone Connect broker. The runtime injects it on\n * the agent / workflow process's env (never hardcode a host — the broker is\n * deployable per env: cloud / emulator / self-host). The trailing slash(es) are\n * trimmed so callers append `/api/connect/...` without doubling the separator.\n */\nexport function brokerBaseUrl(): string {\n const url = process.env['STACKBONE_API_URL'];\n if (!url) {\n throw new Error(\n 'STACKBONE_API_URL is not set. The Stackbone runtime injects the broker base URL; run this agent through `stackbone dev`.',\n );\n }\n return url.replace(/\\/+$/, '');\n}\n\n/**\n * Read the install id the broker request asserts as its scoping claim. The\n * emulator (and, in production, the client's VM) injects it on the process's env;\n * absent → the caller is not running under a Stackbone runtime.\n */\nexport function installationId(): string {\n const id = process.env['STACKBONE_INSTALLATION_ID'];\n if (!id) {\n throw new Error(\n 'STACKBONE_INSTALLATION_ID is not set. The Stackbone runtime injects it; run this agent through `stackbone dev`.',\n );\n }\n return id;\n}\n","// `connect()` — the Stackbone Connect eve ADAPTER (feature 74, Phase G).\n//\n// A connection authored under `agent/connections/<slug>.ts` declares an `auth`\n// strategy that eve probes before every tool call. `connect(connectorId)` returns\n// that strategy, pre-wired to fetch a short-lived credential from the Stackbone\n// Connect BROKER instead of holding a static key — so the operator registers the\n// connector + its OAuth app ONCE in Studio and every agent's connection resolves\n// its token through the broker at call time.\n//\n// SCOPE — DEV-ONLY, APP-SCOPED, NON-INTERACTIVE. This adapter implements ONLY the\n// `principalType: 'app'`, `getToken`-only path: the broker mints the agent's OWN\n// service-account credential out of band (the operator completes the OAuth dance\n// in Studio), and `getToken` just hands it over. There is NO `startAuthorization`\n// / `completeAuthorization` and NO `principalType: 'user'` here — interactive,\n// per-user OAuth rides eve's durable hooks and is a FUTURE phase. `getToken`\n// THROWS to signal \"needs authorization\" (the operator must finish the dance);\n// it never returns null.\n//\n// eve is an OPTIONAL EXTERNAL PEER (kept external by the SDK build; pinned only in\n// the creator's eve workspace), so:\n// - types come from the `eve-connections-peer.d.ts` shim, NOT a hard dependency;\n// - the two eve error classes are MATCHED/PRODUCED BY `err.name`, never\n// `instanceof` — under bundling the adapter and eve can end up with different\n// class identities (dual-instance hazard), so name is the only stable signal.\n// Importing the classes from `'eve/connections'` (external) means at runtime\n// they ARE the creator's eve classes, so eve's\n// `isConnectionAuthorizationRequiredError`/`...Failed` guards (which test\n// `err.name`) recognise what the adapter throws.\n//\n// CALLER IDENTITY — HMAC scheme A (install identity), byte-identical to\n// `surfaces/external/agents/eve-agent.ts signWorkflowHeaders()`: the broker token\n// request carries `hex(HMAC-SHA256(HMAC_SECRET, String(Date.now())))` over the RAW\n// timestamp string under `x-stackbone-timestamp` + `x-stackbone-workflow-signature`.\n// HMAC proves POSSESSION of the shared per-workspace secret, NOT which install is\n// calling, so the request body ALSO carries the explicit `installationId`\n// (`STACKBONE_INSTALLATION_ID`) + connector + `principal: { type: 'app' }`.\n\nimport {\n ConnectionAuthorizationFailedError,\n ConnectionAuthorizationRequiredError,\n type ConnectionAuthDefinition,\n type ConnectionAuthorizationContext,\n type ConnectionPrincipal,\n type TokenResult,\n} from 'eve/connections';\n\nimport { brokerBaseUrl, installationId, signBrokerHeaders } from './connect-transport';\n\n/** The broker route the adapter POSTs a token request to (under `STACKBONE_API_URL`). */\nconst TOKEN_PATH = '/api/connect/token';\n\n/**\n * The auth strategy `connect()` returns, widened with the Stackbone attribution\n * marker. Structurally a `getToken`-only eve `ConnectionAuthDefinition` (so it\n * drops straight into `defineMcpClientConnection({ auth })` /\n * `defineOpenAPIConnection({ auth })`), plus a `stackboneConnect` marker — the\n * sibling of eve's `vercelConnect` — that lets build/Studio tooling detect a\n * broker-backed connection without inspecting the `getToken` closure.\n */\nexport interface StackboneConnectAuth extends ConnectionAuthDefinition {\n /** App-scoped: the adapter only ever emits the non-interactive `'app'` path. */\n readonly principalType: 'app';\n /** Compile-time attribution marker; ignored by eve's token path. */\n readonly stackboneConnect: { readonly connector: string };\n}\n\n/** The broker's token-mint success body — byte-aligned with eve's `TokenResult`. */\ninterface BrokerTokenResponse {\n /** The short-lived scoped token (always applied as `Authorization: Bearer`). */\n readonly token: string;\n /** Absolute ms-since-epoch advisory expiry — same unit as eve's `expiresAt`. */\n readonly expiresAt?: number;\n}\n\n/** The broker's error envelope — discriminated by the string `code`, never a class. */\ninterface BrokerErrorBody {\n readonly code: string;\n readonly message?: string;\n}\n\n/**\n * Translate a broker error code (read off the error body) into the eve error the\n * runtime understands. The mapping is matched/produced BY NAME — the eve classes\n * imported from `'eve/connections'` set `this.name` to their own class name and\n * eve's guards switch on that, so a constructed instance is recognised across the\n * peer boundary.\n *\n * - `no_token` / `user_authorization_required` → `ConnectionAuthorizationRequiredError`\n * (the principal must authorise out of band; eve emits `authorization.required`).\n * - `connector_installation_required` / `app_not_installed` →\n * `ConnectionAuthorizationFailedError(reason: 'app_not_installed', retryable: false)`\n * (terminal: the operator never installed the connector's credentials).\n * - `principal_required` → terminal `ConnectionAuthorizationFailedError`.\n * - any other broker code → a NON-retryable failure (a credential-layer error the\n * adapter cannot map more precisely; do not re-prompt on a wrong-token-shape bug).\n */\nfunction brokerCodeToEveError(connectorId: string, code: string, message?: string): Error {\n switch (code) {\n case 'no_token':\n case 'user_authorization_required':\n return new ConnectionAuthorizationRequiredError(\n connectorId,\n message ? { message } : undefined,\n );\n case 'connector_installation_required':\n case 'app_not_installed':\n return new ConnectionAuthorizationFailedError(connectorId, {\n ...(message ? { message } : {}),\n reason: 'app_not_installed',\n retryable: false,\n });\n case 'principal_required':\n return new ConnectionAuthorizationFailedError(connectorId, {\n ...(message ? { message } : {}),\n reason: 'principal_required',\n retryable: false,\n });\n default:\n return new ConnectionAuthorizationFailedError(connectorId, {\n message: message ?? `Stackbone Connect broker rejected the token request (code \"${code}\").`,\n reason: code,\n retryable: false,\n });\n }\n}\n\n/**\n * Read the broker's `{ code, message }` error envelope off a non-2xx response,\n * tolerating an empty / non-JSON body (a proxy 502 may carry no JSON).\n */\nasync function readBrokerError(response: Response): Promise<BrokerErrorBody> {\n const body = (await response.json().catch(() => null)) as Partial<BrokerErrorBody> | null;\n if (body && typeof body.code === 'string') {\n // Omit `message` when absent rather than setting it to `undefined`:\n // `exactOptionalPropertyTypes: true` forbids an explicit `undefined` on the\n // optional `message` prop (matches the spread pattern in brokerCodeToEveError).\n return { code: body.code, ...(body.message !== undefined ? { message: body.message } : {}) };\n }\n return { code: 'credential_error', message: `Broker responded ${response.status}.` };\n}\n\n/**\n * POST a broker token request and return the eve `TokenResult` VERBATIM (the\n * broker's `{ token, expiresAt }` is byte-aligned with eve's `TokenResult`, and\n * `expiresAt` is already absolute ms — no unit conversion).\n *\n * A broker error body is translated to the matching eve error BY NAME. A\n * transport-level failure (DNS / connection refused / the fetch itself rejects)\n * is a TRANSIENT condition → a RETRYABLE `ConnectionAuthorizationFailedError` so\n * eve re-prompts on a flaky broker rather than treating it as a hard auth failure.\n */\nasync function fetchBrokerToken(connectorId: string): Promise<TokenResult> {\n const body = JSON.stringify({\n connector: connectorId,\n principal: { type: 'app' as const },\n installationId: installationId(),\n });\n let response: Response;\n try {\n response = await fetch(`${brokerBaseUrl()}${TOKEN_PATH}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...signBrokerHeaders(),\n },\n body,\n });\n } catch (cause) {\n // Network / transport failure → transient, retryable.\n throw new ConnectionAuthorizationFailedError(connectorId, {\n message: `Stackbone Connect broker is unreachable: ${\n cause instanceof Error ? cause.message : String(cause)\n }`,\n reason: 'execute_failed',\n retryable: true,\n });\n }\n\n if (!response.ok) {\n const error = await readBrokerError(response);\n throw brokerCodeToEveError(connectorId, error.code, error.message);\n }\n\n const minted = (await response.json().catch(() => null)) as BrokerTokenResponse | null;\n if (!minted || typeof minted.token !== 'string') {\n throw new ConnectionAuthorizationFailedError(connectorId, {\n message: 'Stackbone Connect broker returned a malformed token response.',\n reason: 'invalid_output',\n retryable: false,\n });\n }\n // Return the eve TokenResult verbatim: `{ token, expiresAt? }`, ms-aligned.\n return minted.expiresAt !== undefined\n ? { token: minted.token, expiresAt: minted.expiresAt }\n : { token: minted.token };\n}\n\n/**\n * Best-effort: tell the broker to drop its cached token for this connector +\n * (app) principal so the NEXT `getToken` re-mints. Called by eve's `evict` hook\n * right after a provider 401. Swallows its own errors — eviction is a recovery\n * optimisation, never a failure path.\n */\nasync function evictBrokerToken(connectorId: string): Promise<void> {\n try {\n await fetch(`${brokerBaseUrl()}${TOKEN_PATH.replace(/\\/token$/, '/evict')}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...signBrokerHeaders(),\n },\n body: JSON.stringify({\n connector: connectorId,\n principal: { type: 'app' as const },\n installationId: process.env['STACKBONE_INSTALLATION_ID'],\n }),\n });\n } catch {\n // Best-effort: a failed evict just means the next getToken may reuse a stale\n // cached token; it never breaks the tool call.\n }\n}\n\n/**\n * `connect(connectorId)` — wire a connection's `auth` to the Stackbone Connect\n * broker for the agent's OWN (app-scoped) credential.\n *\n * Use it as the `auth` field of a connection definition:\n *\n * ```ts\n * import { defineOpenAPIConnection } from 'eve/connections';\n * import { connect } from '@stackbone/sdk/connect';\n *\n * export default defineOpenAPIConnection({\n * spec: 'https://api.example.com/openapi.json',\n * auth: connect('example'),\n * });\n * ```\n *\n * `getToken` fetches a short-lived bearer from the broker (HMAC-signed,\n * install-scoped). When the broker reports the connector's credentials are not yet\n * installed it THROWS `ConnectionAuthorizationRequiredError` (eve emits\n * `authorization.required`); a hard broker failure throws\n * `ConnectionAuthorizationFailedError(reason, retryable)`. `evict` purges the\n * broker's cached token on a 401 so the next `getToken` re-mints.\n *\n * @param connectorId The connector id registered in Studio (matches\n * `Connector.connectorId`). Stamped onto the returned `stackboneConnect` marker.\n */\nexport function connect(connectorId: string): StackboneConnectAuth {\n return {\n principalType: 'app',\n stackboneConnect: { connector: connectorId },\n getToken(_opts: {\n readonly principal: ConnectionPrincipal;\n readonly connection: ConnectionAuthorizationContext;\n }): Promise<TokenResult> {\n // App-scoped: the principal is always `{ type: 'app' }` for this adapter —\n // the broker request asserts it explicitly (not derived from `_opts`).\n return fetchBrokerToken(connectorId);\n },\n evict(_opts: {\n readonly principal: ConnectionPrincipal;\n readonly connection: ConnectionAuthorizationContext;\n }): Promise<void> {\n return evictBrokerToken(connectorId);\n },\n };\n}\n\n/**\n * Re-export the two eve connection-auth error classes the adapter PRODUCES, so a\n * creator authoring a custom `getToken` (or a test asserting the adapter's\n * behaviour) constructs the SAME classes the broker maps to — and matches them BY\n * `err.name`, never `instanceof`.\n */\nexport { ConnectionAuthorizationRequiredError, ConnectionAuthorizationFailedError };\n","// Stackbone Connect — channel/connection helpers (feature 74, Phase G).\n//\n// `connect(connectorId)` returns the `auth` strategy for a single connection. The\n// helpers here are the thin glue that ATTACHES that broker-backed auth (and any\n// broker-resolved request headers) onto an eve connection/channel definition,\n// without the creator threading `connect()` into every `define*` call by hand.\n//\n// Like `connect()`, these are app-scoped + non-interactive only and produce eve\n// shapes structurally (eve stays an OPTIONAL external peer — types come from the\n// `eve-connections-peer.d.ts` shim, no hard dependency).\n//\n// CREDENTIAL PLACEMENT — the de-risk notes flag that eve carries ONLY a Bearer\n// token through `auth.getToken` (it hardcodes `Authorization: Bearer <token>`).\n// Anything else — an API-key header (`X-Api-Key`), a custom header — has no slot\n// on eve's `TokenResult`; it rides the connection's SEPARATE `headers` closure.\n// `withConnect` covers the Bearer path (sets `auth`); `connectHeaders` covers the\n// non-Bearer path (produces the `headers` closure).\n\nimport { connect, type StackboneConnectAuth } from './connect';\nimport type { ConnectionAuthDefinition } from 'eve/connections';\n\n/**\n * A connection definition with an `auth` slot — the common shape of what eve's\n * `defineMcpClientConnection` / `defineOpenAPIConnection` accept and return. Kept\n * structural so the helpers compose over either factory's output without importing\n * the concrete definition types.\n */\nexport interface ConnectionLike {\n /** The auth strategy eve probes before every tool call. */\n auth?: ConnectionAuthDefinition;\n}\n\n/**\n * Attach a broker-backed `auth` to an existing connection definition.\n *\n * Sugar over `{ ...definition, auth: connect(connectorId) }` for creators who\n * build the connection with `defineMcpClientConnection` / `defineOpenAPIConnection`\n * first and want to bolt Stackbone Connect on without re-typing the `auth` field.\n * The returned definition carries the `stackboneConnect` attribution marker so\n * build/Studio tooling detects it.\n *\n * @param definition The eve connection definition to wrap.\n * @param connectorId The connector id registered in Studio.\n */\nexport function withConnect<T extends ConnectionLike>(\n definition: T,\n connectorId: string,\n): T & { auth: StackboneConnectAuth } {\n return { ...definition, auth: connect(connectorId) };\n}\n\n/**\n * Resolve the broker-managed request headers for a connector (the non-Bearer\n * credential-placement path: API-key headers, custom headers). eve carries Bearer\n * tokens through `auth.getToken`, but anything else rides the connection's\n * separate `headers` closure — this helper produces that closure, pre-wired to the\n * broker.\n *\n * The returned closure runs fresh per request (eve re-resolves `headers` on every\n * call), so a rotated credential is always current. It reuses `connect()`'s\n * broker round-trip via the connection's `getToken`, then writes the minted token\n * under `headerName` (defaults to `X-Api-Key`, the common provider convention). If\n * the broker signals the credential is missing, the underlying eve error\n * propagates verbatim (matched by name downstream) — the header closure never\n * masks it.\n *\n * @param connectorId The connector id registered in Studio.\n * @param headerName The header to place the minted token under (default `X-Api-Key`).\n */\nexport function connectHeaders(\n connectorId: string,\n headerName = 'X-Api-Key',\n): () => Promise<Record<string, string>> {\n const auth = connect(connectorId);\n return async (): Promise<Record<string, string>> => {\n // Drive the same broker round-trip `auth.getToken` performs. The `connection`\n // url is unused on the app-scoped path (the broker request asserts the\n // principal explicitly), so pass an empty context.\n const { token } = await auth.getToken({\n principal: { type: 'app' },\n connection: { url: '' },\n });\n return { [headerName]: token };\n };\n}\n","// `callConnector()` / `connection()` — call a Stackbone Connect connector DIRECTLY\n// from a workflow, with NO agent in the loop (feature 74).\n//\n// A workflow `\"use step\"` often needs to perform a single provider action (send a\n// Slack message, create a GitHub issue) without spinning up an agent turn. This\n// helper POSTs the action to the agent-facing broker `POST /api/connect/execute`\n// (the emulator in `stackbone dev`, the client's VM in production) which runs ONE\n// `ConnectActionExecutor.execute` against the provider on the caller's behalf and\n// returns the operation output.\n//\n// It is a PLAIN HMAC fetch client: unlike the sibling `connect.ts` (the eve\n// connection-auth ADAPTER), this file imports NO eve types — a workflow that calls\n// a connector directly need not author an eve connection. It shares the SAME\n// transport (HMAC scheme A + env contract) as the adapter via `connect-transport`,\n// so the broker's one verifier accepts both byte-for-byte.\n//\n// CALLER IDENTITY — HMAC scheme A (install identity), byte-identical to\n// `surfaces/external/agents/eve-agent.ts signWorkflowHeaders()`: the request\n// carries `hex(HMAC-SHA256(HMAC_SECRET, String(Date.now())))` over the RAW\n// timestamp string under `x-stackbone-timestamp` + `x-stackbone-workflow-signature`.\n// HMAC proves POSSESSION of the shared per-workspace secret, NOT which install is\n// calling, so the body ALSO carries the explicit `installationId`\n// (`STACKBONE_INSTALLATION_ID`) + connector + operation + principal scoping claims.\n\nimport {\n brokerBaseUrl,\n installationId,\n signBrokerHeaders,\n type Principal,\n} from './connect-transport';\n\n/** The broker route the direct connector call POSTs to (under `STACKBONE_API_URL`). */\nconst EXECUTE_PATH = '/api/connect/execute';\n\n/** Options accepted by `callConnector` / `connection(...).call`. */\nexport interface CallConnectorOptions {\n /**\n * WHO the connector call is made on behalf of. Defaults to `{ type: 'app' }`\n * (the agent's own service-account credential). A `user` principal scopes the\n * broker token issuer to that end user; `client-credentials` / `jwt-bearer` are\n * the broker's M2M auth modes.\n */\n readonly principal?: Principal;\n}\n\n/**\n * The broker's `POST /api/connect/execute` success envelope. The executor's\n * operation output rides under `output` (arbitrary JSON the provider returned).\n */\ninterface ExecuteSuccessBody {\n readonly ok: true;\n readonly output: unknown;\n}\n\n/** The broker's error envelope — discriminated by the string `code`, never a class. */\ninterface ExecuteErrorBody {\n readonly ok: false;\n readonly error: { readonly code: string; readonly message?: string };\n}\n\n/**\n * An `Error` carrying the broker's machine-readable `code` so a workflow matches\n * the failure BY CODE STRING (never `instanceof` — under bundling the SDK and the\n * workflow can hold different class identities; the code is the stable signal).\n */\nexport interface ConnectorCallError extends Error {\n /** The broker error code (`invalid_args`, `credential_error`, `timeout`, …). */\n readonly code: string;\n}\n\n/**\n * Build the `ConnectorCallError` thrown on a broker error. The `code` is carried\n * as an own property so `err.code` reads back the broker taxonomy.\n */\nfunction connectorCallError(code: string, message: string): ConnectorCallError {\n const error = new Error(message) as Error & { code: string };\n error.code = code;\n return error;\n}\n\n/**\n * Read the broker's `{ ok: false, error: { code, message } }` envelope off a\n * non-2xx (or `ok: false`) response, tolerating an empty / non-JSON body (a proxy\n * 502 may carry no JSON). Falls back to a `credential_error` keyed off the HTTP\n * status when no structured code is present.\n */\nfunction errorFromBody(status: number, body: ExecuteErrorBody | null): ConnectorCallError {\n if (body && body.ok === false && body.error && typeof body.error.code === 'string') {\n return connectorCallError(\n body.error.code,\n body.error.message ??\n `Stackbone Connect broker rejected the call (code \"${body.error.code}\").`,\n );\n }\n return connectorCallError('credential_error', `Stackbone Connect broker responded ${status}.`);\n}\n\n/**\n * Call a Stackbone Connect connector operation directly from a workflow.\n *\n * POSTs `{ connector, operation, args, principal, installationId }` to the\n * agent-facing broker (`POST /api/connect/execute`) with HMAC scheme-A headers.\n * On `{ ok: true, output }` it returns the operation `output`; on\n * `{ ok: false, error }` (or any non-2xx) it THROWS a `ConnectorCallError` whose\n * `.code` is the broker error code and `.message` the broker message.\n *\n * ```ts\n * const channels = await callConnector('slack', 'conversations.list');\n * await callConnector('slack', 'chat.postMessage', { channel: 'C123', text: 'hi' });\n * ```\n *\n * @param connector The connect connector id registered in Studio.\n * @param operation The operation / tool id to run on that connector.\n * @param args The operation arguments (path/query/body params). Defaults `{}`.\n * @param opts Optional `{ principal }` — defaults to `{ type: 'app' }`.\n */\nexport async function callConnector(\n connector: string,\n operation: string,\n args?: Readonly<Record<string, unknown>>,\n opts?: CallConnectorOptions,\n): Promise<unknown> {\n const principal: Principal = opts?.principal ?? { type: 'app' };\n // Resolve the env-derived URL + install id BEFORE the fetch try-block so a\n // missing `STACKBONE_API_URL` / `STACKBONE_INSTALLATION_ID` surfaces its own\n // explicit error instead of being swallowed into the transport `execute_failed`.\n const url = `${brokerBaseUrl()}${EXECUTE_PATH}`;\n const body = JSON.stringify({\n connector,\n operation,\n args: args ?? {},\n principal,\n installationId: installationId(),\n });\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...signBrokerHeaders(),\n },\n body,\n });\n } catch (cause) {\n // Network / transport failure → a retryable execute failure surfaced by code.\n throw connectorCallError(\n 'execute_failed',\n `Stackbone Connect broker is unreachable: ${\n cause instanceof Error ? cause.message : String(cause)\n }`,\n );\n }\n\n const parsed = (await response.json().catch(() => null)) as\n | ExecuteSuccessBody\n | ExecuteErrorBody\n | null;\n\n if (!response.ok || (parsed && parsed.ok === false)) {\n throw errorFromBody(response.status, (parsed as ExecuteErrorBody | null) ?? null);\n }\n\n if (!parsed || parsed.ok !== true) {\n // 2xx but a body that is neither `{ ok: true }` nor a structured error —\n // treat as a malformed broker response rather than silently returning it.\n throw connectorCallError(\n 'invalid_output',\n 'Stackbone Connect broker returned a malformed execute response.',\n );\n }\n\n return parsed.output;\n}\n\n/** The dynamic escape hatch present on every `connection(id)` handle. */\nexport interface ConnectorHandle {\n /**\n * Run ANY operation on this connector by id — the untyped escape hatch, always\n * available (use it for an operation id that is not a JS identifier, e.g. a\n * dotted `chat.postMessage`, or a connector with no generated types). Sugar for\n * `callConnector(id, operation, args, opts)`.\n */\n call(\n operation: string,\n args?: Readonly<Record<string, unknown>>,\n opts?: CallConnectorOptions,\n ): Promise<unknown>;\n}\n\n/**\n * Augmentable registry of TYPED connector operations, keyed by the VERBATIM\n * connector id (`'stub-mail'`, `'slack'`). The Stackbone Connect type generator\n * (`stackbone dev` writes `.stackbone/connect.d.ts`) MERGES one member per connector\n * into this interface via a `declare module` augmentation, each a map of the\n * connector's operations → typed `(args) => Promise<output>`, derived from the\n * introspected JSON Schema. Empty by default — with no generated types,\n * `connection(id)` offers only the dynamic `.call(...)`.\n *\n * Same id-keyed-registry mechanism as `eveAgent`'s `AgentRegistry`: the selection\n * style is unified — address an entity by its verbatim id, get a typed handle.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface StackboneConnections {}\n\n/**\n * The typed handle for a connector id: its generated operations (when the id is a\n * known key of `StackboneConnections`) intersected with the dynamic `.call` escape\n * hatch (always present). An unknown id resolves to just `ConnectorHandle`.\n */\nexport type ConnectorOf<Id extends string> = (Id extends keyof StackboneConnections\n ? StackboneConnections[Id]\n : unknown) &\n ConnectorHandle;\n\n/**\n * The exported `connection`: select a connector by its verbatim id and get a typed\n * handle. A known id (a key of the generated `StackboneConnections`) yields its\n * typed operations + `.call`; any other string yields the dynamic `ConnectorHandle`.\n */\nexport interface ConnectionAccessor {\n <Id extends keyof StackboneConnections>(id: Id): ConnectorOf<Id>;\n (id: string): ConnectorHandle;\n}\n\n/**\n * Build the per-id handle: a `Proxy` where `.call` is the dynamic escape hatch and\n * EVERY other property is treated as an operation id, dispatched verbatim to\n * `callConnector(id, operation, args, opts)`. So `connection('stub-mail').sendMail(a)`\n * is `callConnector('stub-mail', 'sendMail', a)` — the typed operations and the\n * dynamic `.call` hit the same broker path, with zero per-connector runtime code.\n */\nconst connectorHandle = (id: string): ConnectorHandle =>\n new Proxy(Object.create(null) as ConnectorHandle, {\n get(_target, prop) {\n // Never look thenable (an accidental `await connection(id)` must not hang).\n if (typeof prop !== 'string' || prop === 'then') return undefined;\n if (prop === 'call') {\n return (\n operation: string,\n args?: Readonly<Record<string, unknown>>,\n opts?: CallConnectorOptions,\n ): Promise<unknown> => callConnector(id, operation, args, opts);\n }\n return (args?: Readonly<Record<string, unknown>>, opts?: CallConnectorOptions): Promise<unknown> =>\n callConnector(id, prop, args, opts);\n },\n });\n\n/**\n * Call Stackbone Connect connectors. Select by verbatim id, then call an operation —\n * typed when `.stackbone/connect.d.ts` is generated, dynamic via `.call` always:\n *\n * ```ts\n * await connection('stub-mail').sendMail({ to, subject, body }); // typed\n * await connection('slack').call('chat.postMessage', { ... }); // dynamic escape hatch\n * ```\n *\n * Mirrors `eveAgent('agent-name')`: one id-keyed selection style across the SDK.\n */\nexport const connection: ConnectionAccessor = ((id: string) =>\n connectorHandle(id)) as ConnectionAccessor;\n"]}
|
package/eve.cjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var openaiCompatible = require('@ai-sdk/openai-compatible');
|
|
4
|
+
var eve = require('eve');
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = 2e5;
|
|
9
|
+
var STACKBONE_EXTERNAL_DEPENDENCIES = [
|
|
10
|
+
"@stackbone/sdk",
|
|
11
|
+
"eve*"
|
|
12
|
+
];
|
|
13
|
+
var openrouter = openaiCompatible.createOpenAICompatible({
|
|
14
|
+
name: "openrouter",
|
|
15
|
+
baseURL: process.env["OPENROUTER_BASE_URL"] ?? "https://openrouter.ai/api/v1",
|
|
16
|
+
apiKey: process.env["OPENROUTER_API_KEY"]
|
|
17
|
+
});
|
|
18
|
+
function openrouterModel(modelId) {
|
|
19
|
+
return openrouter(modelId);
|
|
20
|
+
}
|
|
21
|
+
__name(openrouterModel, "openrouterModel");
|
|
22
|
+
function defineStackboneAgent(config) {
|
|
23
|
+
const { model, modelContextWindowTokens, build, ...rest } = config;
|
|
24
|
+
return eve.defineAgent({
|
|
25
|
+
...rest,
|
|
26
|
+
model: typeof model === "string" ? openrouterModel(model) : model,
|
|
27
|
+
modelContextWindowTokens: modelContextWindowTokens ?? DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
28
|
+
build: mergeExternalDependencies(build)
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
__name(defineStackboneAgent, "defineStackboneAgent");
|
|
32
|
+
function mergeExternalDependencies(build) {
|
|
33
|
+
const extra = build?.externalDependencies ?? [];
|
|
34
|
+
const externalDependencies = [
|
|
35
|
+
.../* @__PURE__ */ new Set([
|
|
36
|
+
...STACKBONE_EXTERNAL_DEPENDENCIES,
|
|
37
|
+
...extra
|
|
38
|
+
])
|
|
39
|
+
];
|
|
40
|
+
return {
|
|
41
|
+
...build,
|
|
42
|
+
externalDependencies
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
__name(mergeExternalDependencies, "mergeExternalDependencies");
|
|
46
|
+
|
|
47
|
+
exports.DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS;
|
|
48
|
+
exports.STACKBONE_EXTERNAL_DEPENDENCIES = STACKBONE_EXTERNAL_DEPENDENCIES;
|
|
49
|
+
exports.defineStackboneAgent = defineStackboneAgent;
|
|
50
|
+
exports.openrouterModel = openrouterModel;
|
|
51
|
+
//# sourceMappingURL=eve.cjs.map
|
|
52
|
+
//# sourceMappingURL=eve.cjs.map
|
package/eve.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../libs/sdk/src/eve.ts"],"names":["DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS","STACKBONE_EXTERNAL_DEPENDENCIES","openrouter","createOpenAICompatible","name","baseURL","process","env","apiKey","openrouterModel","modelId","defineStackboneAgent","config","model","modelContextWindowTokens","build","rest","defineAgent","mergeExternalDependencies","extra","externalDependencies","Set"],"mappings":";;;;;;;AA2CO,IAAMA,mCAAAA,GAAsC;AAS5C,IAAMC,+BAAAA,GAAqD;AAAC,EAAA,gBAAA;AAAkB,EAAA;;AAMrF,IAAMC,aAAaC,uCAAAA,CAAuB;EACxCC,IAAAA,EAAM,YAAA;EACNC,OAAAA,EAASC,OAAAA,CAAQC,GAAAA,CAAI,qBAAA,CAAA,IAA0B,8BAAA;EAC/CC,MAAAA,EAAQF,OAAAA,CAAQC,IAAI,oBAAA;AACtB,CAAA,CAAA;AAOO,SAASE,gBAAgBC,OAAAA,EAAe;AAC7C,EAAA,OAAOR,WAAWQ,OAAAA,CAAAA;AACpB;AAFgBD,MAAAA,CAAAA,eAAAA,EAAAA,iBAAAA,CAAAA;AAoBT,SAASE,qBAAqBC,MAAAA,EAA4B;AAC/D,EAAA,MAAM,EAAEC,KAAAA,EAAOC,wBAAAA,EAA0BC,KAAAA,EAAO,GAAGC,MAAAA,GAASJ,MAAAA;AAC5D,EAAA,OAAOK,eAAAA,CAAY;IACjB,GAAGD,IAAAA;AACHH,IAAAA,KAAAA,EAAO,OAAOA,KAAAA,KAAU,QAAA,GAAWJ,eAAAA,CAAgBI,KAAAA,CAAAA,GAASA,KAAAA;AAC5DC,IAAAA,wBAAAA,EAA0BA,wBAAAA,IAA4Bd,mCAAAA;AACtDe,IAAAA,KAAAA,EAAOG,0BAA0BH,KAAAA;GACnC,CAAA;AACF;AARgBJ,MAAAA,CAAAA,oBAAAA,EAAAA,sBAAAA,CAAAA;AAchB,SAASO,0BAA0BH,KAAAA,EAAuC;AACxE,EAAA,MAAMI,KAAAA,GAAQJ,KAAAA,EAAOK,oBAAAA,IAAwB,EAAA;AAC7C,EAAA,MAAMA,oBAAAA,GAAuB;AAAI,IAAA,mBAAA,IAAIC,GAAAA,CAAI;AAAIpB,MAAAA,GAAAA,+BAAAA;AAAoCkB,MAAAA,GAAAA;AAAM,KAAA;;AACvF,EAAA,OAAO;IAAE,GAAGJ,KAAAA;AAAOK,IAAAA;AAAqB,GAAA;AAC1C;AAJSF,MAAAA,CAAAA,yBAAAA,EAAAA,2BAAAA,CAAAA","file":"eve.cjs","sourcesContent":["// `@stackbone/sdk/eve` — the eve-agent authoring subpath entrypoint.\n//\n// Wraps eve's `defineAgent` with the Stackbone defaults every published eve\n// agent otherwise hand-copies, so an `agent.ts` collapses to:\n//\n// import { defineStackboneAgent } from '@stackbone/sdk/eve';\n//\n// export default defineStackboneAgent({ model: 'anthropic/claude-haiku-4.5' });\n//\n// The helper injects three things:\n//\n// 1. The OpenRouter model bridge. eve routes a BARE model string through the\n// Vercel AI Gateway — it does NOT speak OpenRouter. Stackbone injects the\n// org's managed OpenRouter sub-key as `OPENROUTER_API_KEY`, so we pass a\n// provider INSTANCE (eve routes those directly). Pass `model` as a string id\n// and the helper wraps it with the OpenRouter provider; pass a built\n// `LanguageModel` and it is used verbatim (escape hatch for another provider).\n// 2. `modelContextWindowTokens`. A custom provider carries no AI-Gateway\n// context-window metadata, so eve cannot size the compaction threshold.\n// Defaults to 200_000 (Claude's window); override per agent.\n// 3. `build.externalDependencies`. `@stackbone/sdk` + `eve*` stay external so\n// the published build traces them fully — one SDK AsyncLocalStorage (per-run\n// logs keep their run id) and a complete eve `#`-subpath trace (no\n// ERR_MODULE_NOT_FOUND at boot). `stackbone publish` ENFORCES both; the\n// helper guarantees they are always present and merges in any extra entries.\n//\n// Like `@stackbone/sdk/workflow`, this subpath statically imports the `eve` and\n// `@ai-sdk/openai-compatible` peer dependencies, so it is kept OFF the main\n// `@stackbone/sdk` barrel: a classic tool-only agent imports `@stackbone/sdk`\n// without eager-loading peers it never installed. The build keeps both external\n// (their types come from the sibling `eve-peer.d.ts` /\n// `ai-sdk-openai-compatible-peer.d.ts` shims).\n\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport { defineAgent } from 'eve';\nimport type { AgentBuildDefinition, AgentDefinition, AgentModelDefinition } from 'eve';\n\n/**\n * Default context window (tokens) for the OpenRouter Claude models Stackbone\n * provisions. A custom provider carries no AI-Gateway metadata, so eve needs\n * this to size the conversation-compaction threshold. Override per agent via\n * `modelContextWindowTokens` when targeting a model with a different window.\n */\nexport const DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = 200_000;\n\n/**\n * External dependencies every published eve agent keeps out of its bundle.\n * `@stackbone/sdk` stays external so the agent shares ONE invocation-context\n * AsyncLocalStorage (per-run logs keep their run id); `eve*` forces a full trace\n * of eve's `#`-subpath imports (no ERR_MODULE_NOT_FOUND at boot). `stackbone\n * publish` enforces both, so the helper always injects them.\n */\nexport const STACKBONE_EXTERNAL_DEPENDENCIES: readonly string[] = ['@stackbone/sdk', 'eve*'];\n\n// The OpenRouter provider bridge, constructed once per module load. Stackbone\n// injects the org's managed sub-key as `OPENROUTER_API_KEY`; OpenRouter exposes\n// an OpenAI-compatible API, so the AI SDK's openai-compatible provider talks to\n// it directly.\nconst openrouter = createOpenAICompatible({\n name: 'openrouter',\n baseURL: process.env['OPENROUTER_BASE_URL'] ?? 'https://openrouter.ai/api/v1',\n apiKey: process.env['OPENROUTER_API_KEY'],\n});\n\n/**\n * Resolve an OpenRouter model INSTANCE by id — the same bridge\n * `defineStackboneAgent` applies to a string `model`, exposed for workflow steps\n * that call an LLM directly (e.g. `generateText({ model: openrouterModel(id) })`).\n */\nexport function openrouterModel(modelId: string): AgentModelDefinition {\n return openrouter(modelId);\n}\n\n/**\n * Config accepted by {@link defineStackboneAgent}: every eve `AgentDefinition`\n * field, except `model` also accepts a bare OpenRouter model id (string) the\n * helper wraps with the provider bridge. `modelContextWindowTokens` and `build`\n * are optional — the helper fills sensible defaults.\n */\nexport type StackboneAgentConfig = Omit<AgentDefinition, 'model'> & {\n readonly model: string | AgentModelDefinition;\n};\n\n/**\n * Define an eve agent with Stackbone's defaults pre-applied: the OpenRouter\n * model bridge, the default context window, and the required external\n * dependencies. Returns the same object shape eve's `defineAgent` produces, so\n * the agent file `export default`s it unchanged.\n */\nexport function defineStackboneAgent(config: StackboneAgentConfig): AgentDefinition {\n const { model, modelContextWindowTokens, build, ...rest } = config;\n return defineAgent({\n ...rest,\n model: typeof model === 'string' ? openrouterModel(model) : model,\n modelContextWindowTokens: modelContextWindowTokens ?? DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS,\n build: mergeExternalDependencies(build),\n });\n}\n\n/**\n * Merge the always-required Stackbone external dependencies with any extra ones\n * a creator declares, de-duplicated and order-stable (Stackbone's first).\n */\nfunction mergeExternalDependencies(build: AgentBuildDefinition | undefined): AgentBuildDefinition {\n const extra = build?.externalDependencies ?? [];\n const externalDependencies = [...new Set([...STACKBONE_EXTERNAL_DEPENDENCIES, ...extra])];\n return { ...build, externalDependencies };\n}\n"]}
|
package/eve.d.cts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AgentDefinition, AgentModelDefinition } from 'eve';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default context window (tokens) for the OpenRouter Claude models Stackbone
|
|
5
|
+
* provisions. A custom provider carries no AI-Gateway metadata, so eve needs
|
|
6
|
+
* this to size the conversation-compaction threshold. Override per agent via
|
|
7
|
+
* `modelContextWindowTokens` when targeting a model with a different window.
|
|
8
|
+
*/
|
|
9
|
+
declare const DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = 200000;
|
|
10
|
+
/**
|
|
11
|
+
* External dependencies every published eve agent keeps out of its bundle.
|
|
12
|
+
* `@stackbone/sdk` stays external so the agent shares ONE invocation-context
|
|
13
|
+
* AsyncLocalStorage (per-run logs keep their run id); `eve*` forces a full trace
|
|
14
|
+
* of eve's `#`-subpath imports (no ERR_MODULE_NOT_FOUND at boot). `stackbone
|
|
15
|
+
* publish` enforces both, so the helper always injects them.
|
|
16
|
+
*/
|
|
17
|
+
declare const STACKBONE_EXTERNAL_DEPENDENCIES: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* Resolve an OpenRouter model INSTANCE by id — the same bridge
|
|
20
|
+
* `defineStackboneAgent` applies to a string `model`, exposed for workflow steps
|
|
21
|
+
* that call an LLM directly (e.g. `generateText({ model: openrouterModel(id) })`).
|
|
22
|
+
*/
|
|
23
|
+
declare function openrouterModel(modelId: string): AgentModelDefinition;
|
|
24
|
+
/**
|
|
25
|
+
* Config accepted by {@link defineStackboneAgent}: every eve `AgentDefinition`
|
|
26
|
+
* field, except `model` also accepts a bare OpenRouter model id (string) the
|
|
27
|
+
* helper wraps with the provider bridge. `modelContextWindowTokens` and `build`
|
|
28
|
+
* are optional — the helper fills sensible defaults.
|
|
29
|
+
*/
|
|
30
|
+
type StackboneAgentConfig = Omit<AgentDefinition, 'model'> & {
|
|
31
|
+
readonly model: string | AgentModelDefinition;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Define an eve agent with Stackbone's defaults pre-applied: the OpenRouter
|
|
35
|
+
* model bridge, the default context window, and the required external
|
|
36
|
+
* dependencies. Returns the same object shape eve's `defineAgent` produces, so
|
|
37
|
+
* the agent file `export default`s it unchanged.
|
|
38
|
+
*/
|
|
39
|
+
declare function defineStackboneAgent(config: StackboneAgentConfig): AgentDefinition;
|
|
40
|
+
|
|
41
|
+
export { DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS, STACKBONE_EXTERNAL_DEPENDENCIES, type StackboneAgentConfig, defineStackboneAgent, openrouterModel };
|
package/eve.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AgentDefinition, AgentModelDefinition } from 'eve';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default context window (tokens) for the OpenRouter Claude models Stackbone
|
|
5
|
+
* provisions. A custom provider carries no AI-Gateway metadata, so eve needs
|
|
6
|
+
* this to size the conversation-compaction threshold. Override per agent via
|
|
7
|
+
* `modelContextWindowTokens` when targeting a model with a different window.
|
|
8
|
+
*/
|
|
9
|
+
declare const DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = 200000;
|
|
10
|
+
/**
|
|
11
|
+
* External dependencies every published eve agent keeps out of its bundle.
|
|
12
|
+
* `@stackbone/sdk` stays external so the agent shares ONE invocation-context
|
|
13
|
+
* AsyncLocalStorage (per-run logs keep their run id); `eve*` forces a full trace
|
|
14
|
+
* of eve's `#`-subpath imports (no ERR_MODULE_NOT_FOUND at boot). `stackbone
|
|
15
|
+
* publish` enforces both, so the helper always injects them.
|
|
16
|
+
*/
|
|
17
|
+
declare const STACKBONE_EXTERNAL_DEPENDENCIES: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* Resolve an OpenRouter model INSTANCE by id — the same bridge
|
|
20
|
+
* `defineStackboneAgent` applies to a string `model`, exposed for workflow steps
|
|
21
|
+
* that call an LLM directly (e.g. `generateText({ model: openrouterModel(id) })`).
|
|
22
|
+
*/
|
|
23
|
+
declare function openrouterModel(modelId: string): AgentModelDefinition;
|
|
24
|
+
/**
|
|
25
|
+
* Config accepted by {@link defineStackboneAgent}: every eve `AgentDefinition`
|
|
26
|
+
* field, except `model` also accepts a bare OpenRouter model id (string) the
|
|
27
|
+
* helper wraps with the provider bridge. `modelContextWindowTokens` and `build`
|
|
28
|
+
* are optional — the helper fills sensible defaults.
|
|
29
|
+
*/
|
|
30
|
+
type StackboneAgentConfig = Omit<AgentDefinition, 'model'> & {
|
|
31
|
+
readonly model: string | AgentModelDefinition;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Define an eve agent with Stackbone's defaults pre-applied: the OpenRouter
|
|
35
|
+
* model bridge, the default context window, and the required external
|
|
36
|
+
* dependencies. Returns the same object shape eve's `defineAgent` produces, so
|
|
37
|
+
* the agent file `export default`s it unchanged.
|
|
38
|
+
*/
|
|
39
|
+
declare function defineStackboneAgent(config: StackboneAgentConfig): AgentDefinition;
|
|
40
|
+
|
|
41
|
+
export { DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS, STACKBONE_EXTERNAL_DEPENDENCIES, type StackboneAgentConfig, defineStackboneAgent, openrouterModel };
|
package/eve.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
2
|
+
import { defineAgent } from 'eve';
|
|
3
|
+
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = 2e5;
|
|
7
|
+
var STACKBONE_EXTERNAL_DEPENDENCIES = [
|
|
8
|
+
"@stackbone/sdk",
|
|
9
|
+
"eve*"
|
|
10
|
+
];
|
|
11
|
+
var openrouter = createOpenAICompatible({
|
|
12
|
+
name: "openrouter",
|
|
13
|
+
baseURL: process.env["OPENROUTER_BASE_URL"] ?? "https://openrouter.ai/api/v1",
|
|
14
|
+
apiKey: process.env["OPENROUTER_API_KEY"]
|
|
15
|
+
});
|
|
16
|
+
function openrouterModel(modelId) {
|
|
17
|
+
return openrouter(modelId);
|
|
18
|
+
}
|
|
19
|
+
__name(openrouterModel, "openrouterModel");
|
|
20
|
+
function defineStackboneAgent(config) {
|
|
21
|
+
const { model, modelContextWindowTokens, build, ...rest } = config;
|
|
22
|
+
return defineAgent({
|
|
23
|
+
...rest,
|
|
24
|
+
model: typeof model === "string" ? openrouterModel(model) : model,
|
|
25
|
+
modelContextWindowTokens: modelContextWindowTokens ?? DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
26
|
+
build: mergeExternalDependencies(build)
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
__name(defineStackboneAgent, "defineStackboneAgent");
|
|
30
|
+
function mergeExternalDependencies(build) {
|
|
31
|
+
const extra = build?.externalDependencies ?? [];
|
|
32
|
+
const externalDependencies = [
|
|
33
|
+
.../* @__PURE__ */ new Set([
|
|
34
|
+
...STACKBONE_EXTERNAL_DEPENDENCIES,
|
|
35
|
+
...extra
|
|
36
|
+
])
|
|
37
|
+
];
|
|
38
|
+
return {
|
|
39
|
+
...build,
|
|
40
|
+
externalDependencies
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
__name(mergeExternalDependencies, "mergeExternalDependencies");
|
|
44
|
+
|
|
45
|
+
export { DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS, STACKBONE_EXTERNAL_DEPENDENCIES, defineStackboneAgent, openrouterModel };
|
|
46
|
+
//# sourceMappingURL=eve.js.map
|
|
47
|
+
//# sourceMappingURL=eve.js.map
|
package/eve.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../libs/sdk/src/eve.ts"],"names":["DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS","STACKBONE_EXTERNAL_DEPENDENCIES","openrouter","createOpenAICompatible","name","baseURL","process","env","apiKey","openrouterModel","modelId","defineStackboneAgent","config","model","modelContextWindowTokens","build","rest","defineAgent","mergeExternalDependencies","extra","externalDependencies","Set"],"mappings":";;;;;AA2CO,IAAMA,mCAAAA,GAAsC;AAS5C,IAAMC,+BAAAA,GAAqD;AAAC,EAAA,gBAAA;AAAkB,EAAA;;AAMrF,IAAMC,aAAaC,sBAAAA,CAAuB;EACxCC,IAAAA,EAAM,YAAA;EACNC,OAAAA,EAASC,OAAAA,CAAQC,GAAAA,CAAI,qBAAA,CAAA,IAA0B,8BAAA;EAC/CC,MAAAA,EAAQF,OAAAA,CAAQC,IAAI,oBAAA;AACtB,CAAA,CAAA;AAOO,SAASE,gBAAgBC,OAAAA,EAAe;AAC7C,EAAA,OAAOR,WAAWQ,OAAAA,CAAAA;AACpB;AAFgBD,MAAAA,CAAAA,eAAAA,EAAAA,iBAAAA,CAAAA;AAoBT,SAASE,qBAAqBC,MAAAA,EAA4B;AAC/D,EAAA,MAAM,EAAEC,KAAAA,EAAOC,wBAAAA,EAA0BC,KAAAA,EAAO,GAAGC,MAAAA,GAASJ,MAAAA;AAC5D,EAAA,OAAOK,WAAAA,CAAY;IACjB,GAAGD,IAAAA;AACHH,IAAAA,KAAAA,EAAO,OAAOA,KAAAA,KAAU,QAAA,GAAWJ,eAAAA,CAAgBI,KAAAA,CAAAA,GAASA,KAAAA;AAC5DC,IAAAA,wBAAAA,EAA0BA,wBAAAA,IAA4Bd,mCAAAA;AACtDe,IAAAA,KAAAA,EAAOG,0BAA0BH,KAAAA;GACnC,CAAA;AACF;AARgBJ,MAAAA,CAAAA,oBAAAA,EAAAA,sBAAAA,CAAAA;AAchB,SAASO,0BAA0BH,KAAAA,EAAuC;AACxE,EAAA,MAAMI,KAAAA,GAAQJ,KAAAA,EAAOK,oBAAAA,IAAwB,EAAA;AAC7C,EAAA,MAAMA,oBAAAA,GAAuB;AAAI,IAAA,mBAAA,IAAIC,GAAAA,CAAI;AAAIpB,MAAAA,GAAAA,+BAAAA;AAAoCkB,MAAAA,GAAAA;AAAM,KAAA;;AACvF,EAAA,OAAO;IAAE,GAAGJ,KAAAA;AAAOK,IAAAA;AAAqB,GAAA;AAC1C;AAJSF,MAAAA,CAAAA,yBAAAA,EAAAA,2BAAAA,CAAAA","file":"eve.js","sourcesContent":["// `@stackbone/sdk/eve` — the eve-agent authoring subpath entrypoint.\n//\n// Wraps eve's `defineAgent` with the Stackbone defaults every published eve\n// agent otherwise hand-copies, so an `agent.ts` collapses to:\n//\n// import { defineStackboneAgent } from '@stackbone/sdk/eve';\n//\n// export default defineStackboneAgent({ model: 'anthropic/claude-haiku-4.5' });\n//\n// The helper injects three things:\n//\n// 1. The OpenRouter model bridge. eve routes a BARE model string through the\n// Vercel AI Gateway — it does NOT speak OpenRouter. Stackbone injects the\n// org's managed OpenRouter sub-key as `OPENROUTER_API_KEY`, so we pass a\n// provider INSTANCE (eve routes those directly). Pass `model` as a string id\n// and the helper wraps it with the OpenRouter provider; pass a built\n// `LanguageModel` and it is used verbatim (escape hatch for another provider).\n// 2. `modelContextWindowTokens`. A custom provider carries no AI-Gateway\n// context-window metadata, so eve cannot size the compaction threshold.\n// Defaults to 200_000 (Claude's window); override per agent.\n// 3. `build.externalDependencies`. `@stackbone/sdk` + `eve*` stay external so\n// the published build traces them fully — one SDK AsyncLocalStorage (per-run\n// logs keep their run id) and a complete eve `#`-subpath trace (no\n// ERR_MODULE_NOT_FOUND at boot). `stackbone publish` ENFORCES both; the\n// helper guarantees they are always present and merges in any extra entries.\n//\n// Like `@stackbone/sdk/workflow`, this subpath statically imports the `eve` and\n// `@ai-sdk/openai-compatible` peer dependencies, so it is kept OFF the main\n// `@stackbone/sdk` barrel: a classic tool-only agent imports `@stackbone/sdk`\n// without eager-loading peers it never installed. The build keeps both external\n// (their types come from the sibling `eve-peer.d.ts` /\n// `ai-sdk-openai-compatible-peer.d.ts` shims).\n\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport { defineAgent } from 'eve';\nimport type { AgentBuildDefinition, AgentDefinition, AgentModelDefinition } from 'eve';\n\n/**\n * Default context window (tokens) for the OpenRouter Claude models Stackbone\n * provisions. A custom provider carries no AI-Gateway metadata, so eve needs\n * this to size the conversation-compaction threshold. Override per agent via\n * `modelContextWindowTokens` when targeting a model with a different window.\n */\nexport const DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS = 200_000;\n\n/**\n * External dependencies every published eve agent keeps out of its bundle.\n * `@stackbone/sdk` stays external so the agent shares ONE invocation-context\n * AsyncLocalStorage (per-run logs keep their run id); `eve*` forces a full trace\n * of eve's `#`-subpath imports (no ERR_MODULE_NOT_FOUND at boot). `stackbone\n * publish` enforces both, so the helper always injects them.\n */\nexport const STACKBONE_EXTERNAL_DEPENDENCIES: readonly string[] = ['@stackbone/sdk', 'eve*'];\n\n// The OpenRouter provider bridge, constructed once per module load. Stackbone\n// injects the org's managed sub-key as `OPENROUTER_API_KEY`; OpenRouter exposes\n// an OpenAI-compatible API, so the AI SDK's openai-compatible provider talks to\n// it directly.\nconst openrouter = createOpenAICompatible({\n name: 'openrouter',\n baseURL: process.env['OPENROUTER_BASE_URL'] ?? 'https://openrouter.ai/api/v1',\n apiKey: process.env['OPENROUTER_API_KEY'],\n});\n\n/**\n * Resolve an OpenRouter model INSTANCE by id — the same bridge\n * `defineStackboneAgent` applies to a string `model`, exposed for workflow steps\n * that call an LLM directly (e.g. `generateText({ model: openrouterModel(id) })`).\n */\nexport function openrouterModel(modelId: string): AgentModelDefinition {\n return openrouter(modelId);\n}\n\n/**\n * Config accepted by {@link defineStackboneAgent}: every eve `AgentDefinition`\n * field, except `model` also accepts a bare OpenRouter model id (string) the\n * helper wraps with the provider bridge. `modelContextWindowTokens` and `build`\n * are optional — the helper fills sensible defaults.\n */\nexport type StackboneAgentConfig = Omit<AgentDefinition, 'model'> & {\n readonly model: string | AgentModelDefinition;\n};\n\n/**\n * Define an eve agent with Stackbone's defaults pre-applied: the OpenRouter\n * model bridge, the default context window, and the required external\n * dependencies. Returns the same object shape eve's `defineAgent` produces, so\n * the agent file `export default`s it unchanged.\n */\nexport function defineStackboneAgent(config: StackboneAgentConfig): AgentDefinition {\n const { model, modelContextWindowTokens, build, ...rest } = config;\n return defineAgent({\n ...rest,\n model: typeof model === 'string' ? openrouterModel(model) : model,\n modelContextWindowTokens: modelContextWindowTokens ?? DEFAULT_MODEL_CONTEXT_WINDOW_TOKENS,\n build: mergeExternalDependencies(build),\n });\n}\n\n/**\n * Merge the always-required Stackbone external dependencies with any extra ones\n * a creator declares, de-duplicated and order-stable (Stackbone's first).\n */\nfunction mergeExternalDependencies(build: AgentBuildDefinition | undefined): AgentBuildDefinition {\n const extra = build?.externalDependencies ?? [];\n const externalDependencies = [...new Set([...STACKBONE_EXTERNAL_DEPENDENCIES, ...extra])];\n return { ...build, externalDependencies };\n}\n"]}
|