@personaai/runtime 0.22.0 → 0.24.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 +619 -453
- package/dist/index.cjs +955 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -3
- package/dist/index.d.ts +150 -3
- package/dist/index.js +960 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
CAPABILITY_SCOPES: () => CAPABILITY_SCOPES,
|
|
23
24
|
RUNTIME_VERSION: () => RUNTIME_VERSION,
|
|
24
25
|
RuntimeHttpError: () => RuntimeHttpError,
|
|
25
26
|
createRuntime: () => createRuntime
|
|
@@ -74,6 +75,8 @@ function createClientForRequest(options, userId, logger) {
|
|
|
74
75
|
return new import_sdk.PersonaClient({
|
|
75
76
|
baseUrl: options.baseUrl,
|
|
76
77
|
credential: options.credential,
|
|
78
|
+
// No end user (`null`) = a Project-scoped client: `/health` and the
|
|
79
|
+
// Project-wide admin capabilities.
|
|
77
80
|
externalUserId: userId ?? void 0,
|
|
78
81
|
fetch: options.fetch,
|
|
79
82
|
logger: clientLogger
|
|
@@ -98,6 +101,36 @@ function mapErrorToRuntimeError(err, mode) {
|
|
|
98
101
|
if (err instanceof RuntimeHttpError) {
|
|
99
102
|
return { status: err.status, code: err.code, message: err.message };
|
|
100
103
|
}
|
|
104
|
+
if (err instanceof import_sdk2.ForbiddenScopeError) {
|
|
105
|
+
return {
|
|
106
|
+
status: 500,
|
|
107
|
+
code: "RUNTIME_MISCONFIGURED",
|
|
108
|
+
message: dev ? `The runtime's Persona API key lacks the "${err.requiredScope ?? "unknown"}" scope: ${err.message}` : "The server's Persona API key can't do this.",
|
|
109
|
+
...dev ? { detail: { requiredScope: err.requiredScope, acceptedScopes: err.acceptedScopes } } : {}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (err instanceof import_sdk2.BudgetExceededError) {
|
|
113
|
+
return {
|
|
114
|
+
status: 402,
|
|
115
|
+
code: err.code,
|
|
116
|
+
message: err.message,
|
|
117
|
+
...err.scope ? { details: { scope: err.scope } } : {},
|
|
118
|
+
...dev ? { detail: err.response } : {}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (err instanceof import_sdk2.EvalGateError) {
|
|
122
|
+
return {
|
|
123
|
+
status: err.statusCode,
|
|
124
|
+
code: err.code,
|
|
125
|
+
message: err.message,
|
|
126
|
+
details: {
|
|
127
|
+
...err.datasetId ? { datasetId: err.datasetId } : {},
|
|
128
|
+
...err.minPassRate !== void 0 ? { minPassRate: err.minPassRate } : {},
|
|
129
|
+
...err.passRate !== void 0 ? { passRate: err.passRate } : {},
|
|
130
|
+
...err.runId ? { runId: err.runId } : {}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
101
134
|
if (err instanceof import_sdk2.PersonaApiError) {
|
|
102
135
|
return {
|
|
103
136
|
status: err.statusCode,
|
|
@@ -129,6 +162,7 @@ function errorToResponse(err, mode) {
|
|
|
129
162
|
error: {
|
|
130
163
|
code: mapped.code,
|
|
131
164
|
message: mapped.message,
|
|
165
|
+
...mapped.details !== void 0 ? { details: mapped.details } : {},
|
|
132
166
|
...mapped.detail !== void 0 ? { detail: mapped.detail } : {}
|
|
133
167
|
}
|
|
134
168
|
})
|
|
@@ -159,7 +193,7 @@ function evictStaleRuns(runs, now, graceMs = DEFAULT_RUN_GRACE_MS, maxTracked =
|
|
|
159
193
|
}
|
|
160
194
|
|
|
161
195
|
// src/version.ts
|
|
162
|
-
var RUNTIME_VERSION = "0.
|
|
196
|
+
var RUNTIME_VERSION = "0.24.0";
|
|
163
197
|
|
|
164
198
|
// src/routes/health.ts
|
|
165
199
|
var alwaysOnCapabilities = {
|
|
@@ -169,15 +203,61 @@ var alwaysOnCapabilities = {
|
|
|
169
203
|
files: true,
|
|
170
204
|
memory: true,
|
|
171
205
|
mcpOAuth: true,
|
|
172
|
-
workflows: true
|
|
173
|
-
|
|
206
|
+
workflows: true,
|
|
207
|
+
voice: true,
|
|
208
|
+
feedback: true
|
|
209
|
+
};
|
|
210
|
+
var CAPABILITY_SCOPES = {
|
|
211
|
+
agentsWrite: ["*"],
|
|
212
|
+
mcps: ["*"],
|
|
213
|
+
providers: ["*"],
|
|
214
|
+
skills: ["*"],
|
|
215
|
+
knowledge: ["*"],
|
|
216
|
+
stores: ["*"],
|
|
217
|
+
rcpSources: ["*"],
|
|
218
|
+
auditLogs: ["*"],
|
|
219
|
+
architect: ["*"],
|
|
220
|
+
workflowsWrite: ["*"],
|
|
221
|
+
secrets: ["*"],
|
|
222
|
+
restToolSources: ["*"],
|
|
223
|
+
messages: ["agent:invoke"],
|
|
224
|
+
deploy: ["deploy:write"],
|
|
225
|
+
traces: ["traces:read", "agent:invoke"],
|
|
226
|
+
evals: ["eval:run"],
|
|
227
|
+
observe: ["traces:read"]
|
|
228
|
+
};
|
|
229
|
+
function scopeWarnings(scopes, capabilities) {
|
|
230
|
+
if (!scopes || scopes.includes("*")) return [];
|
|
231
|
+
const has = (s) => scopes.includes(s);
|
|
232
|
+
const warnings = [];
|
|
233
|
+
if (!has("agent:invoke")) {
|
|
234
|
+
warnings.push(
|
|
235
|
+
'The API key lacks "agent:invoke": chat, threads, files, memory and voice will fail.'
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
for (const [cap, needed] of Object.entries(CAPABILITY_SCOPES)) {
|
|
239
|
+
if (!capabilities[cap]) continue;
|
|
240
|
+
const missing = needed.filter((s) => !has(s));
|
|
241
|
+
if (missing.length) {
|
|
242
|
+
warnings.push(
|
|
243
|
+
`capabilities.${cap} is on but the API key lacks ${missing.map((s) => `"${s}"`).join(" and ")}.`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return warnings;
|
|
248
|
+
}
|
|
174
249
|
var healthRoute = async (_request, ctx) => {
|
|
250
|
+
let principal;
|
|
175
251
|
try {
|
|
176
|
-
await ctx.client.whoami();
|
|
252
|
+
principal = await ctx.client.whoami();
|
|
177
253
|
} catch (err) {
|
|
178
254
|
const failed = errorToResponse(err, ctx.mode);
|
|
179
255
|
return { ...failed, status: 503 };
|
|
180
256
|
}
|
|
257
|
+
const raw = principal?.scopes;
|
|
258
|
+
const scopes = Array.isArray(raw) ? raw.filter((s) => typeof s === "string") : void 0;
|
|
259
|
+
const warnings = scopeWarnings(scopes, ctx.capabilities);
|
|
260
|
+
for (const w of warnings) ctx.logger.warn(w);
|
|
181
261
|
return {
|
|
182
262
|
kind: "buffered",
|
|
183
263
|
status: 200,
|
|
@@ -185,7 +265,9 @@ var healthRoute = async (_request, ctx) => {
|
|
|
185
265
|
body: JSON.stringify({
|
|
186
266
|
status: "ok",
|
|
187
267
|
version: RUNTIME_VERSION,
|
|
188
|
-
capabilities: { ...alwaysOnCapabilities, ...ctx.capabilities }
|
|
268
|
+
capabilities: { ...alwaysOnCapabilities, ...ctx.capabilities },
|
|
269
|
+
...scopes ? { scopes } : {},
|
|
270
|
+
...warnings.length ? { warnings } : {}
|
|
189
271
|
})
|
|
190
272
|
};
|
|
191
273
|
};
|
|
@@ -276,6 +358,55 @@ function validateTurnContext(value) {
|
|
|
276
358
|
}
|
|
277
359
|
return context;
|
|
278
360
|
}
|
|
361
|
+
var ENVIRONMENTS = ["development", "production"];
|
|
362
|
+
function clientVersionFields(body, envField = "environment") {
|
|
363
|
+
const out = {};
|
|
364
|
+
const env = body[envField];
|
|
365
|
+
if (env !== void 0 && env !== null) {
|
|
366
|
+
if (!ENVIRONMENTS.includes(env)) {
|
|
367
|
+
throw new RuntimeHttpError(
|
|
368
|
+
400,
|
|
369
|
+
"INVALID_REQUEST",
|
|
370
|
+
`"${envField}" must be "development" or "production".`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
out.environment = env;
|
|
374
|
+
}
|
|
375
|
+
if (body.agentVersion !== void 0 && body.agentVersion !== null) {
|
|
376
|
+
const v = body.agentVersion;
|
|
377
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 1) {
|
|
378
|
+
throw new RuntimeHttpError(
|
|
379
|
+
400,
|
|
380
|
+
"INVALID_REQUEST",
|
|
381
|
+
'"agentVersion" must be a positive integer.'
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
out.agentVersion = v;
|
|
385
|
+
}
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
async function resolveRunVersion(body, ctx, selection, envField = "environment") {
|
|
389
|
+
const { selectVersion, defaultEnvironment, allowClientVersionSelection } = ctx.versioning;
|
|
390
|
+
if (allowClientVersionSelection) {
|
|
391
|
+
const fromClient = clientVersionFields(body, envField);
|
|
392
|
+
if (fromClient.environment || fromClient.agentVersion) return fromClient;
|
|
393
|
+
}
|
|
394
|
+
if (selectVersion) {
|
|
395
|
+
const picked = await selectVersion(selection);
|
|
396
|
+
if (picked && (picked.environment || picked.agentVersion)) {
|
|
397
|
+
return {
|
|
398
|
+
...picked.environment ? { environment: picked.environment } : {},
|
|
399
|
+
...picked.agentVersion ? { agentVersion: picked.agentVersion } : {}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return defaultEnvironment ? { environment: defaultEnvironment } : {};
|
|
404
|
+
}
|
|
405
|
+
function incomingTraceparent(headers, enabled) {
|
|
406
|
+
if (!enabled) return void 0;
|
|
407
|
+
const value = headers.traceparent ?? headers.Traceparent;
|
|
408
|
+
return value && /^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i.test(value.trim()) ? value.trim() : void 0;
|
|
409
|
+
}
|
|
279
410
|
|
|
280
411
|
// src/routes/restToolsManifest.ts
|
|
281
412
|
var restToolsManifestRoute = async (request, ctx) => {
|
|
@@ -334,6 +465,8 @@ function newAccumulator() {
|
|
|
334
465
|
interrupted: false,
|
|
335
466
|
erroredInBand: false,
|
|
336
467
|
threadCreateFired: false,
|
|
468
|
+
personaRunId: void 0,
|
|
469
|
+
threadId: void 0,
|
|
337
470
|
toolNames: /* @__PURE__ */ new Map(),
|
|
338
471
|
nodeMeta: /* @__PURE__ */ new Map()
|
|
339
472
|
};
|
|
@@ -343,6 +476,8 @@ async function processEvent(event, acc, runCtx, hooks) {
|
|
|
343
476
|
switch (e.type) {
|
|
344
477
|
case import_sdk3.EventType.RUN_STARTED: {
|
|
345
478
|
const threadId = typeof e.threadId === "string" ? e.threadId : void 0;
|
|
479
|
+
if (!acc.personaRunId && typeof e.runId === "string") acc.personaRunId = e.runId;
|
|
480
|
+
if (!acc.threadId && threadId) acc.threadId = threadId;
|
|
346
481
|
if (runCtx.kind === "chat" && runCtx.agentId && !acc.threadCreateFired && !runCtx.threadId && threadId) {
|
|
347
482
|
acc.threadCreateFired = true;
|
|
348
483
|
await hooks?.onThreadCreate?.({ userId: runCtx.userId, agentId: runCtx.agentId, threadId });
|
|
@@ -451,9 +586,9 @@ var RunDriver = class {
|
|
|
451
586
|
constructor(runId, runCtx, source, hooks, mode) {
|
|
452
587
|
this.runId = runId;
|
|
453
588
|
this.runCtx = runCtx;
|
|
454
|
-
this.firstFramePromise = new Promise((resolve,
|
|
589
|
+
this.firstFramePromise = new Promise((resolve, reject2) => {
|
|
455
590
|
this.resolveFirstFrame = resolve;
|
|
456
|
-
this.rejectFirstFrame =
|
|
591
|
+
this.rejectFirstFrame = reject2;
|
|
457
592
|
});
|
|
458
593
|
void this.pump(source, hooks, mode);
|
|
459
594
|
}
|
|
@@ -488,7 +623,9 @@ var RunDriver = class {
|
|
|
488
623
|
text: acc.text,
|
|
489
624
|
eventCount: count,
|
|
490
625
|
interrupted: acc.interrupted,
|
|
491
|
-
erroredInBand: acc.erroredInBand
|
|
626
|
+
erroredInBand: acc.erroredInBand,
|
|
627
|
+
...acc.personaRunId ? { personaRunId: acc.personaRunId } : {},
|
|
628
|
+
...acc.threadId || this.runCtx.threadId ? { threadId: acc.threadId ?? this.runCtx.threadId } : {}
|
|
492
629
|
};
|
|
493
630
|
try {
|
|
494
631
|
await hooks?.afterRun?.(this.runCtx, result);
|
|
@@ -538,14 +675,13 @@ var RunDriver = class {
|
|
|
538
675
|
* original connection plus a reconnect that raced it) are supported.
|
|
539
676
|
*/
|
|
540
677
|
subscribe(sinceSeq) {
|
|
541
|
-
const
|
|
542
|
-
const finishedAtSubscribeTime = this.finished;
|
|
678
|
+
const driver = this;
|
|
543
679
|
const listeners = this.listeners;
|
|
544
680
|
const doneListeners = this.doneListeners;
|
|
545
681
|
return {
|
|
546
682
|
[Symbol.asyncIterator]() {
|
|
547
|
-
const queue =
|
|
548
|
-
let live = !
|
|
683
|
+
const queue = driver.frames.slice(Math.max(sinceSeq + 1, 0));
|
|
684
|
+
let live = !driver.finished;
|
|
549
685
|
let wake = null;
|
|
550
686
|
const onFrame = (_seq, frame) => {
|
|
551
687
|
queue.push(frame);
|
|
@@ -685,12 +821,19 @@ var chatRoute = async (request, ctx) => {
|
|
|
685
821
|
hasContext: !!body.context
|
|
686
822
|
});
|
|
687
823
|
const userId = request.userId;
|
|
824
|
+
const version = await resolveRunVersion(request.body, ctx, {
|
|
825
|
+
userId,
|
|
826
|
+
agentId: body.agentId,
|
|
827
|
+
kind: "chat"
|
|
828
|
+
});
|
|
829
|
+
const traceparent = incomingTraceparent(request.headers, ctx.forwardTraceparent);
|
|
688
830
|
const runCtx = {
|
|
689
831
|
userId,
|
|
690
832
|
kind: "chat",
|
|
691
833
|
agentId: body.agentId,
|
|
692
834
|
threadId: body.threadId,
|
|
693
|
-
messages: body.messages
|
|
835
|
+
messages: body.messages,
|
|
836
|
+
...version
|
|
694
837
|
};
|
|
695
838
|
logger.debug("beforeRun hook", { agentId: body.agentId, threadId: body.threadId });
|
|
696
839
|
await ctx.hooks?.beforeRun?.(runCtx);
|
|
@@ -703,7 +846,9 @@ var chatRoute = async (request, ctx) => {
|
|
|
703
846
|
contextOverride: body.contextOverride,
|
|
704
847
|
context: body.context,
|
|
705
848
|
tools: body.tools,
|
|
706
|
-
attachments: body.attachments
|
|
849
|
+
attachments: body.attachments,
|
|
850
|
+
...version,
|
|
851
|
+
...traceparent ? { traceparent } : {}
|
|
707
852
|
});
|
|
708
853
|
const runId = crypto.randomUUID();
|
|
709
854
|
logger.info("chat run created", {
|
|
@@ -902,7 +1047,16 @@ var createVoiceSession = async (request, ctx) => {
|
|
|
902
1047
|
const contextOverride = typeof body.contextOverride === "string" && body.contextOverride ? body.contextOverride : void 0;
|
|
903
1048
|
const context = validateTurnContext(body.context);
|
|
904
1049
|
const tools = parseFrontendTools(body.tools);
|
|
1050
|
+
const version = await resolveRunVersion(body, ctx, {
|
|
1051
|
+
userId: request.userId,
|
|
1052
|
+
agentId,
|
|
1053
|
+
kind: "voice"
|
|
1054
|
+
});
|
|
1055
|
+
const traceparent = incomingTraceparent(request.headers, ctx.forwardTraceparent);
|
|
905
1056
|
const ticket = await ctx.client.voice.createSession(agentId, {
|
|
1057
|
+
...version,
|
|
1058
|
+
// `traceparent` needs @personaai/sdk >= 0.27 (older SDKs ignore it).
|
|
1059
|
+
...traceparent ? { traceparent } : {},
|
|
906
1060
|
...threadId ? { threadId } : {},
|
|
907
1061
|
...contextOverride ? { contextOverride } : {},
|
|
908
1062
|
...context ? { context } : {},
|
|
@@ -911,7 +1065,8 @@ var createVoiceSession = async (request, ctx) => {
|
|
|
911
1065
|
await ctx.hooks?.onVoiceSessionCreate?.({
|
|
912
1066
|
userId: request.userId,
|
|
913
1067
|
agentId,
|
|
914
|
-
threadId
|
|
1068
|
+
threadId,
|
|
1069
|
+
...version
|
|
915
1070
|
});
|
|
916
1071
|
return json(201, ticket);
|
|
917
1072
|
};
|
|
@@ -992,12 +1147,23 @@ var sendAgentMessage = async (request, ctx) => {
|
|
|
992
1147
|
const prompt = requireStringField(body, "prompt");
|
|
993
1148
|
const channel = optionalChannel(body);
|
|
994
1149
|
const displayName = optionalStringField(body, "displayName");
|
|
1150
|
+
const version = await resolveRunVersion(body, ctx, {
|
|
1151
|
+
userId: request.userId,
|
|
1152
|
+
agentId,
|
|
1153
|
+
kind: "messages"
|
|
1154
|
+
});
|
|
1155
|
+
const traceparent = incomingTraceparent(request.headers, ctx.forwardTraceparent);
|
|
1156
|
+
const runOptions = {
|
|
1157
|
+
...version,
|
|
1158
|
+
...traceparent ? { traceparent } : {}
|
|
1159
|
+
};
|
|
995
1160
|
const delivery = await ctx.client.messages.sendFromAgent({
|
|
996
1161
|
agentId,
|
|
997
1162
|
to,
|
|
998
1163
|
prompt,
|
|
999
1164
|
...channel ? { channel } : {},
|
|
1000
|
-
...displayName ? { displayName } : {}
|
|
1165
|
+
...displayName ? { displayName } : {},
|
|
1166
|
+
...runOptions
|
|
1001
1167
|
});
|
|
1002
1168
|
return json(200, delivery);
|
|
1003
1169
|
};
|
|
@@ -1693,11 +1859,26 @@ var workflowStreamRoute = async (request, ctx) => {
|
|
|
1693
1859
|
workflowId,
|
|
1694
1860
|
input: body.input
|
|
1695
1861
|
};
|
|
1862
|
+
const agentVersion = await resolveRunVersion(
|
|
1863
|
+
request.body ?? {},
|
|
1864
|
+
ctx,
|
|
1865
|
+
{ userId, workflowId, kind: "workflow" },
|
|
1866
|
+
"agentEnvironment"
|
|
1867
|
+
);
|
|
1868
|
+
if (agentVersion.environment) runCtx.environment = agentVersion.environment;
|
|
1869
|
+
if (agentVersion.agentVersion) runCtx.agentVersion = agentVersion.agentVersion;
|
|
1870
|
+
const traceparent = incomingTraceparent(request.headers, ctx.forwardTraceparent);
|
|
1696
1871
|
await ctx.hooks?.beforeRun?.(runCtx);
|
|
1872
|
+
const runOptions = {
|
|
1873
|
+
...agentVersion.environment ? { agentEnvironment: agentVersion.environment } : {},
|
|
1874
|
+
...agentVersion.agentVersion ? { agentVersion: agentVersion.agentVersion } : {},
|
|
1875
|
+
...traceparent ? { traceparent } : {}
|
|
1876
|
+
};
|
|
1697
1877
|
const stream = ctx.client.workflows.stream(workflowId, {
|
|
1698
1878
|
input: body.input,
|
|
1699
1879
|
dryRun: body.dryRun,
|
|
1700
|
-
version: body.version
|
|
1880
|
+
version: body.version,
|
|
1881
|
+
...runOptions
|
|
1701
1882
|
});
|
|
1702
1883
|
const runId = crypto.randomUUID();
|
|
1703
1884
|
const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
|
|
@@ -1745,15 +1926,15 @@ function resolveSignedUrl(request, publicUrl) {
|
|
|
1745
1926
|
}
|
|
1746
1927
|
async function computeTwilioSignature(authToken, url, params) {
|
|
1747
1928
|
const payload = url + Object.keys(params).sort().map((key2) => `${key2}${params[key2]}`).join("");
|
|
1748
|
-
const
|
|
1929
|
+
const encoder2 = new TextEncoder();
|
|
1749
1930
|
const key = await crypto.subtle.importKey(
|
|
1750
1931
|
"raw",
|
|
1751
|
-
|
|
1932
|
+
encoder2.encode(authToken),
|
|
1752
1933
|
{ name: "HMAC", hash: "SHA-1" },
|
|
1753
1934
|
false,
|
|
1754
1935
|
["sign"]
|
|
1755
1936
|
);
|
|
1756
|
-
const signature = await crypto.subtle.sign("HMAC", key,
|
|
1937
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder2.encode(payload));
|
|
1757
1938
|
const bytes = new Uint8Array(signature);
|
|
1758
1939
|
let binary = "";
|
|
1759
1940
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
@@ -1918,6 +2099,598 @@ var twilioMessageChannel = async (request, ctx) => {
|
|
|
1918
2099
|
}
|
|
1919
2100
|
};
|
|
1920
2101
|
|
|
2102
|
+
// src/routes/feedback.ts
|
|
2103
|
+
var MAX_COMMENT = 2e3;
|
|
2104
|
+
var MAX_TAGS = 10;
|
|
2105
|
+
var MAX_TAG = 50;
|
|
2106
|
+
var createFeedback = async (request, ctx) => {
|
|
2107
|
+
const body = requireBodyObject(request.body);
|
|
2108
|
+
const runId = requireStringField(body, "runId");
|
|
2109
|
+
if (body.score !== 1 && body.score !== -1) {
|
|
2110
|
+
throw new RuntimeHttpError(400, "INVALID_REQUEST", '"score" must be 1 or -1.');
|
|
2111
|
+
}
|
|
2112
|
+
const score = body.score;
|
|
2113
|
+
let comment;
|
|
2114
|
+
if (body.comment !== void 0 && body.comment !== null) {
|
|
2115
|
+
if (typeof body.comment !== "string" || body.comment.length > MAX_COMMENT) {
|
|
2116
|
+
throw new RuntimeHttpError(
|
|
2117
|
+
400,
|
|
2118
|
+
"INVALID_REQUEST",
|
|
2119
|
+
`"comment" must be a string of at most ${MAX_COMMENT} characters.`
|
|
2120
|
+
);
|
|
2121
|
+
}
|
|
2122
|
+
comment = body.comment.trim() || void 0;
|
|
2123
|
+
}
|
|
2124
|
+
let tags;
|
|
2125
|
+
if (body.tags !== void 0 && body.tags !== null) {
|
|
2126
|
+
if (!Array.isArray(body.tags) || body.tags.length > MAX_TAGS || !body.tags.every((t) => typeof t === "string" && t.length > 0 && t.length <= MAX_TAG)) {
|
|
2127
|
+
throw new RuntimeHttpError(
|
|
2128
|
+
400,
|
|
2129
|
+
"INVALID_REQUEST",
|
|
2130
|
+
`"tags" must be up to ${MAX_TAGS} strings of at most ${MAX_TAG} characters.`
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
tags = body.tags;
|
|
2134
|
+
}
|
|
2135
|
+
const result = await ctx.client.feedback.create({
|
|
2136
|
+
runId,
|
|
2137
|
+
score,
|
|
2138
|
+
...comment ? { comment } : {},
|
|
2139
|
+
...tags?.length ? { tags } : {}
|
|
2140
|
+
});
|
|
2141
|
+
await ctx.hooks?.onFeedback?.({
|
|
2142
|
+
userId: request.userId,
|
|
2143
|
+
runId,
|
|
2144
|
+
score,
|
|
2145
|
+
...comment ? { comment } : {},
|
|
2146
|
+
...tags?.length ? { tags } : {}
|
|
2147
|
+
});
|
|
2148
|
+
return json(200, result);
|
|
2149
|
+
};
|
|
2150
|
+
|
|
2151
|
+
// src/routes/webhooks.ts
|
|
2152
|
+
var encoder = new TextEncoder();
|
|
2153
|
+
var DEFAULT_SEEN_LIMIT = 1e3;
|
|
2154
|
+
function rawBodyBytes(request) {
|
|
2155
|
+
const raw = request.rawBody;
|
|
2156
|
+
if (raw === void 0 || raw === null) return null;
|
|
2157
|
+
return typeof raw === "string" ? encoder.encode(raw) : raw;
|
|
2158
|
+
}
|
|
2159
|
+
function parseSignature(header) {
|
|
2160
|
+
let timestamp = null;
|
|
2161
|
+
const signatures = [];
|
|
2162
|
+
for (const part of header.split(",")) {
|
|
2163
|
+
const eq = part.indexOf("=");
|
|
2164
|
+
if (eq <= 0) continue;
|
|
2165
|
+
const key = part.slice(0, eq).trim();
|
|
2166
|
+
const value = part.slice(eq + 1).trim();
|
|
2167
|
+
if (key === "t" && /^\d+$/.test(value)) timestamp = Number(value);
|
|
2168
|
+
else if (key === "v1" && /^[0-9a-f]{64}$/i.test(value)) signatures.push(value.toLowerCase());
|
|
2169
|
+
}
|
|
2170
|
+
return timestamp === null || signatures.length === 0 ? null : { timestamp, signatures };
|
|
2171
|
+
}
|
|
2172
|
+
function toHex(bytes) {
|
|
2173
|
+
return [...new Uint8Array(bytes)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2174
|
+
}
|
|
2175
|
+
function safeEqual(a, b) {
|
|
2176
|
+
if (a.length !== b.length) return false;
|
|
2177
|
+
let diff = 0;
|
|
2178
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
2179
|
+
return diff === 0;
|
|
2180
|
+
}
|
|
2181
|
+
async function hmacHex(secret, message) {
|
|
2182
|
+
const key = await crypto.subtle.importKey(
|
|
2183
|
+
"raw",
|
|
2184
|
+
encoder.encode(secret),
|
|
2185
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
2186
|
+
false,
|
|
2187
|
+
["sign"]
|
|
2188
|
+
);
|
|
2189
|
+
return toHex(await crypto.subtle.sign("HMAC", key, message));
|
|
2190
|
+
}
|
|
2191
|
+
function concat(a, b) {
|
|
2192
|
+
const out = new Uint8Array(a.length + b.length);
|
|
2193
|
+
out.set(a, 0);
|
|
2194
|
+
out.set(b, a.length);
|
|
2195
|
+
return out;
|
|
2196
|
+
}
|
|
2197
|
+
var reject = (code, message) => json(400, { error: { code, message } });
|
|
2198
|
+
function createSeenSet(limit = DEFAULT_SEEN_LIMIT) {
|
|
2199
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2200
|
+
return {
|
|
2201
|
+
has: (id) => ids.has(id),
|
|
2202
|
+
add(id) {
|
|
2203
|
+
ids.add(id);
|
|
2204
|
+
if (ids.size > limit) ids.delete(ids.values().next().value);
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
function createWebhookRoute() {
|
|
2209
|
+
const memory = createSeenSet();
|
|
2210
|
+
return async (request, ctx) => {
|
|
2211
|
+
const options = ctx.webhooks;
|
|
2212
|
+
if (!options) return json(404, { error: { code: "NOT_FOUND", message: "Not found" } });
|
|
2213
|
+
const logger = ctx.logger.child("webhooks");
|
|
2214
|
+
const body = rawBodyBytes(request);
|
|
2215
|
+
if (!body) {
|
|
2216
|
+
logger.error("webhook request has no rawBody: the adapter must pass the raw bytes");
|
|
2217
|
+
return json(500, {
|
|
2218
|
+
error: {
|
|
2219
|
+
code: "RUNTIME_MISCONFIGURED",
|
|
2220
|
+
message: "The server did not keep the raw request body for webhook verification."
|
|
2221
|
+
}
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
const header = request.headers["x-persona-signature"];
|
|
2225
|
+
if (!header) return reject("MISSING_SIGNATURE", "Missing X-Persona-Signature header");
|
|
2226
|
+
const parsed = parseSignature(header);
|
|
2227
|
+
if (!parsed) return reject("INVALID_SIGNATURE", "Malformed X-Persona-Signature header");
|
|
2228
|
+
const expected = await hmacHex(
|
|
2229
|
+
options.secret,
|
|
2230
|
+
concat(encoder.encode(`${parsed.timestamp}.`), body)
|
|
2231
|
+
);
|
|
2232
|
+
if (!parsed.signatures.some((sig) => safeEqual(sig, expected))) {
|
|
2233
|
+
logger.warn("webhook signature does not match");
|
|
2234
|
+
return reject("INVALID_SIGNATURE", "Webhook signature does not match");
|
|
2235
|
+
}
|
|
2236
|
+
const tolerance = options.toleranceSec ?? 300;
|
|
2237
|
+
if (tolerance > 0 && Math.abs(Math.floor(Date.now() / 1e3) - parsed.timestamp) > tolerance) {
|
|
2238
|
+
return reject("TIMESTAMP_OUT_OF_RANGE", "Webhook timestamp is outside the allowed tolerance");
|
|
2239
|
+
}
|
|
2240
|
+
let event;
|
|
2241
|
+
try {
|
|
2242
|
+
event = JSON.parse(new TextDecoder().decode(body));
|
|
2243
|
+
} catch {
|
|
2244
|
+
return reject("INVALID_PAYLOAD", "Webhook body is not valid JSON");
|
|
2245
|
+
}
|
|
2246
|
+
if (!event || typeof event !== "object" || typeof event.type !== "string") {
|
|
2247
|
+
return reject("INVALID_PAYLOAD", "Webhook body is not a Persona event");
|
|
2248
|
+
}
|
|
2249
|
+
const deliveryId = request.headers["x-persona-delivery"];
|
|
2250
|
+
if (deliveryId) {
|
|
2251
|
+
const store = options.dedupe ?? memory;
|
|
2252
|
+
if (await store.has(deliveryId)) {
|
|
2253
|
+
logger.debug("duplicate webhook delivery ignored", { deliveryId, type: event.type });
|
|
2254
|
+
return json(200, { received: true, duplicate: true });
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
const type = event.type;
|
|
2258
|
+
const handlers = [options.on[type], options.on["*"]].filter(
|
|
2259
|
+
(h) => typeof h === "function"
|
|
2260
|
+
);
|
|
2261
|
+
try {
|
|
2262
|
+
for (const handler of handlers) await handler(event, { id: deliveryId, type });
|
|
2263
|
+
} catch (err) {
|
|
2264
|
+
logger.error("webhook handler failed", {
|
|
2265
|
+
type,
|
|
2266
|
+
deliveryId,
|
|
2267
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2268
|
+
});
|
|
2269
|
+
return json(500, { error: { code: "HANDLER_FAILED", message: "Webhook handler failed" } });
|
|
2270
|
+
}
|
|
2271
|
+
if (deliveryId) await (options.dedupe ?? memory).add(deliveryId);
|
|
2272
|
+
logger.info("webhook handled", { type, deliveryId, handlers: handlers.length });
|
|
2273
|
+
return json(200, { received: true });
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// src/routes/secrets.ts
|
|
2278
|
+
var listSecrets = async (_request, ctx) => {
|
|
2279
|
+
return json(200, await ctx.client.secrets.list());
|
|
2280
|
+
};
|
|
2281
|
+
var createSecret = async (request, ctx) => {
|
|
2282
|
+
const body = requireBodyObject(request.body);
|
|
2283
|
+
const secret = await ctx.client.secrets.create(
|
|
2284
|
+
{
|
|
2285
|
+
label: requireStringField(body, "label"),
|
|
2286
|
+
value: requireStringField(body, "value")
|
|
2287
|
+
},
|
|
2288
|
+
request.headers["idempotency-key"]
|
|
2289
|
+
);
|
|
2290
|
+
return json(201, secret);
|
|
2291
|
+
};
|
|
2292
|
+
var getSecret = async (_request, ctx) => {
|
|
2293
|
+
return json(200, await ctx.client.secrets.get(requireParam(ctx.params, "id")));
|
|
2294
|
+
};
|
|
2295
|
+
var updateSecret = async (request, ctx) => {
|
|
2296
|
+
const body = request.body ?? {};
|
|
2297
|
+
return json(200, await ctx.client.secrets.update(requireParam(ctx.params, "id"), body));
|
|
2298
|
+
};
|
|
2299
|
+
var deleteSecret = async (_request, ctx) => {
|
|
2300
|
+
await ctx.client.secrets.delete(requireParam(ctx.params, "id"));
|
|
2301
|
+
return noContent();
|
|
2302
|
+
};
|
|
2303
|
+
var getSecretUsage = async (_request, ctx) => {
|
|
2304
|
+
return json(200, await ctx.client.secrets.getUsage(requireParam(ctx.params, "id")));
|
|
2305
|
+
};
|
|
2306
|
+
var bulkDeleteSecrets = async (request, ctx) => {
|
|
2307
|
+
const body = requireBodyObject(request.body);
|
|
2308
|
+
const ids = Array.isArray(body.ids) ? body.ids : [];
|
|
2309
|
+
return json(200, await ctx.client.secrets.bulkDelete(ids));
|
|
2310
|
+
};
|
|
2311
|
+
|
|
2312
|
+
// src/routes/restToolSources.ts
|
|
2313
|
+
var listRestToolSources = async (request, ctx) => {
|
|
2314
|
+
const items = await ctx.client.restToolSources.list({
|
|
2315
|
+
page: toInt(request.query.page),
|
|
2316
|
+
limit: toInt(request.query.limit),
|
|
2317
|
+
search: request.query.search
|
|
2318
|
+
});
|
|
2319
|
+
return json(200, items);
|
|
2320
|
+
};
|
|
2321
|
+
var createRestToolSource = async (request, ctx) => {
|
|
2322
|
+
const body = requireBodyObject(request.body);
|
|
2323
|
+
const source = await ctx.client.restToolSources.create(body, request.headers["idempotency-key"]);
|
|
2324
|
+
return json(201, source);
|
|
2325
|
+
};
|
|
2326
|
+
var getRestToolSource = async (_request, ctx) => {
|
|
2327
|
+
return json(200, await ctx.client.restToolSources.get(requireParam(ctx.params, "id")));
|
|
2328
|
+
};
|
|
2329
|
+
var updateRestToolSource = async (request, ctx) => {
|
|
2330
|
+
const body = request.body ?? {};
|
|
2331
|
+
return json(200, await ctx.client.restToolSources.update(requireParam(ctx.params, "id"), body));
|
|
2332
|
+
};
|
|
2333
|
+
var deleteRestToolSource = async (_request, ctx) => {
|
|
2334
|
+
await ctx.client.restToolSources.delete(requireParam(ctx.params, "id"));
|
|
2335
|
+
return noContent();
|
|
2336
|
+
};
|
|
2337
|
+
var getRestToolSourceUsage = async (_request, ctx) => {
|
|
2338
|
+
return json(200, await ctx.client.restToolSources.getUsage(requireParam(ctx.params, "id")));
|
|
2339
|
+
};
|
|
2340
|
+
var testRestToolSource = async (_request, ctx) => {
|
|
2341
|
+
return json(200, await ctx.client.restToolSources.testConnection(requireParam(ctx.params, "id")));
|
|
2342
|
+
};
|
|
2343
|
+
var bulkDeleteRestToolSources = async (request, ctx) => {
|
|
2344
|
+
const body = requireBodyObject(request.body);
|
|
2345
|
+
const ids = Array.isArray(body.ids) ? body.ids : [];
|
|
2346
|
+
return json(200, await ctx.client.restToolSources.bulkDelete(ids));
|
|
2347
|
+
};
|
|
2348
|
+
|
|
2349
|
+
// src/routes/deploy.ts
|
|
2350
|
+
function environmentParam(ctx) {
|
|
2351
|
+
const env = requireParam(ctx.params, "environment");
|
|
2352
|
+
if (env !== "development" && env !== "production") {
|
|
2353
|
+
throw new RuntimeHttpError(
|
|
2354
|
+
400,
|
|
2355
|
+
"INVALID_REQUEST",
|
|
2356
|
+
'environment must be "development" or "production".'
|
|
2357
|
+
);
|
|
2358
|
+
}
|
|
2359
|
+
return env;
|
|
2360
|
+
}
|
|
2361
|
+
function positiveInt(raw, name) {
|
|
2362
|
+
const v = Number(raw);
|
|
2363
|
+
if (!Number.isInteger(v) || v < 1) {
|
|
2364
|
+
throw new RuntimeHttpError(400, "INVALID_REQUEST", `${name} must be a positive integer.`);
|
|
2365
|
+
}
|
|
2366
|
+
return v;
|
|
2367
|
+
}
|
|
2368
|
+
function versionOrDraft(raw, name) {
|
|
2369
|
+
if (!raw || raw === "draft") return "draft";
|
|
2370
|
+
return positiveInt(raw, name);
|
|
2371
|
+
}
|
|
2372
|
+
async function audit(request, ctx, action, target) {
|
|
2373
|
+
await ctx.hooks?.onAdminAction?.({
|
|
2374
|
+
userId: request.userId,
|
|
2375
|
+
capability: "deploy",
|
|
2376
|
+
action,
|
|
2377
|
+
target
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
var listAgentVersions = async (request, ctx) => {
|
|
2381
|
+
return json(
|
|
2382
|
+
200,
|
|
2383
|
+
await ctx.projectClient().agents.versions.list(requireParam(ctx.params, "id"), {
|
|
2384
|
+
page: toInt(request.query.page),
|
|
2385
|
+
limit: toInt(request.query.limit)
|
|
2386
|
+
})
|
|
2387
|
+
);
|
|
2388
|
+
};
|
|
2389
|
+
var publishAgentVersion = async (request, ctx) => {
|
|
2390
|
+
const agentId = requireParam(ctx.params, "id");
|
|
2391
|
+
const body = request.body ?? {};
|
|
2392
|
+
const changelog = typeof body.changelog === "string" ? body.changelog : void 0;
|
|
2393
|
+
const version = await ctx.projectClient().agents.versions.publish(agentId, changelog ? { changelog } : {});
|
|
2394
|
+
await audit(request, ctx, "agent.version.publish", { agentId, version: version.version });
|
|
2395
|
+
return json(201, version);
|
|
2396
|
+
};
|
|
2397
|
+
var agentVersionStats = async (request, ctx) => {
|
|
2398
|
+
const source = request.query.source;
|
|
2399
|
+
return json(
|
|
2400
|
+
200,
|
|
2401
|
+
await ctx.projectClient().agents.versions.stats(requireParam(ctx.params, "id"), {
|
|
2402
|
+
days: toInt(request.query.days),
|
|
2403
|
+
...source ? { source } : {}
|
|
2404
|
+
})
|
|
2405
|
+
);
|
|
2406
|
+
};
|
|
2407
|
+
var getAgentVersion = async (_request, ctx) => {
|
|
2408
|
+
const version = positiveInt(requireParam(ctx.params, "version"), "version");
|
|
2409
|
+
return json(
|
|
2410
|
+
200,
|
|
2411
|
+
await ctx.projectClient().agents.versions.get(requireParam(ctx.params, "id"), version)
|
|
2412
|
+
);
|
|
2413
|
+
};
|
|
2414
|
+
var diffAgentVersion = async (request, ctx) => {
|
|
2415
|
+
const from = versionOrDraft(requireParam(ctx.params, "version"), "version");
|
|
2416
|
+
const to = versionOrDraft(request.query.against, '"against"');
|
|
2417
|
+
return json(
|
|
2418
|
+
200,
|
|
2419
|
+
await ctx.projectClient().agents.versions.diff(requireParam(ctx.params, "id"), from, to)
|
|
2420
|
+
);
|
|
2421
|
+
};
|
|
2422
|
+
var getAgentDeployments = async (_request, ctx) => {
|
|
2423
|
+
return json(
|
|
2424
|
+
200,
|
|
2425
|
+
await ctx.projectClient().agents.versions.deployments(requireParam(ctx.params, "id"))
|
|
2426
|
+
);
|
|
2427
|
+
};
|
|
2428
|
+
var setAgentServePolicy = async (request, ctx) => {
|
|
2429
|
+
const agentId = requireParam(ctx.params, "id");
|
|
2430
|
+
const body = requireBodyObject(request.body);
|
|
2431
|
+
if (typeof body.serveDeployedVersion !== "boolean") {
|
|
2432
|
+
throw new RuntimeHttpError(
|
|
2433
|
+
400,
|
|
2434
|
+
"INVALID_REQUEST",
|
|
2435
|
+
'"serveDeployedVersion" is required and must be a boolean.'
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
2438
|
+
const result = await ctx.projectClient().agents.versions.setServePolicy(agentId, body.serveDeployedVersion);
|
|
2439
|
+
await audit(request, ctx, "agent.serve_policy.set", { agentId, ...result });
|
|
2440
|
+
return json(200, result);
|
|
2441
|
+
};
|
|
2442
|
+
var deployAgentVersion = async (request, ctx) => {
|
|
2443
|
+
const agentId = requireParam(ctx.params, "id");
|
|
2444
|
+
const environment = environmentParam(ctx);
|
|
2445
|
+
const body = request.body ?? {};
|
|
2446
|
+
let version;
|
|
2447
|
+
if (body.version !== void 0 && body.version !== null) {
|
|
2448
|
+
if (typeof body.version !== "number" || !Number.isInteger(body.version) || body.version < 1) {
|
|
2449
|
+
throw new RuntimeHttpError(400, "INVALID_REQUEST", '"version" must be a positive integer.');
|
|
2450
|
+
}
|
|
2451
|
+
version = body.version;
|
|
2452
|
+
}
|
|
2453
|
+
const deployment = await ctx.projectClient().agents.versions.deploy(agentId, { environment, ...version ? { version } : {} });
|
|
2454
|
+
await audit(request, ctx, "agent.version.deploy", {
|
|
2455
|
+
agentId,
|
|
2456
|
+
environment,
|
|
2457
|
+
version: deployment.version
|
|
2458
|
+
});
|
|
2459
|
+
return json(200, deployment);
|
|
2460
|
+
};
|
|
2461
|
+
var rollbackAgentDeployment = async (request, ctx) => {
|
|
2462
|
+
const agentId = requireParam(ctx.params, "id");
|
|
2463
|
+
const environment = environmentParam(ctx);
|
|
2464
|
+
const deployment = await ctx.projectClient().agents.versions.rollback(agentId, environment);
|
|
2465
|
+
await audit(request, ctx, "agent.version.rollback", {
|
|
2466
|
+
agentId,
|
|
2467
|
+
environment,
|
|
2468
|
+
version: deployment.version
|
|
2469
|
+
});
|
|
2470
|
+
return json(200, deployment);
|
|
2471
|
+
};
|
|
2472
|
+
|
|
2473
|
+
// src/routes/traces.ts
|
|
2474
|
+
var STRING_FILTERS = [
|
|
2475
|
+
"agentId",
|
|
2476
|
+
"threadId",
|
|
2477
|
+
"runId",
|
|
2478
|
+
"status",
|
|
2479
|
+
"source",
|
|
2480
|
+
"channel",
|
|
2481
|
+
"environment",
|
|
2482
|
+
"feedback",
|
|
2483
|
+
"from",
|
|
2484
|
+
"to"
|
|
2485
|
+
];
|
|
2486
|
+
var listTraces = async (request, ctx) => {
|
|
2487
|
+
const params = {
|
|
2488
|
+
page: toInt(request.query.page),
|
|
2489
|
+
limit: toInt(request.query.limit),
|
|
2490
|
+
agentVersion: toInt(request.query.agentVersion)
|
|
2491
|
+
};
|
|
2492
|
+
for (const key of STRING_FILTERS) {
|
|
2493
|
+
const value = request.query[key];
|
|
2494
|
+
if (value) params[key] = value;
|
|
2495
|
+
}
|
|
2496
|
+
return json(200, await ctx.projectClient().traces.list(params));
|
|
2497
|
+
};
|
|
2498
|
+
var getTrace = async (request, ctx) => {
|
|
2499
|
+
return json(
|
|
2500
|
+
200,
|
|
2501
|
+
await ctx.projectClient().traces.get(requireParam(ctx.params, "id"), {
|
|
2502
|
+
spans: request.query.spans !== "false"
|
|
2503
|
+
})
|
|
2504
|
+
);
|
|
2505
|
+
};
|
|
2506
|
+
var getTraceFeedback = async (_request, ctx) => {
|
|
2507
|
+
return json(200, await ctx.projectClient().traces.feedback(requireParam(ctx.params, "id")));
|
|
2508
|
+
};
|
|
2509
|
+
var getReplayPlan = async (_request, ctx) => {
|
|
2510
|
+
return json(200, await ctx.projectClient().traces.replayPlan(requireParam(ctx.params, "id")));
|
|
2511
|
+
};
|
|
2512
|
+
var replayTrace = async (request, ctx) => {
|
|
2513
|
+
const traceId = requireParam(ctx.params, "id");
|
|
2514
|
+
const options = request.body ?? {};
|
|
2515
|
+
const result = await ctx.projectClient().traces.replay(traceId, options);
|
|
2516
|
+
await ctx.hooks?.onAdminAction?.({
|
|
2517
|
+
userId: request.userId,
|
|
2518
|
+
capability: "traces",
|
|
2519
|
+
action: "trace.replay",
|
|
2520
|
+
target: { traceId, replayTraceId: result.traceId }
|
|
2521
|
+
});
|
|
2522
|
+
return json(201, result);
|
|
2523
|
+
};
|
|
2524
|
+
var listTraceReplays = async (_request, ctx) => {
|
|
2525
|
+
return json(200, await ctx.projectClient().traces.replays(requireParam(ctx.params, "id")));
|
|
2526
|
+
};
|
|
2527
|
+
|
|
2528
|
+
// src/routes/evals.ts
|
|
2529
|
+
async function audit2(request, ctx, action, target) {
|
|
2530
|
+
await ctx.hooks?.onAdminAction?.({
|
|
2531
|
+
userId: request.userId,
|
|
2532
|
+
capability: "evals",
|
|
2533
|
+
action,
|
|
2534
|
+
target
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
var listDatasets = async (_request, ctx) => {
|
|
2538
|
+
return json(200, await ctx.projectClient().datasets.list());
|
|
2539
|
+
};
|
|
2540
|
+
var createDataset = async (request, ctx) => {
|
|
2541
|
+
const body = requireBodyObject(request.body);
|
|
2542
|
+
const dataset = await ctx.projectClient().datasets.create(body);
|
|
2543
|
+
await audit2(request, ctx, "dataset.create", { datasetId: dataset._id });
|
|
2544
|
+
return json(201, dataset);
|
|
2545
|
+
};
|
|
2546
|
+
var getDataset = async (_request, ctx) => {
|
|
2547
|
+
return json(200, await ctx.projectClient().datasets.get(requireParam(ctx.params, "id")));
|
|
2548
|
+
};
|
|
2549
|
+
var updateDataset = async (request, ctx) => {
|
|
2550
|
+
const body = requireBodyObject(request.body);
|
|
2551
|
+
return json(200, await ctx.projectClient().datasets.update(requireParam(ctx.params, "id"), body));
|
|
2552
|
+
};
|
|
2553
|
+
var deleteDataset = async (request, ctx) => {
|
|
2554
|
+
const datasetId = requireParam(ctx.params, "id");
|
|
2555
|
+
const result = await ctx.projectClient().datasets.delete(datasetId);
|
|
2556
|
+
await audit2(request, ctx, "dataset.delete", { datasetId });
|
|
2557
|
+
return json(200, result);
|
|
2558
|
+
};
|
|
2559
|
+
var listDatasetItems = async (request, ctx) => {
|
|
2560
|
+
return json(
|
|
2561
|
+
200,
|
|
2562
|
+
await ctx.projectClient().datasets.items.list(requireParam(ctx.params, "id"), {
|
|
2563
|
+
page: toInt(request.query.page),
|
|
2564
|
+
limit: toInt(request.query.limit)
|
|
2565
|
+
})
|
|
2566
|
+
);
|
|
2567
|
+
};
|
|
2568
|
+
var addDatasetItems = async (request, ctx) => {
|
|
2569
|
+
const datasetId = requireParam(ctx.params, "id");
|
|
2570
|
+
const body = requireBodyObject(request.body);
|
|
2571
|
+
let cases;
|
|
2572
|
+
if (typeof body.text === "string") cases = body.text;
|
|
2573
|
+
else if (Array.isArray(body.items)) cases = body.items;
|
|
2574
|
+
else {
|
|
2575
|
+
throw new RuntimeHttpError(
|
|
2576
|
+
400,
|
|
2577
|
+
"INVALID_REQUEST",
|
|
2578
|
+
'Send "items" (an array of cases) or "text" (CSV / JSON).'
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
try {
|
|
2582
|
+
return json(201, await ctx.projectClient().datasets.import(datasetId, cases));
|
|
2583
|
+
} catch (err) {
|
|
2584
|
+
if (err instanceof Error && err.message.startsWith("parseDatasetCases:")) {
|
|
2585
|
+
throw new RuntimeHttpError(
|
|
2586
|
+
400,
|
|
2587
|
+
"INVALID_REQUEST",
|
|
2588
|
+
err.message.replace("parseDatasetCases: ", "")
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
throw err;
|
|
2592
|
+
}
|
|
2593
|
+
};
|
|
2594
|
+
var addDatasetItemFromTrace = async (request, ctx) => {
|
|
2595
|
+
const body = requireBodyObject(request.body);
|
|
2596
|
+
const traceId = requireStringField(body, "traceId");
|
|
2597
|
+
const options = body.expected === null ? { expected: null } : typeof body.expected === "string" ? { expected: body.expected } : {};
|
|
2598
|
+
return json(
|
|
2599
|
+
201,
|
|
2600
|
+
await ctx.projectClient().datasets.items.addFromTrace(requireParam(ctx.params, "id"), traceId, options)
|
|
2601
|
+
);
|
|
2602
|
+
};
|
|
2603
|
+
var updateDatasetItem = async (request, ctx) => {
|
|
2604
|
+
const body = requireBodyObject(request.body);
|
|
2605
|
+
return json(
|
|
2606
|
+
200,
|
|
2607
|
+
await ctx.projectClient().datasets.items.update(
|
|
2608
|
+
requireParam(ctx.params, "id"),
|
|
2609
|
+
requireParam(ctx.params, "itemId"),
|
|
2610
|
+
body
|
|
2611
|
+
)
|
|
2612
|
+
);
|
|
2613
|
+
};
|
|
2614
|
+
var deleteDatasetItem = async (_request, ctx) => {
|
|
2615
|
+
return json(
|
|
2616
|
+
200,
|
|
2617
|
+
await ctx.projectClient().datasets.items.delete(requireParam(ctx.params, "id"), requireParam(ctx.params, "itemId"))
|
|
2618
|
+
);
|
|
2619
|
+
};
|
|
2620
|
+
var listEvalRuns = async (request, ctx) => {
|
|
2621
|
+
return json(
|
|
2622
|
+
200,
|
|
2623
|
+
await ctx.projectClient().evals.list({
|
|
2624
|
+
page: toInt(request.query.page),
|
|
2625
|
+
limit: toInt(request.query.limit),
|
|
2626
|
+
...request.query.agentId ? { agentId: request.query.agentId } : {},
|
|
2627
|
+
...request.query.datasetId ? { datasetId: request.query.datasetId } : {}
|
|
2628
|
+
})
|
|
2629
|
+
);
|
|
2630
|
+
};
|
|
2631
|
+
var startEvalRun = async (request, ctx) => {
|
|
2632
|
+
const body = requireBodyObject(request.body);
|
|
2633
|
+
const run = await ctx.projectClient().evals.create(body);
|
|
2634
|
+
await audit2(request, ctx, "eval_run.start", {
|
|
2635
|
+
runId: run.id,
|
|
2636
|
+
agentId: run.agentId,
|
|
2637
|
+
datasetId: run.datasetId
|
|
2638
|
+
});
|
|
2639
|
+
return json(201, run);
|
|
2640
|
+
};
|
|
2641
|
+
var compareEvalRuns = async (request, ctx) => {
|
|
2642
|
+
const { a, b } = request.query;
|
|
2643
|
+
if (!a || !b) {
|
|
2644
|
+
throw new RuntimeHttpError(
|
|
2645
|
+
400,
|
|
2646
|
+
"INVALID_REQUEST",
|
|
2647
|
+
'"a" and "b" query parameters are required.'
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
return json(200, await ctx.projectClient().evals.compare(a, b));
|
|
2651
|
+
};
|
|
2652
|
+
var getEvalRun = async (_request, ctx) => {
|
|
2653
|
+
return json(200, await ctx.projectClient().evals.get(requireParam(ctx.params, "id")));
|
|
2654
|
+
};
|
|
2655
|
+
var deleteEvalRun = async (request, ctx) => {
|
|
2656
|
+
const runId = requireParam(ctx.params, "id");
|
|
2657
|
+
const result = await ctx.projectClient().evals.delete(runId);
|
|
2658
|
+
await audit2(request, ctx, "eval_run.delete", { runId });
|
|
2659
|
+
return json(200, result);
|
|
2660
|
+
};
|
|
2661
|
+
var listEvalResults = async (request, ctx) => {
|
|
2662
|
+
return json(
|
|
2663
|
+
200,
|
|
2664
|
+
await ctx.projectClient().evals.results(requireParam(ctx.params, "id"), {
|
|
2665
|
+
page: toInt(request.query.page),
|
|
2666
|
+
limit: toInt(request.query.limit),
|
|
2667
|
+
...request.query.failed === "true" ? { failed: true } : {}
|
|
2668
|
+
})
|
|
2669
|
+
);
|
|
2670
|
+
};
|
|
2671
|
+
var cancelEvalRun = async (request, ctx) => {
|
|
2672
|
+
const runId = requireParam(ctx.params, "id");
|
|
2673
|
+
const run = await ctx.projectClient().evals.cancel(runId);
|
|
2674
|
+
await audit2(request, ctx, "eval_run.cancel", { runId });
|
|
2675
|
+
return json(200, run);
|
|
2676
|
+
};
|
|
2677
|
+
|
|
2678
|
+
// src/routes/observe.ts
|
|
2679
|
+
var getBudget = async (_request, ctx) => {
|
|
2680
|
+
return json(200, await ctx.projectClient().observe.budget());
|
|
2681
|
+
};
|
|
2682
|
+
var getUsage = async (request, ctx) => {
|
|
2683
|
+
const source = request.query.source;
|
|
2684
|
+
return json(
|
|
2685
|
+
200,
|
|
2686
|
+
await ctx.projectClient().observe.usage({
|
|
2687
|
+
days: toInt(request.query.days),
|
|
2688
|
+
...request.query.agentId ? { agentId: request.query.agentId } : {},
|
|
2689
|
+
...source ? { source } : {}
|
|
2690
|
+
})
|
|
2691
|
+
);
|
|
2692
|
+
};
|
|
2693
|
+
|
|
1921
2694
|
// src/runtime.ts
|
|
1922
2695
|
function resolveCapabilities(capabilities) {
|
|
1923
2696
|
return {
|
|
@@ -1931,7 +2704,13 @@ function resolveCapabilities(capabilities) {
|
|
|
1931
2704
|
auditLogs: capabilities?.auditLogs ?? false,
|
|
1932
2705
|
architect: capabilities?.architect ?? false,
|
|
1933
2706
|
workflowsWrite: capabilities?.workflowsWrite ?? false,
|
|
1934
|
-
messages: capabilities?.messages ?? false
|
|
2707
|
+
messages: capabilities?.messages ?? false,
|
|
2708
|
+
secrets: capabilities?.secrets ?? false,
|
|
2709
|
+
restToolSources: capabilities?.restToolSources ?? false,
|
|
2710
|
+
deploy: capabilities?.deploy ?? false,
|
|
2711
|
+
traces: capabilities?.traces ?? false,
|
|
2712
|
+
evals: capabilities?.evals ?? false,
|
|
2713
|
+
observe: capabilities?.observe ?? false
|
|
1935
2714
|
};
|
|
1936
2715
|
}
|
|
1937
2716
|
function buildRoutes(capabilities) {
|
|
@@ -1953,6 +2732,9 @@ function buildRoutes(capabilities) {
|
|
|
1953
2732
|
// (powered by Gemini Live). Mints a ticket only; the host's own
|
|
1954
2733
|
// frontend opens the WebSocket directly, this runtime never proxies it.
|
|
1955
2734
|
{ method: "POST", pattern: ["voice", "sessions"], handler: createVoiceSession },
|
|
2735
|
+
// Feedback — always on: an end user rates one of their own runs
|
|
2736
|
+
// (Persona refuses ratings on anyone else's).
|
|
2737
|
+
{ method: "POST", pattern: ["feedback"], handler: createFeedback },
|
|
1956
2738
|
// Agents — read-only discovery always on; write ops behind agentsWrite.
|
|
1957
2739
|
{ method: "GET", pattern: ["agents"], handler: listAgents },
|
|
1958
2740
|
// Files — always on, end-user-scoped uploads.
|
|
@@ -2157,8 +2939,128 @@ function buildRoutes(capabilities) {
|
|
|
2157
2939
|
{ method: "GET", pattern: ["workflows", "runs", ":runId"], handler: getWorkflowRun }
|
|
2158
2940
|
);
|
|
2159
2941
|
}
|
|
2942
|
+
if (capabilities.secrets) {
|
|
2943
|
+
routes.push(
|
|
2944
|
+
{ method: "GET", pattern: ["secrets"], handler: listSecrets },
|
|
2945
|
+
{ method: "POST", pattern: ["secrets"], handler: createSecret },
|
|
2946
|
+
{ method: "POST", pattern: ["secrets", "bulk-delete"], handler: bulkDeleteSecrets },
|
|
2947
|
+
{ method: "GET", pattern: ["secrets", ":id"], handler: getSecret },
|
|
2948
|
+
{ method: "PATCH", pattern: ["secrets", ":id"], handler: updateSecret },
|
|
2949
|
+
{ method: "DELETE", pattern: ["secrets", ":id"], handler: deleteSecret },
|
|
2950
|
+
{ method: "GET", pattern: ["secrets", ":id", "usage"], handler: getSecretUsage }
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2953
|
+
if (capabilities.restToolSources) {
|
|
2954
|
+
routes.push(
|
|
2955
|
+
{ method: "GET", pattern: ["rest-tool-sources"], handler: listRestToolSources },
|
|
2956
|
+
{ method: "POST", pattern: ["rest-tool-sources"], handler: createRestToolSource },
|
|
2957
|
+
{
|
|
2958
|
+
method: "POST",
|
|
2959
|
+
pattern: ["rest-tool-sources", "bulk-delete"],
|
|
2960
|
+
handler: bulkDeleteRestToolSources
|
|
2961
|
+
},
|
|
2962
|
+
{ method: "GET", pattern: ["rest-tool-sources", ":id"], handler: getRestToolSource },
|
|
2963
|
+
{ method: "PATCH", pattern: ["rest-tool-sources", ":id"], handler: updateRestToolSource },
|
|
2964
|
+
{ method: "DELETE", pattern: ["rest-tool-sources", ":id"], handler: deleteRestToolSource },
|
|
2965
|
+
{
|
|
2966
|
+
method: "GET",
|
|
2967
|
+
pattern: ["rest-tool-sources", ":id", "usage"],
|
|
2968
|
+
handler: getRestToolSourceUsage
|
|
2969
|
+
},
|
|
2970
|
+
{ method: "POST", pattern: ["rest-tool-sources", ":id", "test"], handler: testRestToolSource }
|
|
2971
|
+
);
|
|
2972
|
+
}
|
|
2973
|
+
if (capabilities.deploy) {
|
|
2974
|
+
routes.push(
|
|
2975
|
+
{ method: "GET", pattern: ["agents", ":id", "versions"], handler: listAgentVersions },
|
|
2976
|
+
{ method: "POST", pattern: ["agents", ":id", "versions"], handler: publishAgentVersion },
|
|
2977
|
+
// Before `versions/:version`, which would otherwise capture "stats".
|
|
2978
|
+
{
|
|
2979
|
+
method: "GET",
|
|
2980
|
+
pattern: ["agents", ":id", "versions", "stats"],
|
|
2981
|
+
handler: agentVersionStats
|
|
2982
|
+
},
|
|
2983
|
+
{
|
|
2984
|
+
method: "GET",
|
|
2985
|
+
pattern: ["agents", ":id", "versions", ":version"],
|
|
2986
|
+
handler: getAgentVersion
|
|
2987
|
+
},
|
|
2988
|
+
{
|
|
2989
|
+
method: "GET",
|
|
2990
|
+
pattern: ["agents", ":id", "versions", ":version", "diff"],
|
|
2991
|
+
handler: diffAgentVersion
|
|
2992
|
+
},
|
|
2993
|
+
{ method: "GET", pattern: ["agents", ":id", "deployments"], handler: getAgentDeployments },
|
|
2994
|
+
{
|
|
2995
|
+
method: "PUT",
|
|
2996
|
+
pattern: ["agents", ":id", "deployments", "policy"],
|
|
2997
|
+
handler: setAgentServePolicy
|
|
2998
|
+
},
|
|
2999
|
+
{
|
|
3000
|
+
method: "POST",
|
|
3001
|
+
pattern: ["agents", ":id", "deployments", ":environment"],
|
|
3002
|
+
handler: deployAgentVersion
|
|
3003
|
+
},
|
|
3004
|
+
{
|
|
3005
|
+
method: "POST",
|
|
3006
|
+
pattern: ["agents", ":id", "deployments", ":environment", "rollback"],
|
|
3007
|
+
handler: rollbackAgentDeployment
|
|
3008
|
+
}
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
3011
|
+
if (capabilities.traces) {
|
|
3012
|
+
routes.push(
|
|
3013
|
+
{ method: "GET", pattern: ["traces"], handler: listTraces },
|
|
3014
|
+
{ method: "GET", pattern: ["traces", ":id"], handler: getTrace },
|
|
3015
|
+
{ method: "GET", pattern: ["traces", ":id", "feedback"], handler: getTraceFeedback },
|
|
3016
|
+
{ method: "GET", pattern: ["traces", ":id", "replay"], handler: getReplayPlan },
|
|
3017
|
+
{ method: "POST", pattern: ["traces", ":id", "replay"], handler: replayTrace },
|
|
3018
|
+
{ method: "GET", pattern: ["traces", ":id", "replays"], handler: listTraceReplays }
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
3021
|
+
if (capabilities.evals) {
|
|
3022
|
+
routes.push(
|
|
3023
|
+
{ method: "GET", pattern: ["datasets"], handler: listDatasets },
|
|
3024
|
+
{ method: "POST", pattern: ["datasets"], handler: createDataset },
|
|
3025
|
+
{ method: "GET", pattern: ["datasets", ":id"], handler: getDataset },
|
|
3026
|
+
{ method: "PATCH", pattern: ["datasets", ":id"], handler: updateDataset },
|
|
3027
|
+
{ method: "DELETE", pattern: ["datasets", ":id"], handler: deleteDataset },
|
|
3028
|
+
{ method: "GET", pattern: ["datasets", ":id", "items"], handler: listDatasetItems },
|
|
3029
|
+
{ method: "POST", pattern: ["datasets", ":id", "items"], handler: addDatasetItems },
|
|
3030
|
+
{
|
|
3031
|
+
method: "POST",
|
|
3032
|
+
pattern: ["datasets", ":id", "items", "from-trace"],
|
|
3033
|
+
handler: addDatasetItemFromTrace
|
|
3034
|
+
},
|
|
3035
|
+
{
|
|
3036
|
+
method: "PATCH",
|
|
3037
|
+
pattern: ["datasets", ":id", "items", ":itemId"],
|
|
3038
|
+
handler: updateDatasetItem
|
|
3039
|
+
},
|
|
3040
|
+
{
|
|
3041
|
+
method: "DELETE",
|
|
3042
|
+
pattern: ["datasets", ":id", "items", ":itemId"],
|
|
3043
|
+
handler: deleteDatasetItem
|
|
3044
|
+
},
|
|
3045
|
+
{ method: "GET", pattern: ["eval-runs"], handler: listEvalRuns },
|
|
3046
|
+
{ method: "POST", pattern: ["eval-runs"], handler: startEvalRun },
|
|
3047
|
+
// Before `eval-runs/:id`, which would otherwise capture "compare".
|
|
3048
|
+
{ method: "GET", pattern: ["eval-runs", "compare"], handler: compareEvalRuns },
|
|
3049
|
+
{ method: "GET", pattern: ["eval-runs", ":id"], handler: getEvalRun },
|
|
3050
|
+
{ method: "DELETE", pattern: ["eval-runs", ":id"], handler: deleteEvalRun },
|
|
3051
|
+
{ method: "GET", pattern: ["eval-runs", ":id", "results"], handler: listEvalResults },
|
|
3052
|
+
{ method: "POST", pattern: ["eval-runs", ":id", "cancel"], handler: cancelEvalRun }
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
if (capabilities.observe) {
|
|
3056
|
+
routes.push(
|
|
3057
|
+
{ method: "GET", pattern: ["observe", "budget"], handler: getBudget },
|
|
3058
|
+
{ method: "GET", pattern: ["observe", "usage"], handler: getUsage }
|
|
3059
|
+
);
|
|
3060
|
+
}
|
|
2160
3061
|
return routes;
|
|
2161
3062
|
}
|
|
3063
|
+
var ADMIN_CAPABILITIES = ["deploy", "traces", "evals", "observe"];
|
|
2162
3064
|
function resolveMode(options) {
|
|
2163
3065
|
if (options.mode) return options.mode;
|
|
2164
3066
|
return process.env.NODE_ENV === "development" ? "development" : "production";
|
|
@@ -2205,6 +3107,15 @@ function createRuntime(options) {
|
|
|
2205
3107
|
}
|
|
2206
3108
|
);
|
|
2207
3109
|
}
|
|
3110
|
+
if (options.webhooks) {
|
|
3111
|
+
if (!options.webhooks.secret) throw new Error('createRuntime: "webhooks.secret" is required');
|
|
3112
|
+
routes.push({
|
|
3113
|
+
method: "POST",
|
|
3114
|
+
pattern: ["webhooks", "persona"],
|
|
3115
|
+
handler: createWebhookRoute(),
|
|
3116
|
+
requiresAuth: false
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
2208
3119
|
const mode = resolveMode(options);
|
|
2209
3120
|
const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15e3;
|
|
2210
3121
|
const runGraceMs = options.runGraceMs ?? DEFAULT_RUN_GRACE_MS;
|
|
@@ -2223,6 +3134,18 @@ function createRuntime(options) {
|
|
|
2223
3134
|
mountPath: options.mountPath ?? "",
|
|
2224
3135
|
enabledCapabilities: enabledCaps.length ? enabledCaps : ["(core only)"]
|
|
2225
3136
|
});
|
|
3137
|
+
const adminCaps = enabledCaps.filter((c) => ADMIN_CAPABILITIES.includes(c));
|
|
3138
|
+
if (adminCaps.length) {
|
|
3139
|
+
logger.warn(
|
|
3140
|
+
"Project-wide admin capabilities are on: they act for the whole Project, not the resolved user. Serve them only behind a resolveUser that admits your own team.",
|
|
3141
|
+
{
|
|
3142
|
+
capabilities: adminCaps,
|
|
3143
|
+
scopes: Object.fromEntries(
|
|
3144
|
+
adminCaps.map((c) => [c, CAPABILITY_SCOPES[c]])
|
|
3145
|
+
)
|
|
3146
|
+
}
|
|
3147
|
+
);
|
|
3148
|
+
}
|
|
2226
3149
|
logger.trace("runtime config", {
|
|
2227
3150
|
baseUrl: options.baseUrl,
|
|
2228
3151
|
hasHooks: !!options.hooks,
|
|
@@ -2355,6 +3278,7 @@ function createRuntime(options) {
|
|
|
2355
3278
|
}
|
|
2356
3279
|
const resolvedRequest = { ...request, userId };
|
|
2357
3280
|
const client = createClientForRequest(options, userId, logger);
|
|
3281
|
+
let projectClient;
|
|
2358
3282
|
const routeLogger = logger.child("route");
|
|
2359
3283
|
logger.debug("handler start", {
|
|
2360
3284
|
method: request.method,
|
|
@@ -2373,7 +3297,15 @@ function createRuntime(options) {
|
|
|
2373
3297
|
logger: routeLogger,
|
|
2374
3298
|
restToolsManifest: options.restToolsManifest,
|
|
2375
3299
|
rcpManifest: options.rcpManifest,
|
|
2376
|
-
channels: options.channels
|
|
3300
|
+
channels: options.channels,
|
|
3301
|
+
projectClient: () => userId === null ? client : projectClient ??= createClientForRequest(options, null, logger),
|
|
3302
|
+
webhooks: options.webhooks,
|
|
3303
|
+
versioning: {
|
|
3304
|
+
selectVersion: options.selectVersion,
|
|
3305
|
+
defaultEnvironment: options.defaultEnvironment,
|
|
3306
|
+
allowClientVersionSelection: options.allowClientVersionSelection
|
|
3307
|
+
},
|
|
3308
|
+
forwardTraceparent: options.forwardTraceparent ?? true
|
|
2377
3309
|
});
|
|
2378
3310
|
const durationMs = Date.now() - startMs;
|
|
2379
3311
|
logger.info("handle succeeded", {
|
|
@@ -2434,6 +3366,7 @@ function createRuntime(options) {
|
|
|
2434
3366
|
}
|
|
2435
3367
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2436
3368
|
0 && (module.exports = {
|
|
3369
|
+
CAPABILITY_SCOPES,
|
|
2437
3370
|
RUNTIME_VERSION,
|
|
2438
3371
|
RuntimeHttpError,
|
|
2439
3372
|
createRuntime
|