@anvia/studio 1.0.0-rc.1 → 1.0.0-rc.3
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 +56 -10
- package/dist/index.d.ts +176 -116
- package/dist/index.js +1408 -1216
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/ArrowClockwise.es-CSEY3Qr6.js +2 -0
- package/dist/ui/assets/{ArrowClockwise.es-yNj_oSyD.js.map → ArrowClockwise.es-CSEY3Qr6.js.map} +1 -1
- package/dist/ui/assets/CaretDown.es-BHAPbv1R.js +2 -0
- package/dist/ui/assets/CaretDown.es-BHAPbv1R.js.map +1 -0
- package/dist/ui/assets/Check.es-BLChmggz.js +2 -0
- package/dist/ui/assets/Check.es-BLChmggz.js.map +1 -0
- package/dist/ui/assets/Wrench.es-BL6BmNv7.js +4 -0
- package/dist/ui/assets/Wrench.es-BL6BmNv7.js.map +1 -0
- package/dist/ui/assets/agents-page-DQqt45If.js +2 -0
- package/dist/ui/assets/{agents-page-ucKNS0fS.js.map → agents-page-DQqt45If.js.map} +1 -1
- package/dist/ui/assets/{badge-RJJoRC4P.js → badge-CcajoiAt.js} +1 -1
- package/dist/ui/assets/{badge-RJJoRC4P.js.map → badge-CcajoiAt.js.map} +1 -1
- package/dist/ui/assets/button-07gzSO47.js +2 -0
- package/dist/ui/assets/button-07gzSO47.js.map +1 -0
- package/dist/ui/assets/chunk-D2ECOWN5-fs3ih0yA.js +65 -0
- package/dist/ui/assets/chunk-D2ECOWN5-fs3ih0yA.js.map +1 -0
- package/dist/ui/assets/dialog-CNNQ3hoF.js +2 -0
- package/dist/ui/assets/dialog-CNNQ3hoF.js.map +1 -0
- package/dist/ui/assets/dist-CqMdRzwk.js +2 -0
- package/dist/ui/assets/dist-CqMdRzwk.js.map +1 -0
- package/dist/ui/assets/dist-DWqJwN1i.js +2 -0
- package/dist/ui/assets/dist-DWqJwN1i.js.map +1 -0
- package/dist/ui/assets/es2015-DsZq7PFO.js +42 -0
- package/dist/ui/assets/es2015-DsZq7PFO.js.map +1 -0
- package/dist/ui/assets/icon-CqkMSRbt.js +2 -0
- package/dist/ui/assets/icon-CqkMSRbt.js.map +1 -0
- package/dist/ui/assets/index-BGNC3dLD.css +1 -0
- package/dist/ui/assets/index-BLaXiUWO.js +12 -0
- package/dist/ui/assets/index-BLaXiUWO.js.map +1 -0
- package/dist/ui/assets/{input-BOUv3rgO.js → input-BiI6gsxA.js} +1 -1
- package/dist/ui/assets/{input-BOUv3rgO.js.map → input-BiI6gsxA.js.map} +1 -1
- package/dist/ui/assets/knowledge-model-S51n2rKh.js +2 -0
- package/dist/ui/assets/knowledge-model-S51n2rKh.js.map +1 -0
- package/dist/ui/assets/knowledge-page-DzRhA9qQ.js +2 -0
- package/dist/ui/assets/{knowledge-page-vwFOGhkh.js.map → knowledge-page-DzRhA9qQ.js.map} +1 -1
- package/dist/ui/assets/mcps-page-ZVngljXI.js +2 -0
- package/dist/ui/assets/{mcps-page-Bsu_-w7A.js.map → mcps-page-ZVngljXI.js.map} +1 -1
- package/dist/ui/assets/{memory-page-DJ_nq7Ip.js → memory-page-DAJk0Zof.js} +4 -4
- package/dist/ui/assets/memory-page-DAJk0Zof.js.map +1 -0
- package/dist/ui/assets/pipelines-page-8osYA129.js +8 -0
- package/dist/ui/assets/pipelines-page-8osYA129.js.map +1 -0
- package/dist/ui/assets/{renderers-5kT5HxmZ.js → renderers-BPGo4d2i.js} +3 -3
- package/dist/ui/assets/{renderers-5kT5HxmZ.js.map → renderers-BPGo4d2i.js.map} +1 -1
- package/dist/ui/assets/request-7oUPS8zY.js +4 -0
- package/dist/ui/assets/request-7oUPS8zY.js.map +1 -0
- package/dist/ui/assets/rfb-DiW3PDuT.js +6 -0
- package/dist/ui/assets/rfb-DiW3PDuT.js.map +1 -0
- package/dist/ui/assets/sandboxes-page-C6kokYwc.js +2 -0
- package/dist/ui/assets/sandboxes-page-C6kokYwc.js.map +1 -0
- package/dist/ui/assets/select-BOYfrfck.js +2 -0
- package/dist/ui/assets/select-BOYfrfck.js.map +1 -0
- package/dist/ui/assets/sessions-page-ClXzRM9K.js +2 -0
- package/dist/ui/assets/{sessions-page-BLGrHrQT.js.map → sessions-page-ClXzRM9K.js.map} +1 -1
- package/dist/ui/assets/status-page-DfuQ0fpa.js +2 -0
- package/dist/ui/assets/{status-page-D-BUKV_r.js.map → status-page-DfuQ0fpa.js.map} +1 -1
- package/dist/ui/assets/studio-CeTsQ4gf.js +2 -0
- package/dist/ui/assets/studio-CeTsQ4gf.js.map +1 -0
- package/dist/ui/assets/textarea-DESKg_ZK.js +2 -0
- package/dist/ui/assets/textarea-DESKg_ZK.js.map +1 -0
- package/dist/ui/assets/tool-runner-Cdrcs2v4.js +2 -0
- package/dist/ui/assets/{tool-runner-6-N-OOCI.js.map → tool-runner-Cdrcs2v4.js.map} +1 -1
- package/dist/ui/assets/tools-page-C865VQmM.js +2 -0
- package/dist/ui/assets/{tools-page-BEQJgFX0.js.map → tools-page-C865VQmM.js.map} +1 -1
- package/dist/ui/assets/trace-browser-D93H7Xxs.js +10 -0
- package/dist/ui/assets/trace-browser-D93H7Xxs.js.map +1 -0
- package/dist/ui/assets/transcript-item-BadzBIzE.js +3 -0
- package/dist/ui/assets/transcript-item-BadzBIzE.js.map +1 -0
- package/dist/ui/assets/with-selector-CRZWaZzC.js +2 -0
- package/dist/ui/assets/with-selector-CRZWaZzC.js.map +1 -0
- package/dist/ui/index.html +19 -3
- package/package.json +16 -11
- package/dist/ui/assets/ArrowClockwise.es-yNj_oSyD.js +0 -2
- package/dist/ui/assets/agents-page-ucKNS0fS.js +0 -2
- package/dist/ui/assets/chunk-ADH7NNCS-DM6fsnDI.js +0 -2
- package/dist/ui/assets/chunk-ADH7NNCS-DM6fsnDI.js.map +0 -1
- package/dist/ui/assets/index-D-CbFx6c.css +0 -1
- package/dist/ui/assets/index-DIV9J1_T.js +0 -56
- package/dist/ui/assets/index-DIV9J1_T.js.map +0 -1
- package/dist/ui/assets/knowledge-page-vwFOGhkh.js +0 -2
- package/dist/ui/assets/mcps-page-Bsu_-w7A.js +0 -2
- package/dist/ui/assets/memory-page-DJ_nq7Ip.js.map +0 -1
- package/dist/ui/assets/pipelines-page-BsHeSdbO.js +0 -8
- package/dist/ui/assets/pipelines-page-BsHeSdbO.js.map +0 -1
- package/dist/ui/assets/sandboxes-page-BQuQepXk.js +0 -2
- package/dist/ui/assets/sandboxes-page-BQuQepXk.js.map +0 -1
- package/dist/ui/assets/sessions-page-BLGrHrQT.js +0 -2
- package/dist/ui/assets/status-page-D-BUKV_r.js +0 -2
- package/dist/ui/assets/tool-runner-6-N-OOCI.js +0 -2
- package/dist/ui/assets/tools-page-BEQJgFX0.js +0 -2
- package/dist/ui/assets/trace-browser-DdGcJyNX.js +0 -10
- package/dist/ui/assets/trace-browser-DdGcJyNX.js.map +0 -1
- package/dist/ui/assets/transcript-item-Bbf77TZa.js +0 -3
- package/dist/ui/assets/transcript-item-Bbf77TZa.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Agent, getAgentToolState as getAgentToolState3 } from "@anvia/core/inte
|
|
|
3
3
|
import { Pipeline } from "@anvia/core/pipeline";
|
|
4
4
|
import { serve } from "@hono/node-server";
|
|
5
5
|
import { Hono as HonoApp } from "hono";
|
|
6
|
+
import { WebSocketServer } from "ws";
|
|
6
7
|
|
|
7
8
|
// src/runtime/json.ts
|
|
8
9
|
function toJsonValue(value) {
|
|
@@ -159,6 +160,20 @@ var StudioRunTraceObserver = class {
|
|
|
159
160
|
this.observations.push(parentObservation);
|
|
160
161
|
this.observations.push(...childTrace.observations(parentObservation.id));
|
|
161
162
|
},
|
|
163
|
+
suspend: (suspendArgs) => {
|
|
164
|
+
this.observations.push(
|
|
165
|
+
traceObservation({
|
|
166
|
+
kind: "tool",
|
|
167
|
+
name: args.toolName,
|
|
168
|
+
status: "suspended",
|
|
169
|
+
turn: args.turn,
|
|
170
|
+
startedAt,
|
|
171
|
+
input: parseOrString(args.args),
|
|
172
|
+
output: toJsonValue({ interaction: suspendArgs.interaction }),
|
|
173
|
+
metadata: toolMetadata(args, false)
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
},
|
|
162
177
|
error: (errorArgs) => {
|
|
163
178
|
const parentObservation = traceObservation({
|
|
164
179
|
kind: "tool",
|
|
@@ -176,9 +191,23 @@ var StudioRunTraceObserver = class {
|
|
|
176
191
|
};
|
|
177
192
|
}
|
|
178
193
|
async end(args) {
|
|
179
|
-
|
|
194
|
+
const result = {
|
|
195
|
+
runId: this.props.args.runId,
|
|
196
|
+
status: args.status,
|
|
197
|
+
text: args.text
|
|
198
|
+
};
|
|
199
|
+
if (args.resumedFrom !== void 0) result.resumedFrom = args.resumedFrom;
|
|
200
|
+
if (args.status === "completed") {
|
|
201
|
+
result.output = toJsonValue(args.output);
|
|
202
|
+
} else if (args.status === "blocked") {
|
|
203
|
+
result.stage = args.stage;
|
|
204
|
+
} else {
|
|
205
|
+
result.interaction = toJsonValue(args.interaction);
|
|
206
|
+
}
|
|
207
|
+
await this.save(args.status === "suspended" ? "suspended" : "success", {
|
|
180
208
|
endedAt: /* @__PURE__ */ new Date(),
|
|
181
|
-
output: args.output,
|
|
209
|
+
output: args.status === "completed" ? traceOutput(args.output) : args.text,
|
|
210
|
+
result,
|
|
182
211
|
usage: args.usage,
|
|
183
212
|
messages: toJsonValue(args.messages)
|
|
184
213
|
});
|
|
@@ -198,12 +227,13 @@ var StudioRunTraceObserver = class {
|
|
|
198
227
|
return;
|
|
199
228
|
}
|
|
200
229
|
const metadata = traceMetadata(this.props.args, result.messages);
|
|
230
|
+
if (result.result !== void 0) metadata.result = result.result;
|
|
201
231
|
const trace = {
|
|
202
232
|
id: this.props.id,
|
|
203
233
|
runId: this.props.args.runId,
|
|
204
234
|
sessionId,
|
|
205
235
|
status,
|
|
206
|
-
trace: this.trace,
|
|
236
|
+
trace: { observer: "studio", ...this.trace },
|
|
207
237
|
startedAt: this.startedAt.toISOString(),
|
|
208
238
|
endedAt: result.endedAt.toISOString(),
|
|
209
239
|
durationMs: durationMs(this.startedAt, result.endedAt),
|
|
@@ -282,15 +312,14 @@ var ChildAgentToolTraceAccumulator = class {
|
|
|
282
312
|
}
|
|
283
313
|
if (child.type === "tool_call" && isRecord(child.toolCall)) {
|
|
284
314
|
const toolCall = child.toolCall;
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
const callId = typeof toolCall.callId === "string" ? toolCall.callId : typeof toolCall.id === "string" ? toolCall.id : void 0;
|
|
315
|
+
const toolName = typeof toolCall.toolName === "string" ? toolCall.toolName : "tool";
|
|
316
|
+
const callId = typeof toolCall.toolCallId === "string" ? toolCall.toolCallId : typeof toolCall.callId === "string" ? toolCall.callId : void 0;
|
|
288
317
|
const start = {
|
|
289
318
|
startedAt: /* @__PURE__ */ new Date(),
|
|
290
319
|
agentId,
|
|
291
320
|
childTurn,
|
|
292
321
|
toolName,
|
|
293
|
-
input: toJsonValue(
|
|
322
|
+
input: toJsonValue("input" in toolCall ? toolCall.input : {}),
|
|
294
323
|
completed: false
|
|
295
324
|
};
|
|
296
325
|
if (agentName !== void 0) start.agentName = agentName;
|
|
@@ -443,14 +472,12 @@ function generationMetadata(args, endArgs) {
|
|
|
443
472
|
const request = args.request;
|
|
444
473
|
const response = endArgs?.response;
|
|
445
474
|
const rawResponse = isRecord(response?.rawResponse) ? response.rawResponse : void 0;
|
|
446
|
-
const
|
|
475
|
+
const modelId = args.modelInfo?.modelId ?? stringValue(rawResponse?.model) ?? "unknown";
|
|
447
476
|
const providerResponse = providerResponseSummary(rawResponse);
|
|
448
477
|
const usage = response?.usage;
|
|
449
478
|
return compactJsonObject({
|
|
450
479
|
provider: args.modelInfo?.provider,
|
|
451
|
-
|
|
452
|
-
requestedModel: request.model,
|
|
453
|
-
defaultModel: args.modelInfo?.defaultModel,
|
|
480
|
+
modelId,
|
|
454
481
|
messageId: response?.messageId,
|
|
455
482
|
usage,
|
|
456
483
|
toolCount: request.tools.length,
|
|
@@ -460,15 +487,13 @@ function generationMetadata(args, endArgs) {
|
|
|
460
487
|
temperature: request.temperature,
|
|
461
488
|
maxTokens: request.maxTokens,
|
|
462
489
|
toolChoice: request.toolChoice,
|
|
463
|
-
|
|
490
|
+
providerOptionKeys: isRecord(request.providerOptions) ? Object.keys(request.providerOptions).sort() : void 0,
|
|
464
491
|
hasOutputSchema: request.outputSchema !== void 0,
|
|
465
492
|
firstDeltaMs: endArgs?.firstDeltaMs,
|
|
466
493
|
providerResponse,
|
|
467
494
|
modelInfo: compactJsonObject({
|
|
468
495
|
provider: args.modelInfo?.provider,
|
|
469
|
-
|
|
470
|
-
requestedModel: request.model,
|
|
471
|
-
defaultModel: args.modelInfo?.defaultModel,
|
|
496
|
+
modelId,
|
|
472
497
|
capabilities: args.modelInfo?.capabilities
|
|
473
498
|
}),
|
|
474
499
|
modelCall: compactJsonObject({
|
|
@@ -494,7 +519,6 @@ function generationMetadata(args, endArgs) {
|
|
|
494
519
|
}
|
|
495
520
|
function completionRequestSummary(request) {
|
|
496
521
|
return compactJsonObject({
|
|
497
|
-
model: request.model,
|
|
498
522
|
instructions: request.instructions === void 0 ? void 0 : { present: true },
|
|
499
523
|
messageCount: request.chatHistory.length,
|
|
500
524
|
documentCount: request.documents.length,
|
|
@@ -504,7 +528,7 @@ function completionRequestSummary(request) {
|
|
|
504
528
|
temperature: request.temperature,
|
|
505
529
|
maxTokens: request.maxTokens,
|
|
506
530
|
toolChoice: request.toolChoice,
|
|
507
|
-
|
|
531
|
+
providerOptionKeys: isRecord(request.providerOptions) ? Object.keys(request.providerOptions).sort() : void 0,
|
|
508
532
|
hasOutputSchema: request.outputSchema !== void 0
|
|
509
533
|
});
|
|
510
534
|
}
|
|
@@ -541,7 +565,6 @@ function toolMetadata(args, skipped, result) {
|
|
|
541
565
|
argumentBytes: byteLength(args.args),
|
|
542
566
|
resultBytes: result === void 0 ? void 0 : byteLength(result),
|
|
543
567
|
hasCallSignature: args.toolCall.signature !== void 0,
|
|
544
|
-
hasAdditionalParams: args.toolCall.additionalParams !== void 0,
|
|
545
568
|
toolDescription: args.toolDefinition?.description,
|
|
546
569
|
parameterKeys: Object.keys(properties).sort(),
|
|
547
570
|
requiredParameterKeys: required,
|
|
@@ -559,8 +582,7 @@ function toolMetadata(args, skipped, result) {
|
|
|
559
582
|
mcpServerName: args.toolMetadata?.mcpServerName,
|
|
560
583
|
argumentBytes: byteLength(args.args),
|
|
561
584
|
resultBytes: result === void 0 ? void 0 : byteLength(result),
|
|
562
|
-
hasCallSignature: args.toolCall.signature !== void 0
|
|
563
|
-
hasAdditionalParams: args.toolCall.additionalParams !== void 0
|
|
585
|
+
hasCallSignature: args.toolCall.signature !== void 0
|
|
564
586
|
})
|
|
565
587
|
});
|
|
566
588
|
}
|
|
@@ -577,6 +599,16 @@ function parseOrString(value) {
|
|
|
577
599
|
return value;
|
|
578
600
|
}
|
|
579
601
|
}
|
|
602
|
+
function traceOutput(value) {
|
|
603
|
+
if (typeof value === "string") {
|
|
604
|
+
return value;
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
return JSON.stringify(toJsonValue(value));
|
|
608
|
+
} catch {
|
|
609
|
+
return String(value);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
580
612
|
function isRecord(value) {
|
|
581
613
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
582
614
|
}
|
|
@@ -877,17 +909,18 @@ function escapeHtml(value) {
|
|
|
877
909
|
}
|
|
878
910
|
|
|
879
911
|
// src/runtime/agent-context.ts
|
|
880
|
-
import {
|
|
912
|
+
import { isVectorContext } from "@anvia/core/agent";
|
|
881
913
|
function staticContextDocuments(agent) {
|
|
882
|
-
return agent.context.filter((input) => !
|
|
914
|
+
return agent.context.filter((input) => !isVectorContext(input));
|
|
883
915
|
}
|
|
884
|
-
function
|
|
885
|
-
return agent.context.filter(
|
|
916
|
+
function vectorContexts(agent) {
|
|
917
|
+
return agent.context.filter(isVectorContext);
|
|
886
918
|
}
|
|
887
919
|
|
|
888
920
|
// src/runtime/agent-runs.ts
|
|
889
|
-
import {
|
|
890
|
-
|
|
921
|
+
import {
|
|
922
|
+
withInternalAgentRunOptions
|
|
923
|
+
} from "@anvia/core/internal/agent";
|
|
891
924
|
|
|
892
925
|
// src/runtime/agent-utils.ts
|
|
893
926
|
import {
|
|
@@ -914,7 +947,8 @@ function serializeError(error) {
|
|
|
914
947
|
|
|
915
948
|
// src/runtime/type-guards.ts
|
|
916
949
|
import {
|
|
917
|
-
isJsonValue as isCoreJsonValue
|
|
950
|
+
isJsonValue as isCoreJsonValue,
|
|
951
|
+
isMessage
|
|
918
952
|
} from "@anvia/core/completion";
|
|
919
953
|
function isObject(value) {
|
|
920
954
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -925,26 +959,16 @@ function isJsonObject(value) {
|
|
|
925
959
|
function isJsonValue(value) {
|
|
926
960
|
return isCoreJsonValue(value);
|
|
927
961
|
}
|
|
928
|
-
function isMessageInput(value) {
|
|
929
|
-
return typeof value === "string" || isMessage(value);
|
|
930
|
-
}
|
|
931
|
-
function isMessage(value) {
|
|
932
|
-
if (!isObject(value) || typeof value.role !== "string") {
|
|
933
|
-
return false;
|
|
934
|
-
}
|
|
935
|
-
if (value.role === "system") {
|
|
936
|
-
return typeof value.content === "string";
|
|
937
|
-
}
|
|
938
|
-
if (value.role === "user" || value.role === "assistant" || value.role === "tool") {
|
|
939
|
-
return Array.isArray(value.content);
|
|
940
|
-
}
|
|
941
|
-
return false;
|
|
942
|
-
}
|
|
943
962
|
function isAgentTraceOptions(value) {
|
|
944
963
|
if (!isObject(value)) {
|
|
945
964
|
return false;
|
|
946
965
|
}
|
|
947
|
-
|
|
966
|
+
if (!Object.keys(value).every(
|
|
967
|
+
(key) => ["name", "userId", "sessionId", "metadata", "tags", "version", "traceId"].includes(key)
|
|
968
|
+
)) {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
return optionalString(value.name) && optionalString(value.userId) && optionalString(value.sessionId) && optionalString(value.version) && optionalString(value.traceId) && optionalStringArray(value.tags) && (value.metadata === void 0 || isJsonObject(value.metadata));
|
|
948
972
|
}
|
|
949
973
|
function isNonNegativeInteger(value) {
|
|
950
974
|
return Number.isInteger(value) && typeof value === "number" && value >= 0;
|
|
@@ -955,15 +979,9 @@ function isPositiveInteger(value) {
|
|
|
955
979
|
function optionalString(value) {
|
|
956
980
|
return value === void 0 || typeof value === "string";
|
|
957
981
|
}
|
|
958
|
-
function optionalBoolean(value) {
|
|
959
|
-
return value === void 0 || typeof value === "boolean";
|
|
960
|
-
}
|
|
961
982
|
function optionalStringArray(value) {
|
|
962
983
|
return value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
963
984
|
}
|
|
964
|
-
function optionalObject(value) {
|
|
965
|
-
return value === void 0 || isObject(value);
|
|
966
|
-
}
|
|
967
985
|
|
|
968
986
|
// src/runtime/http.ts
|
|
969
987
|
function errorResponse(c, status, code, message, details) {
|
|
@@ -1013,8 +1031,8 @@ function createStudioModelRegistry(config) {
|
|
|
1013
1031
|
id,
|
|
1014
1032
|
staticModels
|
|
1015
1033
|
};
|
|
1016
|
-
if (provider.
|
|
1017
|
-
runtimeProvider.
|
|
1034
|
+
if (provider.defaultModelId !== void 0) {
|
|
1035
|
+
runtimeProvider.defaultModelId = normalizeModelId(provider.defaultModelId);
|
|
1018
1036
|
}
|
|
1019
1037
|
providers.set(id, runtimeProvider);
|
|
1020
1038
|
}
|
|
@@ -1023,8 +1041,10 @@ function createStudioModelRegistry(config) {
|
|
|
1023
1041
|
agentPolicies: normalizeAgentPolicies(config.agents ?? {}),
|
|
1024
1042
|
modelCache: /* @__PURE__ */ new Map()
|
|
1025
1043
|
};
|
|
1026
|
-
if (config.
|
|
1027
|
-
Object.assign(registry, {
|
|
1044
|
+
if (config.defaultModelRef !== void 0) {
|
|
1045
|
+
Object.assign(registry, {
|
|
1046
|
+
defaultModelRef: normalizeModelRef(config.defaultModelRef)
|
|
1047
|
+
});
|
|
1028
1048
|
}
|
|
1029
1049
|
return registry;
|
|
1030
1050
|
}
|
|
@@ -1041,7 +1061,7 @@ function studioModelsConfig(registry, agents) {
|
|
|
1041
1061
|
models
|
|
1042
1062
|
};
|
|
1043
1063
|
if (provider.name !== void 0) config2.name = provider.name;
|
|
1044
|
-
if (provider.
|
|
1064
|
+
if (provider.defaultModelId !== void 0) config2.defaultModelId = provider.defaultModelId;
|
|
1045
1065
|
if (provider.metadata !== void 0) config2.metadata = provider.metadata;
|
|
1046
1066
|
return config2;
|
|
1047
1067
|
});
|
|
@@ -1053,7 +1073,7 @@ function studioModelsConfig(registry, agents) {
|
|
|
1053
1073
|
providers,
|
|
1054
1074
|
agents: agentsConfig
|
|
1055
1075
|
};
|
|
1056
|
-
if (registry.
|
|
1076
|
+
if (registry.defaultModelRef !== void 0) config.defaultModelRef = registry.defaultModelRef;
|
|
1057
1077
|
return config;
|
|
1058
1078
|
}
|
|
1059
1079
|
function registerModelRoutes(app, props) {
|
|
@@ -1067,8 +1087,8 @@ function registerModelRoutes(app, props) {
|
|
|
1067
1087
|
const response = {
|
|
1068
1088
|
providers
|
|
1069
1089
|
};
|
|
1070
|
-
if (props.registry.
|
|
1071
|
-
response.
|
|
1090
|
+
if (props.registry.defaultModelRef !== void 0) {
|
|
1091
|
+
response.defaultModelRef = props.registry.defaultModelRef;
|
|
1072
1092
|
}
|
|
1073
1093
|
return c.json(response);
|
|
1074
1094
|
});
|
|
@@ -1101,7 +1121,7 @@ function resolveStudioModel(registry, input) {
|
|
|
1101
1121
|
if (registry === void 0) {
|
|
1102
1122
|
return { warnings: [] };
|
|
1103
1123
|
}
|
|
1104
|
-
const selectedRef = normalizeOptionalModelRef(input.request.model) ?? sessionModelRef(input.sessionMetadata) ?? registry.agentPolicies.get(input.agent.id)?.
|
|
1124
|
+
const selectedRef = normalizeOptionalModelRef(input.request.model) ?? sessionModelRef(input.sessionMetadata) ?? registry.agentPolicies.get(input.agent.id)?.defaultModelRef ?? registry.defaultModelRef;
|
|
1105
1125
|
if (selectedRef === void 0) {
|
|
1106
1126
|
return { warnings: [] };
|
|
1107
1127
|
}
|
|
@@ -1113,7 +1133,7 @@ function resolveStudioModel(registry, input) {
|
|
|
1113
1133
|
}
|
|
1114
1134
|
let model = registry.modelCache.get(selectedRef);
|
|
1115
1135
|
if (model === void 0) {
|
|
1116
|
-
model = provider.createCompletionModel(modelId);
|
|
1136
|
+
model = provider.createCompletionModel({ modelId });
|
|
1117
1137
|
registry.modelCache.set(selectedRef, model);
|
|
1118
1138
|
}
|
|
1119
1139
|
const metadata = provider.staticModels.get(modelId);
|
|
@@ -1131,12 +1151,7 @@ function normalizeOptionalModelRef(ref) {
|
|
|
1131
1151
|
return ref === void 0 ? void 0 : normalizeModelRef(ref);
|
|
1132
1152
|
}
|
|
1133
1153
|
function normalizeModelRef(ref) {
|
|
1134
|
-
|
|
1135
|
-
const trimmed = ref.trim();
|
|
1136
|
-
const parsed = parseModelRef(trimmed);
|
|
1137
|
-
return `${parsed.providerId}:${parsed.modelId}`;
|
|
1138
|
-
}
|
|
1139
|
-
return `${normalizeProviderId(ref.provider)}:${normalizeModelId(ref.model)}`;
|
|
1154
|
+
return `${normalizeProviderId(ref.providerId)}:${normalizeModelId(ref.modelId)}`;
|
|
1140
1155
|
}
|
|
1141
1156
|
function parseModelRef(ref) {
|
|
1142
1157
|
const index = ref.indexOf(":");
|
|
@@ -1179,12 +1194,12 @@ async function agentModelsCatalog(registry, agent) {
|
|
|
1179
1194
|
return [];
|
|
1180
1195
|
}
|
|
1181
1196
|
});
|
|
1182
|
-
const
|
|
1197
|
+
const defaultModelRef = policy?.defaultModelRef ?? registry.defaultModelRef;
|
|
1183
1198
|
const summary = {
|
|
1184
1199
|
agentId: agent.id,
|
|
1185
1200
|
models: [...models, ...exactPolicyModels]
|
|
1186
1201
|
};
|
|
1187
|
-
if (
|
|
1202
|
+
if (defaultModelRef !== void 0) summary.defaultModelRef = defaultModelRef;
|
|
1188
1203
|
if (warnings.length > 0) summary.warnings = warnings;
|
|
1189
1204
|
return summary;
|
|
1190
1205
|
}
|
|
@@ -1222,7 +1237,7 @@ async function providerCatalog(provider) {
|
|
|
1222
1237
|
models: [...models.values()].sort((left, right) => left.ref.localeCompare(right.ref))
|
|
1223
1238
|
};
|
|
1224
1239
|
if (provider.name !== void 0) catalog.name = provider.name;
|
|
1225
|
-
if (provider.
|
|
1240
|
+
if (provider.defaultModelId !== void 0) catalog.defaultModelId = provider.defaultModelId;
|
|
1226
1241
|
if (provider.metadata !== void 0) catalog.metadata = provider.metadata;
|
|
1227
1242
|
if (warning !== void 0) catalog.warning = warning;
|
|
1228
1243
|
return catalog;
|
|
@@ -1256,19 +1271,33 @@ function normalizeAgentPolicies(policies) {
|
|
|
1256
1271
|
return new Map(
|
|
1257
1272
|
Object.entries(policies).map(([agentId, policy]) => {
|
|
1258
1273
|
const normalized = {};
|
|
1259
|
-
if (policy.
|
|
1274
|
+
if (policy.defaultModelRef !== void 0) {
|
|
1275
|
+
normalized.defaultModelRef = normalizeModelRef(policy.defaultModelRef);
|
|
1276
|
+
}
|
|
1260
1277
|
if (policy.allowed !== void 0) {
|
|
1261
|
-
normalized.allowed = policy.allowed.map(
|
|
1262
|
-
(
|
|
1263
|
-
|
|
1278
|
+
normalized.allowed = policy.allowed.map((entry) => {
|
|
1279
|
+
if (typeof entry === "string") {
|
|
1280
|
+
return normalizeWildcardModelRef(entry);
|
|
1281
|
+
}
|
|
1282
|
+
return normalizeModelRef(entry);
|
|
1283
|
+
});
|
|
1264
1284
|
}
|
|
1265
1285
|
return [agentId.trim(), normalized];
|
|
1266
1286
|
})
|
|
1267
1287
|
);
|
|
1268
1288
|
}
|
|
1289
|
+
function normalizeWildcardModelRef(ref) {
|
|
1290
|
+
const normalized = ref.trim();
|
|
1291
|
+
if (!normalized.endsWith(":*")) {
|
|
1292
|
+
throw new ModelSelectionError(`Invalid wildcard model reference: ${ref}`);
|
|
1293
|
+
}
|
|
1294
|
+
return `${normalizeProviderId(normalized.slice(0, -2))}:*`;
|
|
1295
|
+
}
|
|
1269
1296
|
function publicPolicy(policy) {
|
|
1270
1297
|
const config = {};
|
|
1271
|
-
if (policy.
|
|
1298
|
+
if (policy.defaultModelRef !== void 0) {
|
|
1299
|
+
config.defaultModelRef = policy.defaultModelRef;
|
|
1300
|
+
}
|
|
1272
1301
|
if (policy.allowed !== void 0) config.allowed = policy.allowed;
|
|
1273
1302
|
return config;
|
|
1274
1303
|
}
|
|
@@ -1299,15 +1328,13 @@ function modelWarnings(ref, model, request) {
|
|
|
1299
1328
|
function requestModalities(request) {
|
|
1300
1329
|
const modalities = /* @__PURE__ */ new Set(["text"]);
|
|
1301
1330
|
for (const message of requestMessages(request)) {
|
|
1302
|
-
if (typeof message === "string") {
|
|
1303
|
-
continue;
|
|
1304
|
-
}
|
|
1305
1331
|
if (message.role === "user" || message.role === "assistant") {
|
|
1332
|
+
if (typeof message.content === "string") continue;
|
|
1306
1333
|
for (const content of message.content) {
|
|
1307
1334
|
if (content.type === "image") {
|
|
1308
1335
|
modalities.add("image");
|
|
1309
1336
|
}
|
|
1310
|
-
if (content.type === "
|
|
1337
|
+
if (content.type === "file") {
|
|
1311
1338
|
modalities.add("document");
|
|
1312
1339
|
}
|
|
1313
1340
|
}
|
|
@@ -1316,7 +1343,7 @@ function requestModalities(request) {
|
|
|
1316
1343
|
return modalities;
|
|
1317
1344
|
}
|
|
1318
1345
|
function requestMessages(request) {
|
|
1319
|
-
return
|
|
1346
|
+
return request.type === "messages" ? request.messages : [];
|
|
1320
1347
|
}
|
|
1321
1348
|
function normalizeProviderId(id) {
|
|
1322
1349
|
const trimmed = id.trim();
|
|
@@ -1334,7 +1361,7 @@ function normalizeModelId(id) {
|
|
|
1334
1361
|
}
|
|
1335
1362
|
|
|
1336
1363
|
// src/runtime/streams.ts
|
|
1337
|
-
import {
|
|
1364
|
+
import { createClientStreamResponse, createEventStreamResponse } from "@anvia/server";
|
|
1338
1365
|
var studioJsonlHeaders = {
|
|
1339
1366
|
"cache-control": "no-cache, no-transform",
|
|
1340
1367
|
connection: "keep-alive",
|
|
@@ -1343,7 +1370,15 @@ var studioJsonlHeaders = {
|
|
|
1343
1370
|
"x-accel-buffering": "no"
|
|
1344
1371
|
};
|
|
1345
1372
|
function streamStudioJsonl(events) {
|
|
1346
|
-
return
|
|
1373
|
+
return createEventStreamResponse({
|
|
1374
|
+
events: withStudioStreamErrors(events),
|
|
1375
|
+
format: "jsonl",
|
|
1376
|
+
headers: studioJsonlHeaders
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
function streamStudioClient(options) {
|
|
1380
|
+
return createClientStreamResponse({
|
|
1381
|
+
events: options.events,
|
|
1347
1382
|
format: "jsonl",
|
|
1348
1383
|
headers: studioJsonlHeaders
|
|
1349
1384
|
});
|
|
@@ -1362,8 +1397,9 @@ function withStudioStreamErrors(events) {
|
|
|
1362
1397
|
const next = await iterator.next();
|
|
1363
1398
|
if (next.done === true) {
|
|
1364
1399
|
done = true;
|
|
1400
|
+
return next;
|
|
1365
1401
|
}
|
|
1366
|
-
return next;
|
|
1402
|
+
return { done: false, value: serializeStreamError(next.value) };
|
|
1367
1403
|
} catch (error) {
|
|
1368
1404
|
done = true;
|
|
1369
1405
|
return {
|
|
@@ -1381,6 +1417,12 @@ function withStudioStreamErrors(events) {
|
|
|
1381
1417
|
}
|
|
1382
1418
|
};
|
|
1383
1419
|
}
|
|
1420
|
+
function serializeStreamError(event) {
|
|
1421
|
+
if (typeof event !== "object" || event === null || !("type" in event) || event.type !== "error" || !("error" in event)) {
|
|
1422
|
+
return event;
|
|
1423
|
+
}
|
|
1424
|
+
return { ...event, error: serializeError(event.error) };
|
|
1425
|
+
}
|
|
1384
1426
|
function studioStreamError(error) {
|
|
1385
1427
|
return {
|
|
1386
1428
|
type: "error",
|
|
@@ -1589,6 +1631,16 @@ function isEventType(value) {
|
|
|
1589
1631
|
return value === "session_log" || value === "pipeline_log" || value === "trace";
|
|
1590
1632
|
}
|
|
1591
1633
|
|
|
1634
|
+
// src/runtime/runs.ts
|
|
1635
|
+
import {
|
|
1636
|
+
customAgentEventsToClientStream
|
|
1637
|
+
} from "@anvia/client";
|
|
1638
|
+
import { parseAgentInteractionResponse } from "@anvia/core/agent";
|
|
1639
|
+
import {
|
|
1640
|
+
isJsonValue as isJsonValue2,
|
|
1641
|
+
parseMessages
|
|
1642
|
+
} from "@anvia/core/completion";
|
|
1643
|
+
|
|
1592
1644
|
// src/runtime/transcript.ts
|
|
1593
1645
|
function renumberTranscript(entries) {
|
|
1594
1646
|
return entries.map((entry, entryId) => ({ ...entry, entryId }));
|
|
@@ -1602,7 +1654,8 @@ function transcriptFromMessages(messages) {
|
|
|
1602
1654
|
if (message.role === "user") {
|
|
1603
1655
|
const attachments = attachmentsFromMessage(message);
|
|
1604
1656
|
let textEntryAdded = false;
|
|
1605
|
-
|
|
1657
|
+
const contents2 = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
|
|
1658
|
+
for (const content of contents2) {
|
|
1606
1659
|
if (content.type === "text") {
|
|
1607
1660
|
const entry = {
|
|
1608
1661
|
entryId: transcript.length,
|
|
@@ -1616,32 +1669,36 @@ function transcriptFromMessages(messages) {
|
|
|
1616
1669
|
}
|
|
1617
1670
|
}
|
|
1618
1671
|
if (!textEntryAdded && attachments.length > 0) {
|
|
1619
|
-
|
|
1672
|
+
const entry = {
|
|
1620
1673
|
entryId: transcript.length,
|
|
1621
1674
|
kind: "message",
|
|
1622
1675
|
role: "user",
|
|
1623
1676
|
text: "",
|
|
1624
1677
|
attachments
|
|
1625
|
-
}
|
|
1678
|
+
};
|
|
1679
|
+
transcript.push(entry);
|
|
1626
1680
|
}
|
|
1627
1681
|
continue;
|
|
1628
1682
|
}
|
|
1629
1683
|
if (message.role === "tool") {
|
|
1630
1684
|
for (const content of message.content) {
|
|
1631
|
-
|
|
1685
|
+
if (content.type !== "tool-result") {
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
const entry = {
|
|
1632
1689
|
entryId: transcript.length,
|
|
1633
1690
|
kind: "tool",
|
|
1634
|
-
toolName:
|
|
1635
|
-
callId: content.callId ?? content.
|
|
1636
|
-
result: content.
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
});
|
|
1691
|
+
toolName: content.toolName,
|
|
1692
|
+
callId: content.callId ?? content.toolCallId,
|
|
1693
|
+
result: toolResultText(content.output)
|
|
1694
|
+
};
|
|
1695
|
+
if (content.output.type === "content") entry.structuredResult = content.output.value;
|
|
1696
|
+
transcript.push(entry);
|
|
1641
1697
|
}
|
|
1642
1698
|
continue;
|
|
1643
1699
|
}
|
|
1644
|
-
|
|
1700
|
+
const contents = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
|
|
1701
|
+
for (const content of contents) {
|
|
1645
1702
|
if (content.type === "text") {
|
|
1646
1703
|
appendAssistantTranscriptText(transcript, content.text);
|
|
1647
1704
|
} else if (content.type === "reasoning") {
|
|
@@ -1652,13 +1709,13 @@ function transcriptFromMessages(messages) {
|
|
|
1652
1709
|
};
|
|
1653
1710
|
if (content.id !== void 0) entry.reasoningId = content.id;
|
|
1654
1711
|
transcript.push(entry);
|
|
1655
|
-
} else if (content.type === "
|
|
1712
|
+
} else if (content.type === "tool-call") {
|
|
1656
1713
|
transcript.push({
|
|
1657
1714
|
entryId: transcript.length,
|
|
1658
1715
|
kind: "tool",
|
|
1659
|
-
toolName: content.
|
|
1660
|
-
callId: content.callId ?? content.
|
|
1661
|
-
args: formatJson(content.
|
|
1716
|
+
toolName: content.toolName,
|
|
1717
|
+
callId: content.callId ?? content.toolCallId,
|
|
1718
|
+
args: formatJson(content.input)
|
|
1662
1719
|
});
|
|
1663
1720
|
}
|
|
1664
1721
|
}
|
|
@@ -1669,28 +1726,37 @@ function attachmentsFromMessage(message) {
|
|
|
1669
1726
|
if (message.role !== "user" && message.role !== "assistant") {
|
|
1670
1727
|
return [];
|
|
1671
1728
|
}
|
|
1729
|
+
if (typeof message.content === "string") return [];
|
|
1672
1730
|
return message.content.flatMap((content) => {
|
|
1673
1731
|
if (content.type === "image") {
|
|
1674
1732
|
const attachment = { kind: "image" };
|
|
1675
|
-
if (content.
|
|
1676
|
-
attachment.data = content.
|
|
1677
|
-
attachment.mediaType = content.
|
|
1733
|
+
if (content.image.type === "data") {
|
|
1734
|
+
attachment.data = content.image.data;
|
|
1735
|
+
if (content.mediaType !== void 0) attachment.mediaType = content.mediaType;
|
|
1678
1736
|
} else {
|
|
1679
|
-
attachment.url = content.
|
|
1737
|
+
attachment.url = content.image.url;
|
|
1680
1738
|
}
|
|
1681
1739
|
return [attachment];
|
|
1682
1740
|
}
|
|
1683
|
-
if (content.type === "
|
|
1741
|
+
if (content.type === "file") {
|
|
1684
1742
|
const attachment = { kind: "document" };
|
|
1685
|
-
if (content.
|
|
1686
|
-
|
|
1687
|
-
if (content.
|
|
1688
|
-
if (content.
|
|
1743
|
+
if (content.filename !== void 0) attachment.name = content.filename;
|
|
1744
|
+
attachment.mediaType = content.mediaType;
|
|
1745
|
+
if (content.data.type === "data") attachment.data = content.data.data;
|
|
1746
|
+
if (content.data.type === "url") attachment.url = content.data.url;
|
|
1689
1747
|
return [attachment];
|
|
1690
1748
|
}
|
|
1691
1749
|
return [];
|
|
1692
1750
|
});
|
|
1693
1751
|
}
|
|
1752
|
+
function toolResultText(output) {
|
|
1753
|
+
if (output.type === "text" || output.type === "error-text") return output.value;
|
|
1754
|
+
if (output.type === "json" || output.type === "error-json") return formatJson(output.value);
|
|
1755
|
+
if (output.type === "execution-denied") return output.reason ?? "Execution denied";
|
|
1756
|
+
return output.value.map(
|
|
1757
|
+
(part) => part.type === "text" ? part.text : `[file:${part.mediaType}${part.filename === void 0 ? "" : `:${part.filename}`}]`
|
|
1758
|
+
).join("\n");
|
|
1759
|
+
}
|
|
1694
1760
|
function appendAssistantTranscriptText(transcript, text) {
|
|
1695
1761
|
const last = transcript.at(-1);
|
|
1696
1762
|
if (last?.kind === "message" && last.role === "assistant") {
|
|
@@ -1738,48 +1804,43 @@ var AsyncEventQueue = class {
|
|
|
1738
1804
|
return new Promise((resolve2) => this.resolvers.push(resolve2));
|
|
1739
1805
|
}
|
|
1740
1806
|
};
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
if (runNext !== void 0) {
|
|
1750
|
-
pending.push(runNext.then((value) => ({ source: "run", value })));
|
|
1751
|
-
}
|
|
1752
|
-
if (approvalNext !== void 0) {
|
|
1753
|
-
pending.push(approvalNext.then((value) => ({ source: "approval", value })));
|
|
1754
|
-
}
|
|
1755
|
-
const result = await Promise.race(pending);
|
|
1756
|
-
if (result.source === "run") {
|
|
1757
|
-
if (result.value.done === true) {
|
|
1758
|
-
runDone = true;
|
|
1759
|
-
runNext = void 0;
|
|
1760
|
-
approvalEvents.close();
|
|
1761
|
-
} else {
|
|
1762
|
-
runNext = runIterator.next();
|
|
1763
|
-
yield result.value.value;
|
|
1764
|
-
}
|
|
1765
|
-
continue;
|
|
1766
|
-
}
|
|
1767
|
-
if (result.value.done === true) {
|
|
1768
|
-
approvalNext = void 0;
|
|
1769
|
-
} else {
|
|
1770
|
-
approvalNext = approvalEvents.next();
|
|
1771
|
-
yield result.value.value;
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
} finally {
|
|
1775
|
-
if (!runDone && runIterator.return !== void 0) {
|
|
1776
|
-
await runIterator.return();
|
|
1777
|
-
}
|
|
1778
|
-
approvalEvents.close();
|
|
1779
|
-
}
|
|
1807
|
+
function streamAgentRunEvents(_c, events, options) {
|
|
1808
|
+
return streamStudioClient({
|
|
1809
|
+
events: customAgentEventsToClientStream({
|
|
1810
|
+
events: withAgentRunCancellation(events, options.onCancel),
|
|
1811
|
+
runId: options.runId,
|
|
1812
|
+
mapCustomEvent: studioEventToClientEvent
|
|
1813
|
+
})
|
|
1814
|
+
});
|
|
1780
1815
|
}
|
|
1781
|
-
function
|
|
1782
|
-
|
|
1816
|
+
function studioEventToClientEvent(event, context) {
|
|
1817
|
+
if (event.type === "session_log" && isJsonValue2(event.log)) {
|
|
1818
|
+
return {
|
|
1819
|
+
type: "data",
|
|
1820
|
+
runId: context.runId,
|
|
1821
|
+
name: "studio.session_log",
|
|
1822
|
+
data: event.log,
|
|
1823
|
+
transient: true
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
if (event.type === "pipeline_log" && isJsonValue2(event.log)) {
|
|
1827
|
+
return {
|
|
1828
|
+
type: "data",
|
|
1829
|
+
runId: context.runId,
|
|
1830
|
+
name: "studio.pipeline_log",
|
|
1831
|
+
data: event.log,
|
|
1832
|
+
transient: true
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
if (event.type === "pipeline_final" && isJsonValue2(event.output)) {
|
|
1836
|
+
return {
|
|
1837
|
+
type: "data",
|
|
1838
|
+
runId: context.runId,
|
|
1839
|
+
name: "studio.pipeline_final",
|
|
1840
|
+
data: event.output
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
return void 0;
|
|
1783
1844
|
}
|
|
1784
1845
|
function withAgentRunCancellation(events, onCancel) {
|
|
1785
1846
|
const iterator = events[Symbol.asyncIterator]();
|
|
@@ -1889,7 +1950,7 @@ function createPersistedStreamingSessionTranscript(props) {
|
|
|
1889
1950
|
if (event.type === "final" || event.type === "error") {
|
|
1890
1951
|
assignTranscriptRunDuration(transcript, Date.now() - props.startedAt);
|
|
1891
1952
|
}
|
|
1892
|
-
const eventStatus = event.type === "final" ? "success" : event.type === "error" ? "error" : "running";
|
|
1953
|
+
const eventStatus = event.type === "final" ? event.result.status === "suspended" ? "suspended" : "success" : event.type === "error" ? "error" : "running";
|
|
1893
1954
|
const saveInput = {
|
|
1894
1955
|
id: props.session.id,
|
|
1895
1956
|
runId: props.runId,
|
|
@@ -1899,10 +1960,10 @@ function createPersistedStreamingSessionTranscript(props) {
|
|
|
1899
1960
|
};
|
|
1900
1961
|
if (event.type === "error") saveInput.error = serializeError(event.error);
|
|
1901
1962
|
await saveTranscript(saveInput);
|
|
1902
|
-
const generatedMessages = event.type === "final" ? event.messages.slice(1) : [];
|
|
1963
|
+
const generatedMessages = event.type === "final" ? event.result.messages.slice(props.generatedMessagesStartIndex ?? 1) : [];
|
|
1903
1964
|
if (props.persistGeneratedMessages === true && generatedMessages.length > 0) {
|
|
1904
1965
|
await props.store.append({
|
|
1905
|
-
|
|
1966
|
+
scope: { sessionId: props.session.id },
|
|
1906
1967
|
runId: props.runId,
|
|
1907
1968
|
turn: 1,
|
|
1908
1969
|
messages: generatedMessages
|
|
@@ -2004,9 +2065,9 @@ function acceptTranscriptStreamEvent(transcript, event) {
|
|
|
2004
2065
|
transcript.push({
|
|
2005
2066
|
entryId: transcript.length,
|
|
2006
2067
|
kind: "tool",
|
|
2007
|
-
toolName: event.toolCall.
|
|
2008
|
-
callId: event.toolCall.callId ?? event.toolCall.
|
|
2009
|
-
args: formatJson(event.toolCall.
|
|
2068
|
+
toolName: event.toolCall.toolName,
|
|
2069
|
+
callId: event.toolCall.callId ?? event.toolCall.toolCallId,
|
|
2070
|
+
args: formatJson(event.toolCall.input)
|
|
2010
2071
|
});
|
|
2011
2072
|
}
|
|
2012
2073
|
if (event.type === "tool_result") {
|
|
@@ -2047,86 +2108,72 @@ function acceptTranscriptStreamEvent(transcript, event) {
|
|
|
2047
2108
|
}
|
|
2048
2109
|
appendChildAgentTranscriptEvent(matched, event);
|
|
2049
2110
|
}
|
|
2050
|
-
if (event.type === "
|
|
2111
|
+
if (event.type === "final" && event.result.status === "suspended") {
|
|
2112
|
+
const interaction = event.result.interaction;
|
|
2051
2113
|
const matched = findTranscriptToolEntry(
|
|
2052
2114
|
transcript,
|
|
2053
|
-
|
|
2054
|
-
|
|
2115
|
+
interaction.toolName,
|
|
2116
|
+
interaction.callId ?? interaction.toolCallId
|
|
2055
2117
|
);
|
|
2056
|
-
if (matched !== void 0) {
|
|
2057
|
-
matched.approval = {
|
|
2058
|
-
id: event.approval.id,
|
|
2059
|
-
status: event.approval.status,
|
|
2060
|
-
requestedAt: event.approval.requestedAt
|
|
2061
|
-
};
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
if (event.type === "tool_approval_result") {
|
|
2065
|
-
const matched = findTranscriptToolEntry(
|
|
2066
|
-
transcript,
|
|
2067
|
-
event.approval.toolName,
|
|
2068
|
-
approvalCallId(event.approval)
|
|
2069
|
-
);
|
|
2070
|
-
if (matched !== void 0) {
|
|
2118
|
+
if (matched !== void 0 && interaction.type === "tool-approval") {
|
|
2071
2119
|
const approval = {
|
|
2072
|
-
id:
|
|
2073
|
-
status:
|
|
2074
|
-
requestedAt:
|
|
2120
|
+
id: interaction.id,
|
|
2121
|
+
status: "pending",
|
|
2122
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2075
2123
|
};
|
|
2076
|
-
if (
|
|
2077
|
-
if (event.approval.reason !== void 0) approval.reason = event.approval.reason;
|
|
2124
|
+
if (interaction.reason !== void 0) approval.reason = interaction.reason;
|
|
2078
2125
|
matched.approval = approval;
|
|
2079
2126
|
}
|
|
2080
|
-
|
|
2081
|
-
if (event.type === "tool_question_request") {
|
|
2082
|
-
const matched = findTranscriptToolEntry(
|
|
2083
|
-
transcript,
|
|
2084
|
-
event.question.toolName,
|
|
2085
|
-
questionCallId(event.question)
|
|
2086
|
-
);
|
|
2087
|
-
if (matched !== void 0) {
|
|
2127
|
+
if (matched !== void 0 && interaction.type === "tool-question") {
|
|
2088
2128
|
matched.question = {
|
|
2089
|
-
id:
|
|
2090
|
-
status:
|
|
2091
|
-
requestedAt:
|
|
2092
|
-
questions:
|
|
2129
|
+
id: interaction.id,
|
|
2130
|
+
status: "pending",
|
|
2131
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2132
|
+
questions: interaction.questions.map((question) => ({
|
|
2133
|
+
id: question.id,
|
|
2134
|
+
question: question.text,
|
|
2135
|
+
choices: [...question.choices ?? []],
|
|
2136
|
+
allowCustom: question.choices === void 0 || question.allowCustom === true
|
|
2137
|
+
}))
|
|
2093
2138
|
};
|
|
2094
2139
|
}
|
|
2095
2140
|
}
|
|
2096
|
-
if (event.type === "
|
|
2141
|
+
if (event.type === "interaction_response") {
|
|
2142
|
+
const response = event.response;
|
|
2097
2143
|
const matched = findTranscriptToolEntry(
|
|
2098
2144
|
transcript,
|
|
2099
|
-
|
|
2100
|
-
|
|
2145
|
+
response.toolName,
|
|
2146
|
+
response.callId ?? response.toolCallId
|
|
2101
2147
|
);
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2148
|
+
const respondedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2149
|
+
if (matched?.approval !== void 0 && response.type === "tool-approval-response") {
|
|
2150
|
+
const approval = {
|
|
2151
|
+
...matched.approval,
|
|
2152
|
+
status: response.approved ? "approved" : "rejected",
|
|
2153
|
+
resolvedAt: respondedAt
|
|
2154
|
+
};
|
|
2155
|
+
if (response.reason !== void 0) approval.reason = response.reason;
|
|
2156
|
+
matched.approval = approval;
|
|
2157
|
+
}
|
|
2158
|
+
if (matched?.question !== void 0 && response.type === "tool-question-response") {
|
|
2159
|
+
matched.question = {
|
|
2160
|
+
...matched.question,
|
|
2161
|
+
status: "answered",
|
|
2162
|
+
answeredAt: respondedAt,
|
|
2163
|
+
answers: response.answers.map((answer) => ({
|
|
2164
|
+
questionId: answer.questionId,
|
|
2165
|
+
answer: answer.value
|
|
2166
|
+
}))
|
|
2108
2167
|
};
|
|
2109
|
-
if (event.question.answeredAt !== void 0) question.answeredAt = event.question.answeredAt;
|
|
2110
|
-
if (event.question.cancelledAt !== void 0) {
|
|
2111
|
-
question.cancelledAt = event.question.cancelledAt;
|
|
2112
|
-
}
|
|
2113
|
-
if (event.question.answers !== void 0) question.answers = event.question.answers;
|
|
2114
|
-
matched.question = question;
|
|
2115
2168
|
}
|
|
2116
2169
|
}
|
|
2117
|
-
if (event.type === "final" && event.trace?.traceId !== void 0) {
|
|
2118
|
-
assignTranscriptTraceId(transcript, event.trace.traceId);
|
|
2170
|
+
if (event.type === "final" && event.result.trace?.traceId !== void 0) {
|
|
2171
|
+
assignTranscriptTraceId(transcript, event.result.trace.traceId);
|
|
2119
2172
|
}
|
|
2120
2173
|
if (event.type === "error") {
|
|
2121
2174
|
appendTranscriptAssistantError(transcript, errorText(event.error));
|
|
2122
2175
|
}
|
|
2123
2176
|
}
|
|
2124
|
-
function approvalCallId(approval) {
|
|
2125
|
-
return approval.callId ?? approval.toolCallId;
|
|
2126
|
-
}
|
|
2127
|
-
function questionCallId(question) {
|
|
2128
|
-
return question.callId ?? question.toolCallId;
|
|
2129
|
-
}
|
|
2130
2177
|
function appendChildAgentTranscriptEvent(entry, event) {
|
|
2131
2178
|
const childEvent = childAgentTranscriptEvent(event);
|
|
2132
2179
|
if (childEvent === void 0) {
|
|
@@ -2187,9 +2234,9 @@ function childAgentTranscriptEvent(event) {
|
|
|
2187
2234
|
const entry = {
|
|
2188
2235
|
kind: "tool",
|
|
2189
2236
|
agentId: event.agentId,
|
|
2190
|
-
toolName: child.toolCall.
|
|
2191
|
-
callId: child.toolCall.callId ?? child.toolCall.
|
|
2192
|
-
args: formatJson(child.toolCall.
|
|
2237
|
+
toolName: child.toolCall.toolName,
|
|
2238
|
+
callId: child.toolCall.callId ?? child.toolCall.toolCallId,
|
|
2239
|
+
args: formatJson(child.toolCall.input)
|
|
2193
2240
|
};
|
|
2194
2241
|
if (event.agentName !== void 0) entry.agentName = event.agentName;
|
|
2195
2242
|
return entry;
|
|
@@ -2326,17 +2373,18 @@ function extractMessageText(message) {
|
|
|
2326
2373
|
if (message.role === "system") {
|
|
2327
2374
|
return message.content;
|
|
2328
2375
|
}
|
|
2376
|
+
if (typeof message.content === "string") {
|
|
2377
|
+
return message.content;
|
|
2378
|
+
}
|
|
2329
2379
|
return message.content.flatMap((item) => {
|
|
2330
2380
|
if (item.type === "text" || item.type === "reasoning") {
|
|
2331
2381
|
return [item.text];
|
|
2332
2382
|
}
|
|
2333
|
-
if (item.type === "
|
|
2334
|
-
return [`${item.
|
|
2383
|
+
if (item.type === "tool-call") {
|
|
2384
|
+
return [`${item.toolName}(${formatJson(item.input)})`];
|
|
2335
2385
|
}
|
|
2336
|
-
if (item.type === "
|
|
2337
|
-
return item.
|
|
2338
|
-
(result) => "text" in result ? result.text : `[image:${result.mediaType ?? "image/png"}]`
|
|
2339
|
-
);
|
|
2386
|
+
if (item.type === "tool-result") {
|
|
2387
|
+
return [toolResultOutputText(item.output)];
|
|
2340
2388
|
}
|
|
2341
2389
|
return [];
|
|
2342
2390
|
}).join("\n");
|
|
@@ -2345,23 +2393,26 @@ function optionalTranscriptAttachments(message) {
|
|
|
2345
2393
|
if (typeof message === "string" || message.role !== "user") {
|
|
2346
2394
|
return {};
|
|
2347
2395
|
}
|
|
2396
|
+
if (typeof message.content === "string") {
|
|
2397
|
+
return {};
|
|
2398
|
+
}
|
|
2348
2399
|
const attachments = message.content.flatMap((content) => {
|
|
2349
2400
|
if (content.type === "image") {
|
|
2350
2401
|
const attachment = { kind: "image" };
|
|
2351
|
-
if (content.
|
|
2352
|
-
attachment.data = content.
|
|
2353
|
-
attachment.mediaType = content.
|
|
2402
|
+
if (content.image.type === "data") {
|
|
2403
|
+
attachment.data = content.image.data;
|
|
2404
|
+
if (content.mediaType !== void 0) attachment.mediaType = content.mediaType;
|
|
2354
2405
|
} else {
|
|
2355
|
-
attachment.url = content.
|
|
2406
|
+
attachment.url = content.image.url;
|
|
2356
2407
|
}
|
|
2357
2408
|
return [attachment];
|
|
2358
2409
|
}
|
|
2359
|
-
if (content.type === "
|
|
2410
|
+
if (content.type === "file") {
|
|
2360
2411
|
const attachment = { kind: "document" };
|
|
2361
|
-
if (content.
|
|
2362
|
-
|
|
2363
|
-
if (content.
|
|
2364
|
-
if (content.
|
|
2412
|
+
if (content.filename !== void 0) attachment.name = content.filename;
|
|
2413
|
+
attachment.mediaType = content.mediaType;
|
|
2414
|
+
if (content.data.type === "data") attachment.data = content.data.data;
|
|
2415
|
+
if (content.data.type === "url") attachment.url = content.data.url;
|
|
2365
2416
|
return [attachment];
|
|
2366
2417
|
}
|
|
2367
2418
|
return [];
|
|
@@ -2378,32 +2429,63 @@ async function parseRunRequest(c) {
|
|
|
2378
2429
|
if (!isObject(body)) {
|
|
2379
2430
|
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
2380
2431
|
}
|
|
2381
|
-
|
|
2432
|
+
if ("message" in body || "history" in body) {
|
|
2433
|
+
return {
|
|
2434
|
+
error: errorResponse(
|
|
2435
|
+
c,
|
|
2436
|
+
400,
|
|
2437
|
+
"bad_request",
|
|
2438
|
+
"Legacy message/history requests are not supported; use messages"
|
|
2439
|
+
)
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
const request = parseRunRequestBody(c, body);
|
|
2382
2443
|
if ("error" in request) {
|
|
2383
2444
|
return request;
|
|
2384
2445
|
}
|
|
2385
2446
|
return parseRunRequestOptions(c, body, request);
|
|
2386
2447
|
}
|
|
2387
|
-
function
|
|
2388
|
-
if (
|
|
2448
|
+
function parseRunRequestBody(c, body) {
|
|
2449
|
+
if (body.type === "interaction_response") {
|
|
2450
|
+
if (typeof body.interactionId !== "string" || body.interactionId.trim().length === 0) {
|
|
2451
|
+
return { error: errorResponse(c, 400, "bad_request", "interactionId must be a string") };
|
|
2452
|
+
}
|
|
2453
|
+
if (body.messages !== void 0 || body.sessionId !== void 0 || body.model !== void 0) {
|
|
2454
|
+
return {
|
|
2455
|
+
error: errorResponse(
|
|
2456
|
+
c,
|
|
2457
|
+
400,
|
|
2458
|
+
"bad_request",
|
|
2459
|
+
"Interaction responses cannot include messages, sessionId, or model"
|
|
2460
|
+
)
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
try {
|
|
2464
|
+
return {
|
|
2465
|
+
type: "interaction_response",
|
|
2466
|
+
interactionId: body.interactionId,
|
|
2467
|
+
response: parseAgentInteractionResponse(body.response)
|
|
2468
|
+
};
|
|
2469
|
+
} catch {
|
|
2470
|
+
return {
|
|
2471
|
+
error: errorResponse(c, 400, "bad_request", "response must be an interaction response")
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
if (body.type !== "messages") {
|
|
2389
2476
|
return {
|
|
2390
|
-
error: errorResponse(
|
|
2477
|
+
error: errorResponse(
|
|
2478
|
+
c,
|
|
2479
|
+
400,
|
|
2480
|
+
"bad_request",
|
|
2481
|
+
'type must be "messages" or "interaction_response"'
|
|
2482
|
+
)
|
|
2391
2483
|
};
|
|
2392
2484
|
}
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
if (!Array.isArray(body.history) || !body.history.every(isMessage)) {
|
|
2398
|
-
return { error: errorResponse(c, 400, "bad_request", "history must be a Message array") };
|
|
2399
|
-
}
|
|
2400
|
-
request.history = body.history;
|
|
2401
|
-
}
|
|
2402
|
-
return request;
|
|
2403
|
-
}
|
|
2404
|
-
function parseUiRunRequestBody(c, body) {
|
|
2405
|
-
const messages = body.messages;
|
|
2406
|
-
if (!Array.isArray(messages) || !messages.every(isMessage)) {
|
|
2485
|
+
let messages;
|
|
2486
|
+
try {
|
|
2487
|
+
messages = parseMessages(body.messages);
|
|
2488
|
+
} catch {
|
|
2407
2489
|
return {
|
|
2408
2490
|
error: errorResponse(c, 400, "bad_request", "messages must be a non-empty Message array")
|
|
2409
2491
|
};
|
|
@@ -2419,19 +2501,21 @@ function parseUiRunRequestBody(c, body) {
|
|
|
2419
2501
|
error: errorResponse(c, 400, "bad_request", "messages must be a non-empty Message array")
|
|
2420
2502
|
};
|
|
2421
2503
|
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2504
|
+
if (message.role !== "user") {
|
|
2505
|
+
return {
|
|
2506
|
+
error: errorResponse(c, 400, "bad_request", "The final message must be user-authored")
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
return { type: "messages", messages };
|
|
2426
2510
|
}
|
|
2427
2511
|
function parseRunRequestOptions(c, body, request) {
|
|
2428
|
-
if ("sessionId" in body) {
|
|
2512
|
+
if (request.type === "messages" && "sessionId" in body) {
|
|
2429
2513
|
if (typeof body.sessionId !== "string" || body.sessionId.trim().length === 0) {
|
|
2430
2514
|
return { error: errorResponse(c, 400, "bad_request", "sessionId must be a string") };
|
|
2431
2515
|
}
|
|
2432
|
-
if (request.
|
|
2516
|
+
if (request.messages.length !== 1 || request.messages[0]?.role !== "user") {
|
|
2433
2517
|
return {
|
|
2434
|
-
error: errorResponse(c, 400, "bad_request", "sessionId
|
|
2518
|
+
error: errorResponse(c, 400, "bad_request", "sessionId requires exactly one user message")
|
|
2435
2519
|
};
|
|
2436
2520
|
}
|
|
2437
2521
|
request.sessionId = body.sessionId;
|
|
@@ -2442,7 +2526,7 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2442
2526
|
}
|
|
2443
2527
|
request.stream = body.stream;
|
|
2444
2528
|
}
|
|
2445
|
-
if ("maxTurns" in body) {
|
|
2529
|
+
if (request.type === "messages" && "maxTurns" in body) {
|
|
2446
2530
|
if (!isNonNegativeInteger(body.maxTurns)) {
|
|
2447
2531
|
return {
|
|
2448
2532
|
error: errorResponse(c, 400, "bad_request", "maxTurns must be a non-negative integer")
|
|
@@ -2450,7 +2534,7 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2450
2534
|
}
|
|
2451
2535
|
request.maxTurns = body.maxTurns;
|
|
2452
2536
|
}
|
|
2453
|
-
if ("toolConcurrency" in body) {
|
|
2537
|
+
if (request.type === "messages" && "toolConcurrency" in body) {
|
|
2454
2538
|
if (!isPositiveInteger(body.toolConcurrency)) {
|
|
2455
2539
|
return {
|
|
2456
2540
|
error: errorResponse(c, 400, "bad_request", "toolConcurrency must be a positive integer")
|
|
@@ -2458,13 +2542,11 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2458
2542
|
}
|
|
2459
2543
|
request.toolConcurrency = body.toolConcurrency;
|
|
2460
2544
|
}
|
|
2461
|
-
if ("model" in body) {
|
|
2462
|
-
if (typeof body.model === "string") {
|
|
2463
|
-
request.model = body.model;
|
|
2464
|
-
} else if (isObject(body.model) && typeof body.model.provider === "string" && typeof body.model.model === "string") {
|
|
2545
|
+
if (request.type === "messages" && "model" in body) {
|
|
2546
|
+
if (isObject(body.model) && typeof body.model.providerId === "string" && typeof body.model.modelId === "string") {
|
|
2465
2547
|
request.model = {
|
|
2466
|
-
|
|
2467
|
-
|
|
2548
|
+
providerId: body.model.providerId,
|
|
2549
|
+
modelId: body.model.modelId
|
|
2468
2550
|
};
|
|
2469
2551
|
} else {
|
|
2470
2552
|
return {
|
|
@@ -2472,7 +2554,7 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2472
2554
|
c,
|
|
2473
2555
|
400,
|
|
2474
2556
|
"bad_request",
|
|
2475
|
-
"model must be a
|
|
2557
|
+
"model must be a { providerId, modelId } object"
|
|
2476
2558
|
)
|
|
2477
2559
|
};
|
|
2478
2560
|
}
|
|
@@ -2493,6 +2575,14 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2493
2575
|
}
|
|
2494
2576
|
return request;
|
|
2495
2577
|
}
|
|
2578
|
+
function toolResultOutputText(output) {
|
|
2579
|
+
if (output.type === "text" || output.type === "error-text") return output.value;
|
|
2580
|
+
if (output.type === "json" || output.type === "error-json") return formatJson(output.value);
|
|
2581
|
+
if (output.type === "execution-denied") return output.reason ?? "Execution denied";
|
|
2582
|
+
return output.value.map(
|
|
2583
|
+
(part) => part.type === "text" ? part.text : `[file:${part.mediaType}${part.filename === void 0 ? "" : `:${part.filename}`}]`
|
|
2584
|
+
).join("\n");
|
|
2585
|
+
}
|
|
2496
2586
|
|
|
2497
2587
|
// src/runtime/session-logs.ts
|
|
2498
2588
|
async function appendSessionLog(store, input) {
|
|
@@ -2627,6 +2717,25 @@ function memorySavedLog(props) {
|
|
|
2627
2717
|
metadata
|
|
2628
2718
|
};
|
|
2629
2719
|
}
|
|
2720
|
+
function memoryCompactedLog(props) {
|
|
2721
|
+
const metadata = {
|
|
2722
|
+
originalMessageCount: props.originalMessageCount,
|
|
2723
|
+
compactedMessageCount: props.compactedMessageCount,
|
|
2724
|
+
retainedMessageCount: props.retainedMessageCount,
|
|
2725
|
+
attempts: props.attempts
|
|
2726
|
+
};
|
|
2727
|
+
const usage = usageSummary(props.usage);
|
|
2728
|
+
if (usage !== void 0) metadata.usage = usage;
|
|
2729
|
+
return {
|
|
2730
|
+
sessionId: props.sessionId,
|
|
2731
|
+
runId: props.runId,
|
|
2732
|
+
level: "info",
|
|
2733
|
+
category: "memory",
|
|
2734
|
+
event: "memory.compacted",
|
|
2735
|
+
message: "Session memory compacted",
|
|
2736
|
+
metadata
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2630
2739
|
function runFailedLog(sessionId, runId, error, startedAt) {
|
|
2631
2740
|
return {
|
|
2632
2741
|
sessionId,
|
|
@@ -2643,6 +2752,9 @@ function runFailedLog(sessionId, runId, error, startedAt) {
|
|
|
2643
2752
|
}
|
|
2644
2753
|
function logsFromStreamEvent(props) {
|
|
2645
2754
|
const { event, sessionId, runId } = props;
|
|
2755
|
+
if (event.type === "memory_compaction") {
|
|
2756
|
+
return [memoryCompactedLog({ sessionId, runId, ...event })];
|
|
2757
|
+
}
|
|
2646
2758
|
if (event.type === "turn_start") {
|
|
2647
2759
|
return [
|
|
2648
2760
|
{
|
|
@@ -2668,12 +2780,12 @@ function logsFromStreamEvent(props) {
|
|
|
2668
2780
|
level: "info",
|
|
2669
2781
|
category: "tool",
|
|
2670
2782
|
event: "tool.called",
|
|
2671
|
-
message: `Tool ${event.toolCall.
|
|
2783
|
+
message: `Tool ${event.toolCall.toolName} called`,
|
|
2672
2784
|
metadata: {
|
|
2673
2785
|
turn: event.turn,
|
|
2674
|
-
toolName: event.toolCall.
|
|
2675
|
-
callId: event.toolCall.callId ?? event.toolCall.
|
|
2676
|
-
argumentBytes: byteLength2(formatUnknown(event.toolCall.
|
|
2786
|
+
toolName: event.toolCall.toolName,
|
|
2787
|
+
callId: event.toolCall.callId ?? event.toolCall.toolCallId,
|
|
2788
|
+
argumentBytes: byteLength2(formatUnknown(event.toolCall.input))
|
|
2677
2789
|
}
|
|
2678
2790
|
}
|
|
2679
2791
|
];
|
|
@@ -2722,102 +2834,79 @@ function logsFromStreamEvent(props) {
|
|
|
2722
2834
|
];
|
|
2723
2835
|
}
|
|
2724
2836
|
if (event.type === "final") {
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
durationMs: Date.now() - props.startedAt,
|
|
2730
|
-
usage: event.usage,
|
|
2731
|
-
output: event.output,
|
|
2732
|
-
messageCount: event.messages.length
|
|
2733
|
-
}),
|
|
2734
|
-
memorySavedLog({ sessionId, runId, messageCount: event.messages.length })
|
|
2735
|
-
];
|
|
2736
|
-
}
|
|
2737
|
-
if (event.type === "error") {
|
|
2738
|
-
return [runFailedLog(sessionId, runId, event.error, props.startedAt)];
|
|
2739
|
-
}
|
|
2740
|
-
if (event.type === "tool_approval_request") {
|
|
2741
|
-
const metadata = {
|
|
2742
|
-
approvalId: event.approval.id,
|
|
2743
|
-
toolName: event.approval.toolName,
|
|
2744
|
-
status: event.approval.status,
|
|
2745
|
-
hasReason: event.approval.reason !== void 0,
|
|
2746
|
-
argumentBytes: byteLength2(event.approval.args)
|
|
2747
|
-
};
|
|
2748
|
-
if (event.approval.callId !== void 0) metadata.callId = event.approval.callId;
|
|
2749
|
-
return [
|
|
2750
|
-
{
|
|
2837
|
+
const result = event.result;
|
|
2838
|
+
const logs = [];
|
|
2839
|
+
if (result.status === "suspended") {
|
|
2840
|
+
logs.push({
|
|
2751
2841
|
sessionId,
|
|
2752
2842
|
runId,
|
|
2753
2843
|
level: "info",
|
|
2754
|
-
category: "
|
|
2755
|
-
event: "
|
|
2756
|
-
message: `
|
|
2757
|
-
metadata
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
}
|
|
2779
|
-
];
|
|
2780
|
-
}
|
|
2781
|
-
if (event.type === "tool_question_request") {
|
|
2782
|
-
const metadata = {
|
|
2783
|
-
questionId: event.question.id,
|
|
2784
|
-
toolName: event.question.toolName,
|
|
2785
|
-
status: event.question.status,
|
|
2786
|
-
questionCount: event.question.questions.length,
|
|
2787
|
-
argumentBytes: byteLength2(event.question.args)
|
|
2788
|
-
};
|
|
2789
|
-
if (event.question.callId !== void 0) metadata.callId = event.question.callId;
|
|
2790
|
-
return [
|
|
2791
|
-
{
|
|
2844
|
+
category: "run",
|
|
2845
|
+
event: "run.suspended",
|
|
2846
|
+
message: `Run suspended for ${result.interaction.type}`,
|
|
2847
|
+
metadata: {
|
|
2848
|
+
interactionId: result.interaction.id,
|
|
2849
|
+
interactionType: result.interaction.type,
|
|
2850
|
+
toolName: result.interaction.toolName,
|
|
2851
|
+
sourceRunId: result.continuation.sourceRunId
|
|
2852
|
+
}
|
|
2853
|
+
});
|
|
2854
|
+
} else {
|
|
2855
|
+
logs.push(
|
|
2856
|
+
runCompletedLog({
|
|
2857
|
+
sessionId,
|
|
2858
|
+
runId,
|
|
2859
|
+
durationMs: Date.now() - props.startedAt,
|
|
2860
|
+
usage: result.usage,
|
|
2861
|
+
output: result.text,
|
|
2862
|
+
messageCount: result.messages.length
|
|
2863
|
+
})
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
if (result.resumedFrom !== void 0) {
|
|
2867
|
+
logs.push({
|
|
2792
2868
|
sessionId,
|
|
2793
2869
|
runId,
|
|
2794
2870
|
level: "info",
|
|
2795
|
-
category: "
|
|
2796
|
-
event: "
|
|
2797
|
-
message:
|
|
2798
|
-
metadata
|
|
2799
|
-
|
|
2800
|
-
|
|
2871
|
+
category: "run",
|
|
2872
|
+
event: "interaction.resumed",
|
|
2873
|
+
message: "Interaction resumed in a linked run",
|
|
2874
|
+
metadata: {
|
|
2875
|
+
interactionId: result.resumedFrom.interactionId,
|
|
2876
|
+
sourceRunId: result.resumedFrom.runId,
|
|
2877
|
+
resumedRunId: result.runId
|
|
2878
|
+
}
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
logs.push(memorySavedLog({ sessionId, runId, messageCount: result.messages.length }));
|
|
2882
|
+
return logs;
|
|
2801
2883
|
}
|
|
2802
|
-
if (event.type === "
|
|
2803
|
-
|
|
2884
|
+
if (event.type === "error") {
|
|
2885
|
+
return [runFailedLog(sessionId, runId, event.error, props.startedAt)];
|
|
2886
|
+
}
|
|
2887
|
+
if (event.type === "interaction_response") {
|
|
2888
|
+
const response = event.response;
|
|
2804
2889
|
const metadata = {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
argumentBytes: byteLength2(event.question.args),
|
|
2810
|
-
answerCount: event.question.answers?.length ?? 0
|
|
2890
|
+
interactionId: response.interactionId,
|
|
2891
|
+
interactionType: response.type === "tool-approval-response" ? "tool-approval" : "tool-question",
|
|
2892
|
+
toolName: response.toolName,
|
|
2893
|
+
sourceRunId: event.sourceRunId
|
|
2811
2894
|
};
|
|
2812
|
-
if (
|
|
2895
|
+
if (response.callId !== void 0) metadata.callId = response.callId;
|
|
2896
|
+
if (response.type === "tool-approval-response") {
|
|
2897
|
+
metadata.approved = response.approved;
|
|
2898
|
+
metadata.hasReason = response.reason !== void 0;
|
|
2899
|
+
} else {
|
|
2900
|
+
metadata.answerCount = response.answers.length;
|
|
2901
|
+
}
|
|
2813
2902
|
return [
|
|
2814
2903
|
{
|
|
2815
2904
|
sessionId,
|
|
2816
2905
|
runId,
|
|
2817
2906
|
level: "info",
|
|
2818
|
-
category: "
|
|
2819
|
-
event:
|
|
2820
|
-
message:
|
|
2907
|
+
category: "run",
|
|
2908
|
+
event: "interaction.responded",
|
|
2909
|
+
message: `Interaction response received for ${response.toolName}`,
|
|
2821
2910
|
metadata
|
|
2822
2911
|
}
|
|
2823
2912
|
];
|
|
@@ -2843,16 +2932,16 @@ function childAgentLog(event, sessionId, runId) {
|
|
|
2843
2932
|
level: "debug",
|
|
2844
2933
|
category: "tool",
|
|
2845
2934
|
event: "child_tool.called",
|
|
2846
|
-
message: `Child agent ${event.agentName ?? event.agentId} called ${child.toolCall.
|
|
2935
|
+
message: `Child agent ${event.agentName ?? event.agentId} called ${child.toolCall.toolName}`,
|
|
2847
2936
|
metadata: {
|
|
2848
2937
|
parentToolName: event.toolName,
|
|
2849
2938
|
agentId: event.agentId,
|
|
2850
2939
|
hasAgentName: event.agentName !== void 0,
|
|
2851
2940
|
turn: event.turn,
|
|
2852
2941
|
childTurn: child.turn,
|
|
2853
|
-
toolName: child.toolCall.
|
|
2854
|
-
callId: child.toolCall.callId ?? child.toolCall.
|
|
2855
|
-
argumentBytes: byteLength2(formatUnknown(child.toolCall.
|
|
2942
|
+
toolName: child.toolCall.toolName,
|
|
2943
|
+
callId: child.toolCall.callId ?? child.toolCall.toolCallId,
|
|
2944
|
+
argumentBytes: byteLength2(formatUnknown(child.toolCall.input))
|
|
2856
2945
|
}
|
|
2857
2946
|
}
|
|
2858
2947
|
];
|
|
@@ -2903,14 +2992,16 @@ function childAgentLog(event, sessionId, runId) {
|
|
|
2903
2992
|
];
|
|
2904
2993
|
}
|
|
2905
2994
|
if (child.type === "final") {
|
|
2995
|
+
const result = child.result;
|
|
2906
2996
|
const metadata = {
|
|
2907
2997
|
parentToolName: event.toolName,
|
|
2908
2998
|
agentId: event.agentId,
|
|
2909
2999
|
hasAgentName: event.agentName !== void 0,
|
|
2910
|
-
outputBytes: byteLength2(
|
|
2911
|
-
messageCount:
|
|
3000
|
+
outputBytes: byteLength2(result.text),
|
|
3001
|
+
messageCount: result.messages.length,
|
|
3002
|
+
status: result.status
|
|
2912
3003
|
};
|
|
2913
|
-
const usage = usageSummary(
|
|
3004
|
+
const usage = usageSummary(result.usage);
|
|
2914
3005
|
if (usage !== void 0) metadata.usage = usage;
|
|
2915
3006
|
return [
|
|
2916
3007
|
{
|
|
@@ -3010,6 +3101,56 @@ async function prepareAgentRun(c, props) {
|
|
|
3010
3101
|
if ("error" in body) {
|
|
3011
3102
|
return body.error;
|
|
3012
3103
|
}
|
|
3104
|
+
if (body.type === "interaction_response") {
|
|
3105
|
+
const registration = props.continuationRegistry.take(
|
|
3106
|
+
body.interactionId,
|
|
3107
|
+
agentId,
|
|
3108
|
+
body.response
|
|
3109
|
+
);
|
|
3110
|
+
if (registration.status === "missing") {
|
|
3111
|
+
return errorResponse(c, 404, "not_found", "Interaction continuation is unavailable");
|
|
3112
|
+
}
|
|
3113
|
+
if (registration.status === "claimed") {
|
|
3114
|
+
return errorResponse(c, 409, "conflict", "Interaction response was already claimed");
|
|
3115
|
+
}
|
|
3116
|
+
if (registration.status === "invalid") {
|
|
3117
|
+
return errorResponse(c, 400, "bad_request", registration.error.message);
|
|
3118
|
+
}
|
|
3119
|
+
const { registration: claimed } = registration;
|
|
3120
|
+
const source = claimed.context;
|
|
3121
|
+
const runId2 = globalThis.crypto.randomUUID();
|
|
3122
|
+
let resumedBody = {
|
|
3123
|
+
...source.body
|
|
3124
|
+
};
|
|
3125
|
+
if (body.stream !== void 0) resumedBody = { ...resumedBody, stream: body.stream };
|
|
3126
|
+
if (body.metadata !== void 0) resumedBody = { ...resumedBody, metadata: body.metadata };
|
|
3127
|
+
if (body.trace !== void 0) resumedBody = { ...resumedBody, trace: body.trace };
|
|
3128
|
+
const runOptions = createRunOptions(resumedBody, agentId, source.session, c.req.raw.signal);
|
|
3129
|
+
const execution2 = {
|
|
3130
|
+
generate: (options) => source.runAgent.generate({
|
|
3131
|
+
continuation: claimed.continuation,
|
|
3132
|
+
response: body.response,
|
|
3133
|
+
...options
|
|
3134
|
+
}),
|
|
3135
|
+
stream: (options) => source.runAgent.stream({
|
|
3136
|
+
continuation: claimed.continuation,
|
|
3137
|
+
response: body.response,
|
|
3138
|
+
...options
|
|
3139
|
+
})
|
|
3140
|
+
};
|
|
3141
|
+
return {
|
|
3142
|
+
...source,
|
|
3143
|
+
body: resumedBody,
|
|
3144
|
+
execution: execution2,
|
|
3145
|
+
failureMessages: void 0,
|
|
3146
|
+
memoryCompactionLogged: false,
|
|
3147
|
+
options: runOptions,
|
|
3148
|
+
runId: runId2,
|
|
3149
|
+
runStartedAt: Date.now(),
|
|
3150
|
+
generatedMessagesStartIndex: 0,
|
|
3151
|
+
shouldPersistSessionMessages: source.shouldPersistSessionMessages
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3013
3154
|
const session = await resolveRunSession(c, body, agentId, props.stores);
|
|
3014
3155
|
if (session instanceof Response) {
|
|
3015
3156
|
return session;
|
|
@@ -3036,12 +3177,12 @@ async function prepareAgentRun(c, props) {
|
|
|
3036
3177
|
store: props.stores.sessions
|
|
3037
3178
|
});
|
|
3038
3179
|
const memoryMetadata = runMemoryMetadata(agentId, body, selectedModel, runId);
|
|
3039
|
-
const promptMessage =
|
|
3180
|
+
const promptMessage = body.messages.at(-1);
|
|
3040
3181
|
const sessionStore = props.stores.sessions;
|
|
3041
3182
|
const shouldPersistSessionMessages = session !== void 0 && sessionStore !== void 0 && !usesStoreAsAgentMemory(runAgent, sessionStore);
|
|
3042
3183
|
if (shouldPersistSessionMessages) {
|
|
3043
3184
|
await sessionStore.append({
|
|
3044
|
-
|
|
3185
|
+
scope: { sessionId: session.id, metadata: memoryMetadata },
|
|
3045
3186
|
runId,
|
|
3046
3187
|
turn: 1,
|
|
3047
3188
|
messages: [promptMessage]
|
|
@@ -3061,10 +3202,13 @@ async function prepareAgentRun(c, props) {
|
|
|
3061
3202
|
body,
|
|
3062
3203
|
memoryMetadata,
|
|
3063
3204
|
execution,
|
|
3064
|
-
|
|
3205
|
+
failureMessages: void 0,
|
|
3206
|
+
memoryCompactionLogged: false,
|
|
3207
|
+
options: createRunOptions(body, agentId, session, c.req.raw.signal),
|
|
3065
3208
|
runAgent,
|
|
3066
3209
|
runId,
|
|
3067
3210
|
runStartedAt,
|
|
3211
|
+
generatedMessagesStartIndex: 1,
|
|
3068
3212
|
selectedModel,
|
|
3069
3213
|
session,
|
|
3070
3214
|
sessionStore,
|
|
@@ -3106,7 +3250,7 @@ async function recordRunReceived(props) {
|
|
|
3106
3250
|
sessionId: props.session.id,
|
|
3107
3251
|
runId: props.runId,
|
|
3108
3252
|
agentId: props.agentId,
|
|
3109
|
-
message: props.body.
|
|
3253
|
+
message: props.body.messages.at(-1),
|
|
3110
3254
|
stream: props.body.stream === true,
|
|
3111
3255
|
hasTrace: props.body.trace !== void 0
|
|
3112
3256
|
};
|
|
@@ -3154,23 +3298,33 @@ function runMemoryMetadata(agentId, body, selectedModel, runId) {
|
|
|
3154
3298
|
return metadata;
|
|
3155
3299
|
}
|
|
3156
3300
|
function createRunExecution(props) {
|
|
3157
|
-
|
|
3158
|
-
|
|
3301
|
+
const session = props.session;
|
|
3302
|
+
if (session !== void 0 && props.runAgent.memory !== void 0) {
|
|
3159
3303
|
return {
|
|
3160
|
-
generate: (options) =>
|
|
3161
|
-
|
|
3304
|
+
generate: (options) => props.runAgent.generate({
|
|
3305
|
+
prompt: props.promptMessage,
|
|
3306
|
+
session: { sessionId: session.id, metadata: props.memoryMetadata },
|
|
3307
|
+
...options
|
|
3308
|
+
}),
|
|
3309
|
+
stream: (options) => props.runAgent.stream({
|
|
3310
|
+
prompt: props.promptMessage,
|
|
3311
|
+
session: { sessionId: session.id, metadata: props.memoryMetadata },
|
|
3312
|
+
...options
|
|
3313
|
+
})
|
|
3162
3314
|
};
|
|
3163
3315
|
}
|
|
3164
|
-
const
|
|
3316
|
+
const transcript = session !== void 0 ? [...session.messages, props.promptMessage] : props.body.messages;
|
|
3165
3317
|
return {
|
|
3166
|
-
generate: (options) => props.runAgent.generate(
|
|
3167
|
-
stream: (options) => props.runAgent.stream(
|
|
3318
|
+
generate: (options) => props.runAgent.generate({ messages: transcript, ...options }),
|
|
3319
|
+
stream: (options) => props.runAgent.stream({ messages: transcript, ...options })
|
|
3168
3320
|
};
|
|
3169
3321
|
}
|
|
3170
|
-
function createRunOptions(body, agentId, session) {
|
|
3171
|
-
const options = {
|
|
3172
|
-
if (body.maxTurns !== void 0) options.maxTurns = body.maxTurns;
|
|
3173
|
-
if (body.toolConcurrency !== void 0)
|
|
3322
|
+
function createRunOptions(body, agentId, session, abortSignal) {
|
|
3323
|
+
const options = { abortSignal };
|
|
3324
|
+
if (body.type === "messages" && body.maxTurns !== void 0) options.maxTurns = body.maxTurns;
|
|
3325
|
+
if (body.type === "messages" && body.toolConcurrency !== void 0) {
|
|
3326
|
+
options.toolConcurrency = body.toolConcurrency;
|
|
3327
|
+
}
|
|
3174
3328
|
if (body.trace !== void 0) {
|
|
3175
3329
|
options.trace = traceForRun(body.trace, agentId, session);
|
|
3176
3330
|
} else if (session !== void 0) {
|
|
@@ -3179,34 +3333,11 @@ function createRunOptions(body, agentId, session) {
|
|
|
3179
3333
|
return options;
|
|
3180
3334
|
}
|
|
3181
3335
|
function handleStreamingAgentRun(c, run, props) {
|
|
3182
|
-
const
|
|
3183
|
-
const
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
};
|
|
3188
|
-
if (run.session !== void 0) approvalContext.sessionId = run.session.id;
|
|
3189
|
-
if (run.body.metadata !== void 0) approvalContext.metadata = run.body.metadata;
|
|
3190
|
-
const questionContext = {
|
|
3191
|
-
runId: run.runId,
|
|
3192
|
-
agentId: run.agentId,
|
|
3193
|
-
emit: (event) => runtimeEvents.push(event)
|
|
3194
|
-
};
|
|
3195
|
-
if (run.session !== void 0) questionContext.sessionId = run.session.id;
|
|
3196
|
-
if (run.body.metadata !== void 0) questionContext.metadata = run.body.metadata;
|
|
3197
|
-
const effectiveHook = props.questionRuntime.createHook(questionContext);
|
|
3198
|
-
const streamOptions = withInternalAgentRunOptions(
|
|
3199
|
-
{ ...run.options },
|
|
3200
|
-
{ hook: effectiveHook, runId: run.runId }
|
|
3201
|
-
);
|
|
3202
|
-
const runStream = mergeRunAndApprovalEvents(
|
|
3203
|
-
resumeStreamingApprovals(
|
|
3204
|
-
run.runAgent,
|
|
3205
|
-
run.execution.stream(streamOptions),
|
|
3206
|
-
props.approvalRuntime,
|
|
3207
|
-
approvalContext
|
|
3208
|
-
),
|
|
3209
|
-
runtimeEvents
|
|
3336
|
+
const streamOptions = withInternalAgentRunOptions({ ...run.options }, { runId: run.runId });
|
|
3337
|
+
const runStream = registerStreamingContinuation(
|
|
3338
|
+
run.execution.stream(streamOptions),
|
|
3339
|
+
props.continuationRegistry,
|
|
3340
|
+
run
|
|
3210
3341
|
);
|
|
3211
3342
|
let stream = runStream;
|
|
3212
3343
|
let persistedRun;
|
|
@@ -3221,17 +3352,17 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3221
3352
|
}),
|
|
3222
3353
|
store: run.sessionStore,
|
|
3223
3354
|
session: run.session,
|
|
3224
|
-
message: run.body.
|
|
3355
|
+
message: run.body.messages.at(-1),
|
|
3225
3356
|
runId: run.runId,
|
|
3226
3357
|
startedAt: run.runStartedAt,
|
|
3227
|
-
persistGeneratedMessages: run.shouldPersistSessionMessages
|
|
3358
|
+
persistGeneratedMessages: run.shouldPersistSessionMessages,
|
|
3359
|
+
generatedMessagesStartIndex: run.generatedMessagesStartIndex
|
|
3228
3360
|
});
|
|
3229
3361
|
stream = persistedRun.events;
|
|
3230
3362
|
}
|
|
3231
3363
|
return streamAgentRunEvents(c, stream, {
|
|
3364
|
+
runId: run.runId,
|
|
3232
3365
|
onCancel: async () => {
|
|
3233
|
-
props.approvalRuntime.cancelRun(run.runId);
|
|
3234
|
-
props.questionRuntime.cancelRun(run.runId);
|
|
3235
3366
|
const persistence = [];
|
|
3236
3367
|
if (persistedRun !== void 0) {
|
|
3237
3368
|
persistence.push(persistedRun.cancel());
|
|
@@ -3250,59 +3381,57 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3250
3381
|
}
|
|
3251
3382
|
async function handleBufferedAgentRun(c, run, props) {
|
|
3252
3383
|
try {
|
|
3253
|
-
const runtimeOptions = await startBufferedSessionRun(run
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
const response = result;
|
|
3261
|
-
await completeBufferedSessionRun(run, response);
|
|
3262
|
-
return c.json(response);
|
|
3384
|
+
const runtimeOptions = await startBufferedSessionRun(run);
|
|
3385
|
+
const result = await run.execution.generate(runtimeOptions);
|
|
3386
|
+
if (result.status === "suspended") {
|
|
3387
|
+
props.continuationRegistry.register({ continuation: result.continuation, context: run });
|
|
3388
|
+
}
|
|
3389
|
+
await completeBufferedSessionRun(run, result);
|
|
3390
|
+
return c.json(agentRunResponse(result));
|
|
3263
3391
|
} catch (error) {
|
|
3264
3392
|
await failBufferedSessionRun(run, error);
|
|
3265
3393
|
return errorResponse(c, 500, "internal_error", "Agent run failed", serializeError(error));
|
|
3266
3394
|
}
|
|
3267
3395
|
}
|
|
3268
|
-
|
|
3396
|
+
function agentRunResponse(result) {
|
|
3397
|
+
if (result.status !== "suspended") return result;
|
|
3398
|
+
const { continuation: _continuation, messages: _messages, ...response } = result;
|
|
3399
|
+
return response;
|
|
3400
|
+
}
|
|
3401
|
+
async function startBufferedSessionRun(run) {
|
|
3269
3402
|
if (run.session !== void 0) {
|
|
3270
3403
|
await appendSessionLog(run.sessionStore, runStartedLog(run.session, run.runId));
|
|
3271
3404
|
await appendSessionLog(run.sessionStore, memoryLoadedLog(run.session, run.runId));
|
|
3272
3405
|
}
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3406
|
+
const internalOptions = {
|
|
3407
|
+
onFailure: ({ messages }) => {
|
|
3408
|
+
run.failureMessages = structuredClone(messages);
|
|
3409
|
+
},
|
|
3410
|
+
runId: run.runId
|
|
3276
3411
|
};
|
|
3277
|
-
if (run.session !== void 0
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3412
|
+
if (run.session !== void 0 && run.sessionStore !== void 0) {
|
|
3413
|
+
const sessionId = run.session.id;
|
|
3414
|
+
const sessionStore = run.sessionStore;
|
|
3415
|
+
internalOptions.onMemoryCompaction = async (compaction) => {
|
|
3416
|
+
await appendSessionLog(
|
|
3417
|
+
sessionStore,
|
|
3418
|
+
memoryCompactedLog({
|
|
3419
|
+
sessionId,
|
|
3420
|
+
runId: run.runId,
|
|
3421
|
+
...compaction
|
|
3422
|
+
})
|
|
3423
|
+
);
|
|
3424
|
+
run.memoryCompactionLogged = true;
|
|
3425
|
+
};
|
|
3426
|
+
}
|
|
3427
|
+
return withInternalAgentRunOptions({ ...run.options }, internalOptions);
|
|
3281
3428
|
}
|
|
3282
|
-
function
|
|
3283
|
-
const
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
};
|
|
3287
|
-
if (run.session !== void 0) context.sessionId = run.session.id;
|
|
3288
|
-
if (run.body.metadata !== void 0) context.metadata = run.body.metadata;
|
|
3289
|
-
if (emit !== void 0) context.emit = emit;
|
|
3290
|
-
return context;
|
|
3291
|
-
}
|
|
3292
|
-
async function* resumeStreamingApprovals(agent, initialStream, approvalRuntime, context) {
|
|
3293
|
-
let stream = initialStream;
|
|
3294
|
-
while (true) {
|
|
3295
|
-
let pending;
|
|
3296
|
-
for await (const event of stream) {
|
|
3297
|
-
if (event.type === "approval_required") {
|
|
3298
|
-
pending = event;
|
|
3299
|
-
break;
|
|
3300
|
-
}
|
|
3301
|
-
yield event;
|
|
3429
|
+
async function* registerStreamingContinuation(stream, registry, run) {
|
|
3430
|
+
for await (const event of stream) {
|
|
3431
|
+
if (event.type === "final" && event.result.status === "suspended") {
|
|
3432
|
+
registry.register({ continuation: event.result.continuation, context: run });
|
|
3302
3433
|
}
|
|
3303
|
-
|
|
3304
|
-
const decision = await approvalRuntime.request(context, pending.approval);
|
|
3305
|
-
stream = agent.resume(pending, decision);
|
|
3434
|
+
yield event;
|
|
3306
3435
|
}
|
|
3307
3436
|
}
|
|
3308
3437
|
async function completeBufferedSessionRun(run, response) {
|
|
@@ -3310,10 +3439,10 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3310
3439
|
return;
|
|
3311
3440
|
}
|
|
3312
3441
|
if (run.shouldPersistSessionMessages) {
|
|
3313
|
-
const generatedMessages = response.messages.slice(
|
|
3442
|
+
const generatedMessages = response.messages.slice(run.generatedMessagesStartIndex);
|
|
3314
3443
|
if (generatedMessages.length > 0) {
|
|
3315
3444
|
await run.sessionStore.append({
|
|
3316
|
-
|
|
3445
|
+
scope: { sessionId: run.session.id, metadata: run.memoryMetadata },
|
|
3317
3446
|
runId: run.runId,
|
|
3318
3447
|
turn: 1,
|
|
3319
3448
|
messages: generatedMessages
|
|
@@ -3326,21 +3455,85 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3326
3455
|
await run.sessionStore.saveSessionRunTranscript({
|
|
3327
3456
|
id: run.session.id,
|
|
3328
3457
|
runId: run.runId,
|
|
3329
|
-
...optionalTitle(run.body.
|
|
3458
|
+
...optionalTitle(run.body.messages.at(-1)),
|
|
3330
3459
|
transcript,
|
|
3331
|
-
status: "success"
|
|
3460
|
+
status: response.status === "suspended" ? "suspended" : "success"
|
|
3332
3461
|
});
|
|
3462
|
+
if (response.memoryCompaction !== void 0 && !run.memoryCompactionLogged) {
|
|
3463
|
+
await appendSessionLog(
|
|
3464
|
+
run.sessionStore,
|
|
3465
|
+
memoryCompactedLog({
|
|
3466
|
+
sessionId: run.session.id,
|
|
3467
|
+
runId: run.runId,
|
|
3468
|
+
...response.memoryCompaction
|
|
3469
|
+
})
|
|
3470
|
+
);
|
|
3471
|
+
run.memoryCompactionLogged = true;
|
|
3472
|
+
}
|
|
3333
3473
|
await appendSessionLog(
|
|
3334
3474
|
run.sessionStore,
|
|
3335
|
-
|
|
3475
|
+
response.status === "suspended" ? {
|
|
3476
|
+
sessionId: run.session.id,
|
|
3477
|
+
runId: run.runId,
|
|
3478
|
+
level: "info",
|
|
3479
|
+
category: "run",
|
|
3480
|
+
event: "run.suspended",
|
|
3481
|
+
message: `Run suspended for ${response.interaction.type}`,
|
|
3482
|
+
metadata: {
|
|
3483
|
+
interactionId: response.interaction.id,
|
|
3484
|
+
interactionType: response.interaction.type,
|
|
3485
|
+
toolName: response.interaction.toolName,
|
|
3486
|
+
sourceRunId: response.continuation.sourceRunId
|
|
3487
|
+
}
|
|
3488
|
+
} : runCompletedLog({
|
|
3336
3489
|
sessionId: run.session.id,
|
|
3337
3490
|
runId: run.runId,
|
|
3338
3491
|
durationMs: durationMs2,
|
|
3339
3492
|
usage: response.usage,
|
|
3340
|
-
output: response.
|
|
3493
|
+
output: response.text,
|
|
3341
3494
|
messageCount: response.messages.length
|
|
3342
3495
|
})
|
|
3343
3496
|
);
|
|
3497
|
+
if (response.resumedFrom !== void 0) {
|
|
3498
|
+
const responseMessage = response.messages[0];
|
|
3499
|
+
const responsePart = responseMessage?.role === "tool" ? responseMessage.content[0] : void 0;
|
|
3500
|
+
if (responsePart !== void 0 && responsePart.type !== "tool-result") {
|
|
3501
|
+
const metadata = {
|
|
3502
|
+
interactionId: responsePart.interactionId,
|
|
3503
|
+
interactionType: responsePart.type === "tool-approval-response" ? "tool-approval" : "tool-question",
|
|
3504
|
+
toolName: responsePart.toolName,
|
|
3505
|
+
sourceRunId: response.resumedFrom.runId
|
|
3506
|
+
};
|
|
3507
|
+
if (responsePart.type === "tool-approval-response") {
|
|
3508
|
+
metadata.approved = responsePart.approved;
|
|
3509
|
+
metadata.hasReason = responsePart.reason !== void 0;
|
|
3510
|
+
} else {
|
|
3511
|
+
metadata.answerCount = responsePart.answers.length;
|
|
3512
|
+
}
|
|
3513
|
+
await appendSessionLog(run.sessionStore, {
|
|
3514
|
+
sessionId: run.session.id,
|
|
3515
|
+
runId: run.runId,
|
|
3516
|
+
level: "info",
|
|
3517
|
+
category: "run",
|
|
3518
|
+
event: "interaction.responded",
|
|
3519
|
+
message: `Interaction response received for ${responsePart.toolName}`,
|
|
3520
|
+
metadata
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
await appendSessionLog(run.sessionStore, {
|
|
3524
|
+
sessionId: run.session.id,
|
|
3525
|
+
runId: run.runId,
|
|
3526
|
+
level: "info",
|
|
3527
|
+
category: "run",
|
|
3528
|
+
event: "interaction.resumed",
|
|
3529
|
+
message: "Interaction resumed in a linked run",
|
|
3530
|
+
metadata: {
|
|
3531
|
+
interactionId: response.resumedFrom.interactionId,
|
|
3532
|
+
sourceRunId: response.resumedFrom.runId,
|
|
3533
|
+
resumedRunId: response.runId
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3344
3537
|
await appendSessionLog(
|
|
3345
3538
|
run.sessionStore,
|
|
3346
3539
|
memorySavedLog({
|
|
@@ -3354,17 +3547,14 @@ async function failBufferedSessionRun(run, error) {
|
|
|
3354
3547
|
if (run.session === void 0 || run.sessionStore === void 0) {
|
|
3355
3548
|
return;
|
|
3356
3549
|
}
|
|
3357
|
-
const messages = await run.sessionStore.load({
|
|
3358
|
-
sessionId: run.session.id,
|
|
3359
|
-
metadata: run.memoryMetadata
|
|
3360
|
-
});
|
|
3361
3550
|
const durationMs2 = Date.now() - run.runStartedAt;
|
|
3362
|
-
const
|
|
3551
|
+
const messages = run.failureMessages ?? [run.body.messages.at(-1)];
|
|
3552
|
+
const transcript = transcriptFromMessages([...messages]);
|
|
3363
3553
|
assignTranscriptRunDuration(transcript, durationMs2);
|
|
3364
3554
|
await run.sessionStore.saveSessionRunTranscript({
|
|
3365
3555
|
id: run.session.id,
|
|
3366
3556
|
runId: run.runId,
|
|
3367
|
-
...optionalTitle(run.body.
|
|
3557
|
+
...optionalTitle(run.body.messages.at(-1)),
|
|
3368
3558
|
transcript,
|
|
3369
3559
|
status: "error",
|
|
3370
3560
|
error: serializeError(error)
|
|
@@ -3374,254 +3564,13 @@ async function failBufferedSessionRun(run, error) {
|
|
|
3374
3564
|
runFailedLog(run.session.id, run.runId, error, run.runStartedAt)
|
|
3375
3565
|
);
|
|
3376
3566
|
}
|
|
3377
|
-
function normalizePromptMessage(message) {
|
|
3378
|
-
return typeof message === "string" ? Message.user(message) : message;
|
|
3379
|
-
}
|
|
3380
3567
|
function usesStoreAsAgentMemory(agent, store) {
|
|
3381
3568
|
const memoryStore = agent.memory?.store;
|
|
3382
3569
|
return memoryStore !== void 0 && rawObservedStore(memoryStore) === rawObservedStore(store);
|
|
3383
3570
|
}
|
|
3384
3571
|
|
|
3385
|
-
// src/runtime/
|
|
3386
|
-
import {
|
|
3387
|
-
|
|
3388
|
-
// src/runtime/query.ts
|
|
3389
|
-
function optionalQueryString(value) {
|
|
3390
|
-
const trimmed = value?.trim();
|
|
3391
|
-
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
3392
|
-
}
|
|
3393
|
-
function parseLimit(value, defaultMax = 50, max = 100) {
|
|
3394
|
-
if (value === void 0 || value.trim().length === 0) {
|
|
3395
|
-
return defaultMax;
|
|
3396
|
-
}
|
|
3397
|
-
const limit = Number(value);
|
|
3398
|
-
if (!Number.isInteger(limit) || limit <= 0) {
|
|
3399
|
-
return void 0;
|
|
3400
|
-
}
|
|
3401
|
-
return Math.min(limit, max);
|
|
3402
|
-
}
|
|
3403
|
-
function parseTraceStatus(value) {
|
|
3404
|
-
const status = optionalQueryString(value);
|
|
3405
|
-
if (status === void 0) {
|
|
3406
|
-
return void 0;
|
|
3407
|
-
}
|
|
3408
|
-
return status === "running" || status === "success" || status === "error" ? status : false;
|
|
3409
|
-
}
|
|
3410
|
-
function parseAfter(value) {
|
|
3411
|
-
if (value === void 0 || value.trim().length === 0) {
|
|
3412
|
-
return void 0;
|
|
3413
|
-
}
|
|
3414
|
-
const after = Number(value);
|
|
3415
|
-
if (!Number.isInteger(after) || after < 0) {
|
|
3416
|
-
return false;
|
|
3417
|
-
}
|
|
3418
|
-
return after;
|
|
3419
|
-
}
|
|
3420
|
-
|
|
3421
|
-
// src/runtime/approvals.ts
|
|
3422
|
-
function registerApprovalRoutes(app, approvals) {
|
|
3423
|
-
app.get("/approvals", (c) => {
|
|
3424
|
-
const status = parseApprovalStatus(c.req.query("status"));
|
|
3425
|
-
if (status === false) {
|
|
3426
|
-
return errorResponse(c, 400, "bad_request", "status must be pending or resolved");
|
|
3427
|
-
}
|
|
3428
|
-
const options = {};
|
|
3429
|
-
const runId = optionalQueryString(c.req.query("runId"));
|
|
3430
|
-
const agentId = optionalQueryString(c.req.query("agentId"));
|
|
3431
|
-
const sessionId = optionalQueryString(c.req.query("sessionId"));
|
|
3432
|
-
if (status !== void 0) {
|
|
3433
|
-
options.status = status;
|
|
3434
|
-
}
|
|
3435
|
-
if (runId !== void 0) {
|
|
3436
|
-
options.runId = runId;
|
|
3437
|
-
}
|
|
3438
|
-
if (agentId !== void 0) {
|
|
3439
|
-
options.agentId = agentId;
|
|
3440
|
-
}
|
|
3441
|
-
if (sessionId !== void 0) {
|
|
3442
|
-
options.sessionId = sessionId;
|
|
3443
|
-
}
|
|
3444
|
-
return c.json({
|
|
3445
|
-
approvals: approvals.list(options)
|
|
3446
|
-
});
|
|
3447
|
-
});
|
|
3448
|
-
app.post("/approvals/:approvalId/decision", async (c) => {
|
|
3449
|
-
const body = await parseApprovalDecisionRequest(c);
|
|
3450
|
-
if ("error" in body) {
|
|
3451
|
-
return body.error;
|
|
3452
|
-
}
|
|
3453
|
-
const result = approvals.decide(c.req.param("approvalId"), body);
|
|
3454
|
-
if (result === "missing") {
|
|
3455
|
-
return errorResponse(c, 404, "not_found", "Approval not found");
|
|
3456
|
-
}
|
|
3457
|
-
if (result === "resolved") {
|
|
3458
|
-
return errorResponse(c, 409, "conflict", "Approval is already resolved");
|
|
3459
|
-
}
|
|
3460
|
-
return c.json(result);
|
|
3461
|
-
});
|
|
3462
|
-
}
|
|
3463
|
-
function parseApprovalStatus(value) {
|
|
3464
|
-
const status = optionalQueryString(value);
|
|
3465
|
-
if (status === void 0) {
|
|
3466
|
-
return void 0;
|
|
3467
|
-
}
|
|
3468
|
-
return status === "pending" || status === "resolved" ? status : false;
|
|
3469
|
-
}
|
|
3470
|
-
async function parseApprovalDecisionRequest(c) {
|
|
3471
|
-
let body;
|
|
3472
|
-
try {
|
|
3473
|
-
body = await c.req.json();
|
|
3474
|
-
} catch {
|
|
3475
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
|
|
3476
|
-
}
|
|
3477
|
-
if (!isObject(body)) {
|
|
3478
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
3479
|
-
}
|
|
3480
|
-
if (typeof body.approved !== "boolean") {
|
|
3481
|
-
return { error: errorResponse(c, 400, "bad_request", "approved must be a boolean") };
|
|
3482
|
-
}
|
|
3483
|
-
if ("reason" in body && typeof body.reason !== "string") {
|
|
3484
|
-
return { error: errorResponse(c, 400, "bad_request", "reason must be a string") };
|
|
3485
|
-
}
|
|
3486
|
-
const reason = typeof body.reason === "string" && body.reason.trim().length > 0 ? body.reason.trim() : void 0;
|
|
3487
|
-
return approvalDecision(body.approved, reason);
|
|
3488
|
-
}
|
|
3489
|
-
function createApprovalRuntime() {
|
|
3490
|
-
const approvals = /* @__PURE__ */ new Map();
|
|
3491
|
-
return {
|
|
3492
|
-
approvals,
|
|
3493
|
-
async request(context, approval) {
|
|
3494
|
-
const decision = await requestApproval(approvals, context, approval);
|
|
3495
|
-
return decision.approved ? { approved: true, ...decision.reason === void 0 ? {} : { reason: decision.reason } } : {
|
|
3496
|
-
approved: false,
|
|
3497
|
-
...decision.reason === void 0 ? {} : { reason: decision.reason }
|
|
3498
|
-
};
|
|
3499
|
-
},
|
|
3500
|
-
list(options) {
|
|
3501
|
-
return [...approvals.values()].filter((approval) => {
|
|
3502
|
-
if (options.status === "pending" && approval.status !== "pending") {
|
|
3503
|
-
return false;
|
|
3504
|
-
}
|
|
3505
|
-
if (options.status === "resolved" && approval.status === "pending") {
|
|
3506
|
-
return false;
|
|
3507
|
-
}
|
|
3508
|
-
if (options.runId !== void 0 && approval.runId !== options.runId) {
|
|
3509
|
-
return false;
|
|
3510
|
-
}
|
|
3511
|
-
if (options.agentId !== void 0 && approval.agentId !== options.agentId) {
|
|
3512
|
-
return false;
|
|
3513
|
-
}
|
|
3514
|
-
if (options.sessionId !== void 0 && approval.sessionId !== options.sessionId) {
|
|
3515
|
-
return false;
|
|
3516
|
-
}
|
|
3517
|
-
return true;
|
|
3518
|
-
}).map(publicApproval);
|
|
3519
|
-
},
|
|
3520
|
-
cancelRun(runId) {
|
|
3521
|
-
const cancelled = [];
|
|
3522
|
-
for (const approval of approvals.values()) {
|
|
3523
|
-
if (!isPendingApproval(approval) || approval.runId !== runId) {
|
|
3524
|
-
continue;
|
|
3525
|
-
}
|
|
3526
|
-
const resolved = resolveApproval(approval, "cancelled", {
|
|
3527
|
-
reason: "Run cancelled in Anvia Studio."
|
|
3528
|
-
});
|
|
3529
|
-
approvals.set(approval.id, resolved);
|
|
3530
|
-
approval.emit?.({ type: "tool_approval_result", approval: resolved });
|
|
3531
|
-
approval.resolve(approvalDecision(false, resolved.reason));
|
|
3532
|
-
cancelled.push(resolved);
|
|
3533
|
-
}
|
|
3534
|
-
return cancelled;
|
|
3535
|
-
},
|
|
3536
|
-
decide(id, decision) {
|
|
3537
|
-
const approval = approvals.get(id);
|
|
3538
|
-
if (approval === void 0) {
|
|
3539
|
-
return "missing";
|
|
3540
|
-
}
|
|
3541
|
-
if (!isPendingApproval(approval)) {
|
|
3542
|
-
return "resolved";
|
|
3543
|
-
}
|
|
3544
|
-
const reason = decision.approved ? decision.reason : decision.reason ?? approval.rejectMessage ?? "Rejected in Anvia Studio.";
|
|
3545
|
-
const resolved = resolveApproval(
|
|
3546
|
-
approval,
|
|
3547
|
-
decision.approved ? "approved" : "rejected",
|
|
3548
|
-
reason === void 0 ? {} : { reason }
|
|
3549
|
-
);
|
|
3550
|
-
approvals.set(id, resolved);
|
|
3551
|
-
approval.emit?.({ type: "tool_approval_result", approval: resolved });
|
|
3552
|
-
approval.resolve(approvalDecision(decision.approved, reason));
|
|
3553
|
-
return publicApproval(resolved);
|
|
3554
|
-
}
|
|
3555
|
-
};
|
|
3556
|
-
}
|
|
3557
|
-
async function requestApproval(approvals, context, request) {
|
|
3558
|
-
const id = request.id;
|
|
3559
|
-
const details = getAgentApprovalRequestDetails(request);
|
|
3560
|
-
const approval = {
|
|
3561
|
-
id,
|
|
3562
|
-
runId: context.runId,
|
|
3563
|
-
agentId: context.agentId,
|
|
3564
|
-
toolName: request.toolName,
|
|
3565
|
-
internalCallId: details?.internalCallId ?? request.id,
|
|
3566
|
-
args: details?.rawArgs ?? JSON.stringify(request.input) ?? "null",
|
|
3567
|
-
status: "pending",
|
|
3568
|
-
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3569
|
-
resolve: () => {
|
|
3570
|
-
}
|
|
3571
|
-
};
|
|
3572
|
-
if (context.sessionId !== void 0) approval.sessionId = context.sessionId;
|
|
3573
|
-
if (request.toolCallId !== void 0) approval.callId = request.toolCallId;
|
|
3574
|
-
if (request.reason !== void 0) approval.reason = request.reason;
|
|
3575
|
-
if (details?.rejectMessage !== void 0) approval.rejectMessage = details.rejectMessage;
|
|
3576
|
-
if (context.emit !== void 0) approval.emit = context.emit;
|
|
3577
|
-
const decision = new Promise((resolve2) => {
|
|
3578
|
-
approval.resolve = (decision2) => {
|
|
3579
|
-
const current = approvals.get(id);
|
|
3580
|
-
if (!isPendingApproval(current)) {
|
|
3581
|
-
if (current !== void 0) {
|
|
3582
|
-
resolve2(approvalDecision(current.status === "approved", current.reason));
|
|
3583
|
-
}
|
|
3584
|
-
return;
|
|
3585
|
-
}
|
|
3586
|
-
const reason = decision2.approved ? decision2.reason : decision2.reason ?? details?.rejectMessage ?? "Rejected in Anvia Studio.";
|
|
3587
|
-
const resolved = resolveApproval(
|
|
3588
|
-
current,
|
|
3589
|
-
decision2.approved ? "approved" : "rejected",
|
|
3590
|
-
reason === void 0 ? {} : { reason }
|
|
3591
|
-
);
|
|
3592
|
-
approvals.set(id, resolved);
|
|
3593
|
-
context.emit?.({ type: "tool_approval_result", approval: resolved });
|
|
3594
|
-
resolve2(approvalDecision(decision2.approved, reason));
|
|
3595
|
-
};
|
|
3596
|
-
});
|
|
3597
|
-
approvals.set(id, approval);
|
|
3598
|
-
context.emit?.({ type: "tool_approval_request", approval: publicApproval(approval) });
|
|
3599
|
-
return decision;
|
|
3600
|
-
}
|
|
3601
|
-
function isPendingApproval(approval) {
|
|
3602
|
-
return approval !== void 0 && approval.status === "pending" && "resolve" in approval;
|
|
3603
|
-
}
|
|
3604
|
-
function resolveApproval(approval, status, options = {}) {
|
|
3605
|
-
const resolved = {
|
|
3606
|
-
...approval,
|
|
3607
|
-
status,
|
|
3608
|
-
resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3609
|
-
};
|
|
3610
|
-
if (options.reason !== void 0) resolved.reason = options.reason;
|
|
3611
|
-
return publicApproval(resolved);
|
|
3612
|
-
}
|
|
3613
|
-
function approvalDecision(approved, reason) {
|
|
3614
|
-
const decision = { approved };
|
|
3615
|
-
if (reason !== void 0) decision.reason = reason;
|
|
3616
|
-
return decision;
|
|
3617
|
-
}
|
|
3618
|
-
function publicApproval(approval) {
|
|
3619
|
-
const { emit, rejectMessage, resolve: resolve2, ...rest } = approval;
|
|
3620
|
-
void emit;
|
|
3621
|
-
void rejectMessage;
|
|
3622
|
-
void resolve2;
|
|
3623
|
-
return rest;
|
|
3624
|
-
}
|
|
3572
|
+
// src/runtime/config.ts
|
|
3573
|
+
import { isQuestionTool } from "@anvia/core/tool";
|
|
3625
3574
|
|
|
3626
3575
|
// src/runtime/eval-config.ts
|
|
3627
3576
|
var CASE_PREVIEW_LIMIT = 5;
|
|
@@ -3703,6 +3652,41 @@ function isRecord2(value) {
|
|
|
3703
3652
|
|
|
3704
3653
|
// src/runtime/knowledge.ts
|
|
3705
3654
|
import { getAgentToolState } from "@anvia/core/internal/agent";
|
|
3655
|
+
|
|
3656
|
+
// src/runtime/query.ts
|
|
3657
|
+
function optionalQueryString(value) {
|
|
3658
|
+
const trimmed = value?.trim();
|
|
3659
|
+
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
3660
|
+
}
|
|
3661
|
+
function parseLimit(value, defaultMax = 50, max = 100) {
|
|
3662
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3663
|
+
return defaultMax;
|
|
3664
|
+
}
|
|
3665
|
+
const limit = Number(value);
|
|
3666
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
3667
|
+
return void 0;
|
|
3668
|
+
}
|
|
3669
|
+
return Math.min(limit, max);
|
|
3670
|
+
}
|
|
3671
|
+
function parseTraceStatus(value) {
|
|
3672
|
+
const status = optionalQueryString(value);
|
|
3673
|
+
if (status === void 0) {
|
|
3674
|
+
return void 0;
|
|
3675
|
+
}
|
|
3676
|
+
return status === "running" || status === "success" || status === "suspended" || status === "error" ? status : false;
|
|
3677
|
+
}
|
|
3678
|
+
function parseAfter(value) {
|
|
3679
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3680
|
+
return void 0;
|
|
3681
|
+
}
|
|
3682
|
+
const after = Number(value);
|
|
3683
|
+
if (!Number.isInteger(after) || after < 0) {
|
|
3684
|
+
return false;
|
|
3685
|
+
}
|
|
3686
|
+
return after;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
// src/runtime/knowledge.ts
|
|
3706
3690
|
function registerKnowledgeRoutes(app, props) {
|
|
3707
3691
|
app.get("/knowledge", async (c) => {
|
|
3708
3692
|
const limit = parseLimit(c.req.query("limit"));
|
|
@@ -3763,7 +3747,7 @@ async function agentKnowledgeConfig(agent) {
|
|
|
3763
3747
|
async function knowledgeSources(agent) {
|
|
3764
3748
|
const toolIndexes = getAgentToolState(agent.agent).toolIndexes;
|
|
3765
3749
|
const staticContext = staticContextDocuments(agent.agent);
|
|
3766
|
-
const indexedContext =
|
|
3750
|
+
const indexedContext = vectorContexts(agent.agent);
|
|
3767
3751
|
const sources = [
|
|
3768
3752
|
{
|
|
3769
3753
|
sourceId: staticSourceId(),
|
|
@@ -3775,20 +3759,20 @@ async function knowledgeSources(agent) {
|
|
|
3775
3759
|
}
|
|
3776
3760
|
];
|
|
3777
3761
|
const dynamicContextSources = await Promise.all(
|
|
3778
|
-
indexedContext.map(async (
|
|
3779
|
-
const inspect = inspectFn(
|
|
3780
|
-
const count = await inspectableCount(inspect,
|
|
3762
|
+
indexedContext.map(async (vectorContext, index) => {
|
|
3763
|
+
const inspect = inspectFn(vectorContext.store);
|
|
3764
|
+
const count = await inspectableCount(inspect, vectorContext.filter);
|
|
3781
3765
|
const source = {
|
|
3782
3766
|
sourceId: dynamicContextSourceId(index),
|
|
3783
3767
|
kind: "dynamic_context",
|
|
3784
3768
|
label: `Dynamic context ${index + 1}`,
|
|
3785
3769
|
count: 1,
|
|
3786
3770
|
registrationIndex: index,
|
|
3787
|
-
topK:
|
|
3771
|
+
topK: vectorContext.topK,
|
|
3788
3772
|
inspectable: inspect !== void 0
|
|
3789
3773
|
};
|
|
3790
|
-
if (
|
|
3791
|
-
source.
|
|
3774
|
+
if (vectorContext.minScore !== void 0) {
|
|
3775
|
+
source.minScore = vectorContext.minScore;
|
|
3792
3776
|
}
|
|
3793
3777
|
if (count !== void 0) source.itemCount = count;
|
|
3794
3778
|
return source;
|
|
@@ -3807,8 +3791,8 @@ async function knowledgeSources(agent) {
|
|
|
3807
3791
|
topK: toolIndex.topK,
|
|
3808
3792
|
inspectable: inspect !== void 0
|
|
3809
3793
|
};
|
|
3810
|
-
if (toolIndex.
|
|
3811
|
-
source.
|
|
3794
|
+
if (toolIndex.minScore !== void 0) {
|
|
3795
|
+
source.minScore = toolIndex.minScore;
|
|
3812
3796
|
}
|
|
3813
3797
|
if (count !== void 0) source.itemCount = count;
|
|
3814
3798
|
return source;
|
|
@@ -3827,20 +3811,20 @@ async function knowledgeItemsPage(agent, sourceId, request) {
|
|
|
3827
3811
|
if (sourceId === staticSourceId()) {
|
|
3828
3812
|
return staticKnowledgeItemsPage(agent, request);
|
|
3829
3813
|
}
|
|
3830
|
-
const
|
|
3831
|
-
if (
|
|
3832
|
-
const
|
|
3833
|
-
if (
|
|
3814
|
+
const vectorContextIndex = dynamicSourceIndex(sourceId, "dynamic_context");
|
|
3815
|
+
if (vectorContextIndex !== void 0) {
|
|
3816
|
+
const vectorContext = vectorContexts(agent.agent)[vectorContextIndex];
|
|
3817
|
+
if (vectorContext === void 0) {
|
|
3834
3818
|
return void 0;
|
|
3835
3819
|
}
|
|
3836
|
-
const inspect = inspectFn(
|
|
3820
|
+
const inspect = inspectFn(vectorContext.store);
|
|
3837
3821
|
if (inspect === void 0) {
|
|
3838
3822
|
return nonInspectablePage(agent.id, sourceId, "dynamic_context");
|
|
3839
3823
|
}
|
|
3840
3824
|
const page = await inspect({
|
|
3841
3825
|
limit: request.limit,
|
|
3842
3826
|
cursor: request.cursor,
|
|
3843
|
-
filter:
|
|
3827
|
+
filter: vectorContext.filter
|
|
3844
3828
|
});
|
|
3845
3829
|
const result = {
|
|
3846
3830
|
agentId: agent.id,
|
|
@@ -3881,12 +3865,12 @@ async function knowledgeItemsPage(agent, sourceId, request) {
|
|
|
3881
3865
|
}
|
|
3882
3866
|
return void 0;
|
|
3883
3867
|
}
|
|
3884
|
-
function inspectFn(
|
|
3885
|
-
if (!isRecord3(
|
|
3868
|
+
function inspectFn(store) {
|
|
3869
|
+
if (!isRecord3(store) || typeof store.inspect !== "function") {
|
|
3886
3870
|
return void 0;
|
|
3887
3871
|
}
|
|
3888
|
-
const inspect =
|
|
3889
|
-
return (request) => inspect.call(
|
|
3872
|
+
const inspect = store.inspect;
|
|
3873
|
+
return (request) => inspect.call(store, request);
|
|
3890
3874
|
}
|
|
3891
3875
|
function staticKnowledgeItemsPage(agent, request) {
|
|
3892
3876
|
const staticContext = staticContextDocuments(agent.agent);
|
|
@@ -4102,7 +4086,7 @@ function isRecord3(value) {
|
|
|
4102
4086
|
|
|
4103
4087
|
// src/runtime/tool-metadata.ts
|
|
4104
4088
|
import { getAgentToolState as getAgentToolState2 } from "@anvia/core/internal/agent";
|
|
4105
|
-
|
|
4089
|
+
import { isMcpTool } from "@anvia/core/mcp";
|
|
4106
4090
|
function agentToolItems(agent) {
|
|
4107
4091
|
const state = getAgentToolState2(agent.agent);
|
|
4108
4092
|
const items = state.staticTools.map((tool) => ({ tool, source: "static" }));
|
|
@@ -4135,15 +4119,10 @@ function toolRequiresApproval(tool) {
|
|
|
4135
4119
|
return approval !== void 0 && approval !== false;
|
|
4136
4120
|
}
|
|
4137
4121
|
function mcpServerName(tool) {
|
|
4138
|
-
|
|
4139
|
-
if (typeof metadata !== "object" || metadata === null) {
|
|
4140
|
-
return void 0;
|
|
4141
|
-
}
|
|
4142
|
-
const serverName = metadata.serverName;
|
|
4143
|
-
return typeof serverName === "string" && serverName.length > 0 ? serverName : void 0;
|
|
4122
|
+
return isMcpTool(tool) ? tool.mcp.serverName : void 0;
|
|
4144
4123
|
}
|
|
4145
|
-
function
|
|
4146
|
-
return
|
|
4124
|
+
function agentHasMcpServers(agent) {
|
|
4125
|
+
return agent.agent.mcpServers.length > 0;
|
|
4147
4126
|
}
|
|
4148
4127
|
|
|
4149
4128
|
// src/runtime/config.ts
|
|
@@ -4165,7 +4144,7 @@ function agentConfig(agent) {
|
|
|
4165
4144
|
function agentRuntimeSummary(agent) {
|
|
4166
4145
|
const tools = agentToolItems(agent);
|
|
4167
4146
|
const staticContext = staticContextDocuments(agent.agent);
|
|
4168
|
-
const indexedContext =
|
|
4147
|
+
const indexedContext = vectorContexts(agent.agent);
|
|
4169
4148
|
const name = agent.name ?? agent.agent.name;
|
|
4170
4149
|
const description = agent.description ?? agent.agent.description;
|
|
4171
4150
|
const summary = {
|
|
@@ -4178,7 +4157,7 @@ function agentRuntimeSummary(agent) {
|
|
|
4178
4157
|
mcpToolCount: tools.filter((item) => mcpServerName(item.tool) !== void 0).length,
|
|
4179
4158
|
staticContextCount: staticContext.length,
|
|
4180
4159
|
dynamicContextCount: indexedContext.length,
|
|
4181
|
-
observerCount: agent.agent.observers.length,
|
|
4160
|
+
observerCount: Object.keys(agent.agent.observability?.observers ?? {}).length,
|
|
4182
4161
|
hasMemory: agent.agent.memory !== void 0,
|
|
4183
4162
|
hasLifecycle: agent.agent.lifecycle !== void 0,
|
|
4184
4163
|
hasOutputSchema: agent.agent.outputSchema !== void 0
|
|
@@ -4250,14 +4229,16 @@ function capabilityConfig(_options, agents, pipelines, stores, sandboxCount = 0)
|
|
|
4250
4229
|
if (agents.some((agent) => agent.agent.tools.length > 0)) {
|
|
4251
4230
|
capabilities.tools = { enabled: true };
|
|
4252
4231
|
}
|
|
4253
|
-
if (agents.some(
|
|
4232
|
+
if (agents.some(agentHasMcpServers)) {
|
|
4254
4233
|
capabilities.mcps = { enabled: true };
|
|
4255
4234
|
}
|
|
4256
4235
|
if (sandboxCount > 0) {
|
|
4257
4236
|
capabilities.sandboxes = { enabled: true };
|
|
4258
4237
|
}
|
|
4259
|
-
if (agents.some(
|
|
4260
|
-
|
|
4238
|
+
if (agents.some(
|
|
4239
|
+
(agent) => agent.agent.tools.some((tool) => toolRequiresApproval(tool) || isQuestionTool(tool))
|
|
4240
|
+
)) {
|
|
4241
|
+
capabilities.interactions = { enabled: true };
|
|
4261
4242
|
}
|
|
4262
4243
|
if (agents.some(agentHasKnowledge)) {
|
|
4263
4244
|
capabilities.knowledge = { enabled: true };
|
|
@@ -4339,6 +4320,47 @@ async function parseEvalRunRequest(c) {
|
|
|
4339
4320
|
return request;
|
|
4340
4321
|
}
|
|
4341
4322
|
|
|
4323
|
+
// src/runtime/interactions.ts
|
|
4324
|
+
import {
|
|
4325
|
+
assertAgentInteractionResponse
|
|
4326
|
+
} from "@anvia/core/agent";
|
|
4327
|
+
function createStudioContinuationRegistry() {
|
|
4328
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4329
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
4330
|
+
return {
|
|
4331
|
+
register(registration) {
|
|
4332
|
+
const id = registration.continuation.interaction.id;
|
|
4333
|
+
if (pending.has(id) || claimed.has(id)) {
|
|
4334
|
+
throw new TypeError(`Duplicate Studio interaction ID "${id}".`);
|
|
4335
|
+
}
|
|
4336
|
+
pending.set(id, registration);
|
|
4337
|
+
},
|
|
4338
|
+
take(interactionId, agentId, response) {
|
|
4339
|
+
if (claimed.has(interactionId)) return { status: "claimed" };
|
|
4340
|
+
const registration = pending.get(interactionId);
|
|
4341
|
+
if (registration === void 0 || registration.continuation.agentId !== agentId) {
|
|
4342
|
+
return { status: "missing" };
|
|
4343
|
+
}
|
|
4344
|
+
const request = registration.continuation.interaction;
|
|
4345
|
+
try {
|
|
4346
|
+
assertAgentInteractionResponse(request, response);
|
|
4347
|
+
} catch (error) {
|
|
4348
|
+
return {
|
|
4349
|
+
status: "invalid",
|
|
4350
|
+
error: error instanceof TypeError ? error : new TypeError(String(error))
|
|
4351
|
+
};
|
|
4352
|
+
}
|
|
4353
|
+
pending.delete(interactionId);
|
|
4354
|
+
claimed.add(interactionId);
|
|
4355
|
+
return { status: "ready", registration };
|
|
4356
|
+
},
|
|
4357
|
+
clear() {
|
|
4358
|
+
pending.clear();
|
|
4359
|
+
claimed.clear();
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4342
4364
|
// src/runtime/mcps.ts
|
|
4343
4365
|
function registerMcpRoutes(app, props) {
|
|
4344
4366
|
app.get("/agents/:agentId/mcps", async (c) => {
|
|
@@ -4354,43 +4376,29 @@ function registerMcpRoutes(app, props) {
|
|
|
4354
4376
|
});
|
|
4355
4377
|
}
|
|
4356
4378
|
async function agentMcpMetadata(agent) {
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
return [...servers.entries()].map(([name, tools]) => {
|
|
4381
|
-
const sortedTools = tools.sort((left, right) => {
|
|
4382
|
-
if (left.source !== right.source) {
|
|
4383
|
-
return left.source === "static" ? -1 : 1;
|
|
4384
|
-
}
|
|
4385
|
-
return left.name.localeCompare(right.name);
|
|
4386
|
-
});
|
|
4387
|
-
return {
|
|
4388
|
-
agentId: agent.id,
|
|
4389
|
-
name,
|
|
4390
|
-
toolCount: sortedTools.length,
|
|
4391
|
-
tools: sortedTools
|
|
4392
|
-
};
|
|
4393
|
-
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
4379
|
+
return Promise.all(
|
|
4380
|
+
agent.agent.mcpServers.map(async (server) => {
|
|
4381
|
+
const tools = await Promise.all(
|
|
4382
|
+
server.tools.map(async (tool) => {
|
|
4383
|
+
const definition = await tool.definition("");
|
|
4384
|
+
return {
|
|
4385
|
+
name: definition.name,
|
|
4386
|
+
description: definition.description,
|
|
4387
|
+
parameters: definition.parameters,
|
|
4388
|
+
source: "static",
|
|
4389
|
+
approval: approvalMetadata(tool)
|
|
4390
|
+
};
|
|
4391
|
+
})
|
|
4392
|
+
);
|
|
4393
|
+
const sortedTools = tools.sort((left, right) => left.name.localeCompare(right.name));
|
|
4394
|
+
return {
|
|
4395
|
+
agentId: agent.id,
|
|
4396
|
+
name: server.name,
|
|
4397
|
+
toolCount: sortedTools.length,
|
|
4398
|
+
tools: sortedTools
|
|
4399
|
+
};
|
|
4400
|
+
})
|
|
4401
|
+
).then((servers) => servers.sort((left, right) => left.name.localeCompare(right.name)));
|
|
4394
4402
|
}
|
|
4395
4403
|
|
|
4396
4404
|
// src/runtime/memory.ts
|
|
@@ -4612,7 +4620,7 @@ function agentMemorySource(ref, label, agentId, storeKindValue, inspector) {
|
|
|
4612
4620
|
).slice(0, options.limit);
|
|
4613
4621
|
},
|
|
4614
4622
|
async getConversation(conversationRef) {
|
|
4615
|
-
const conversation = await inspector.getConversation(conversationRef);
|
|
4623
|
+
const conversation = await inspector.getConversation({ ref: conversationRef });
|
|
4616
4624
|
return conversation === void 0 ? void 0 : inspectorConversationDetail(conversation, summary.agentIds);
|
|
4617
4625
|
}
|
|
4618
4626
|
};
|
|
@@ -4835,12 +4843,12 @@ function pipelineRunFailedLog(pipelineId, runId, error, startedAt) {
|
|
|
4835
4843
|
}
|
|
4836
4844
|
};
|
|
4837
4845
|
}
|
|
4838
|
-
function pipelineStageLog(
|
|
4846
|
+
function pipelineStageLog(event, studioPipelineId = event.pipelineId) {
|
|
4839
4847
|
const category = stageCategory(event.node);
|
|
4840
4848
|
if (event.type === "stage_started") {
|
|
4841
4849
|
return {
|
|
4842
|
-
pipelineId,
|
|
4843
|
-
runId,
|
|
4850
|
+
pipelineId: studioPipelineId,
|
|
4851
|
+
runId: event.runId,
|
|
4844
4852
|
level: "debug",
|
|
4845
4853
|
category,
|
|
4846
4854
|
event: `${event.node.kind}.started`,
|
|
@@ -4850,8 +4858,8 @@ function pipelineStageLog(pipelineId, runId, event) {
|
|
|
4850
4858
|
}
|
|
4851
4859
|
if (event.type === "stage_completed") {
|
|
4852
4860
|
return {
|
|
4853
|
-
pipelineId,
|
|
4854
|
-
runId,
|
|
4861
|
+
pipelineId: studioPipelineId,
|
|
4862
|
+
runId: event.runId,
|
|
4855
4863
|
level: "debug",
|
|
4856
4864
|
category,
|
|
4857
4865
|
event: `${event.node.kind}.completed`,
|
|
@@ -4863,8 +4871,8 @@ function pipelineStageLog(pipelineId, runId, event) {
|
|
|
4863
4871
|
};
|
|
4864
4872
|
}
|
|
4865
4873
|
return {
|
|
4866
|
-
pipelineId,
|
|
4867
|
-
runId,
|
|
4874
|
+
pipelineId: studioPipelineId,
|
|
4875
|
+
runId: event.runId,
|
|
4868
4876
|
level: "error",
|
|
4869
4877
|
category,
|
|
4870
4878
|
event: `${event.node.kind}.failed`,
|
|
@@ -4891,6 +4899,7 @@ function stageCategory(node) {
|
|
|
4891
4899
|
function nodeMetadata(node) {
|
|
4892
4900
|
const metadata = {
|
|
4893
4901
|
nodeId: node.id,
|
|
4902
|
+
nodePath: [...node.path],
|
|
4894
4903
|
kind: node.kind,
|
|
4895
4904
|
label: node.label
|
|
4896
4905
|
};
|
|
@@ -5052,17 +5061,24 @@ async function executePipelineRun(c, props, pipeline, body) {
|
|
|
5052
5061
|
if (body.metadata !== void 0) streamOptions.metadata = body.metadata;
|
|
5053
5062
|
if (props.logStore !== void 0) streamOptions.logStore = props.logStore;
|
|
5054
5063
|
if (props.runStore !== void 0) streamOptions.runStore = props.runStore;
|
|
5064
|
+
streamOptions.abortSignal = c.req.raw.signal;
|
|
5055
5065
|
return streamPipelineRun(c, streamOptions);
|
|
5056
5066
|
}
|
|
5057
5067
|
try {
|
|
5058
5068
|
await appendPipelineLog(props.logStore, pipelineRunStartedLog(pipeline, runId));
|
|
5059
|
-
const
|
|
5069
|
+
const result = await pipeline.pipeline.run({
|
|
5070
|
+
input: body.input,
|
|
5071
|
+
runId,
|
|
5072
|
+
metadata: body.metadata,
|
|
5073
|
+
abortSignal: c.req.raw.signal,
|
|
5060
5074
|
observer: {
|
|
5061
5075
|
async onEvent(event) {
|
|
5062
|
-
await appendPipelineLog(props.logStore, pipelineStageLog(pipeline.id
|
|
5076
|
+
await appendPipelineLog(props.logStore, pipelineStageLog(event, pipeline.id));
|
|
5063
5077
|
}
|
|
5064
|
-
}
|
|
5078
|
+
},
|
|
5079
|
+
failOnObserverError: true
|
|
5065
5080
|
});
|
|
5081
|
+
const output = result.output;
|
|
5066
5082
|
const jsonOutput = toJsonValue(output);
|
|
5067
5083
|
const endedAt = Date.now();
|
|
5068
5084
|
const successRecord = {
|
|
@@ -5127,19 +5143,24 @@ function streamPipelineRun(_c, props) {
|
|
|
5127
5143
|
async function* pipelineRunEvents(props) {
|
|
5128
5144
|
yield* emitPipelineLog(props.logStore, pipelineRunStartedLog(props.pipeline, props.runId));
|
|
5129
5145
|
const events = new AsyncEventQueue();
|
|
5130
|
-
const run = props.pipeline.pipeline.run(
|
|
5146
|
+
const run = props.pipeline.pipeline.run({
|
|
5147
|
+
input: props.input,
|
|
5148
|
+
runId: props.runId,
|
|
5149
|
+
metadata: props.metadata,
|
|
5150
|
+
abortSignal: props.abortSignal,
|
|
5131
5151
|
observer: {
|
|
5132
5152
|
async onEvent(event) {
|
|
5133
5153
|
const log = await appendPipelineLog(
|
|
5134
5154
|
props.logStore,
|
|
5135
|
-
pipelineStageLog(props.pipeline.id
|
|
5155
|
+
pipelineStageLog(event, props.pipeline.id)
|
|
5136
5156
|
);
|
|
5137
5157
|
if (log !== void 0) {
|
|
5138
5158
|
events.push({ type: "pipeline_log", log });
|
|
5139
5159
|
}
|
|
5140
5160
|
}
|
|
5141
|
-
}
|
|
5142
|
-
|
|
5161
|
+
},
|
|
5162
|
+
failOnObserverError: true
|
|
5163
|
+
}).then(async ({ output }) => {
|
|
5143
5164
|
const jsonOutput = toJsonValue(output);
|
|
5144
5165
|
const endedAt = Date.now();
|
|
5145
5166
|
const successRecord = {
|
|
@@ -5292,272 +5313,284 @@ function parsePipelineLogAfter(value) {
|
|
|
5292
5313
|
return after;
|
|
5293
5314
|
}
|
|
5294
5315
|
|
|
5295
|
-
// src/runtime/
|
|
5296
|
-
import {
|
|
5297
|
-
import {
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
const
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
}
|
|
5311
|
-
if (runId !== void 0) {
|
|
5312
|
-
options.runId = runId;
|
|
5313
|
-
}
|
|
5314
|
-
if (agentId !== void 0) {
|
|
5315
|
-
options.agentId = agentId;
|
|
5316
|
-
}
|
|
5317
|
-
if (sessionId !== void 0) {
|
|
5318
|
-
options.sessionId = sessionId;
|
|
5319
|
-
}
|
|
5320
|
-
return c.json({ questions: questions.list(options) });
|
|
5316
|
+
// src/runtime/sandbox-views.ts
|
|
5317
|
+
import { upgradeWebSocket } from "@hono/node-server";
|
|
5318
|
+
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
5319
|
+
import { WebSocket } from "ws";
|
|
5320
|
+
var minLeaseTimeoutMs = 5e3;
|
|
5321
|
+
var maxLeaseTimeoutMs = 12e4;
|
|
5322
|
+
var maxPendingWebSocketBytes = 1024 * 1024;
|
|
5323
|
+
function registerSandboxViewRoutes(app, registry) {
|
|
5324
|
+
const leases = /* @__PURE__ */ new Map();
|
|
5325
|
+
const upstreamSockets = /* @__PURE__ */ new Set();
|
|
5326
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/control", async (c) => {
|
|
5327
|
+
const resolved = await authorizedView(c, registry);
|
|
5328
|
+
if (resolved instanceof Response) return resolved;
|
|
5329
|
+
pruneExpiredLease(leases, viewKey(resolved));
|
|
5330
|
+
return c.json(resolved.view.source.control.snapshot());
|
|
5321
5331
|
});
|
|
5322
|
-
app.
|
|
5323
|
-
const
|
|
5324
|
-
if (
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
return c.json(result);
|
|
5332
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/connection", async (c) => {
|
|
5333
|
+
const resolved = await authorizedView(c, registry);
|
|
5334
|
+
if (resolved instanceof Response) return resolved;
|
|
5335
|
+
const response = {
|
|
5336
|
+
sandboxRef: resolved.entry.summary.ref,
|
|
5337
|
+
viewId: resolved.view.id,
|
|
5338
|
+
protocol: "novnc",
|
|
5339
|
+
websocketPath: `/sandboxes/${encodeURIComponent(resolved.entry.summary.ref)}/views/${encodeURIComponent(resolved.view.id)}/ws`,
|
|
5340
|
+
authentication: resolved.view.authentication.type === "password" ? { type: "password", password: resolved.view.authentication.password } : { type: "none" }
|
|
5341
|
+
};
|
|
5342
|
+
c.header("cache-control", "no-store");
|
|
5343
|
+
return c.json(response);
|
|
5335
5344
|
});
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
return
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
let body;
|
|
5346
|
-
try {
|
|
5347
|
-
body = await c.req.json();
|
|
5348
|
-
} catch {
|
|
5349
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
|
|
5350
|
-
}
|
|
5351
|
-
if (!isObject(body)) {
|
|
5352
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
5353
|
-
}
|
|
5354
|
-
if (!Array.isArray(body.answers)) {
|
|
5355
|
-
return { error: errorResponse(c, 400, "bad_request", "answers must be an array") };
|
|
5356
|
-
}
|
|
5357
|
-
const answers = [];
|
|
5358
|
-
for (const answer of body.answers) {
|
|
5359
|
-
if (!isObject(answer)) {
|
|
5360
|
-
return { error: errorResponse(c, 400, "bad_request", "answers must contain objects") };
|
|
5345
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/acquire", async (c) => {
|
|
5346
|
+
const resolved = await authorizedView(c, registry);
|
|
5347
|
+
if (resolved instanceof Response) return resolved;
|
|
5348
|
+
const body = await readControlBody(c);
|
|
5349
|
+
if (body instanceof Response) return body;
|
|
5350
|
+
const key = viewKey(resolved);
|
|
5351
|
+
pruneExpiredLease(leases, key);
|
|
5352
|
+
if (leases.has(key)) {
|
|
5353
|
+
return errorResponse(c, 409, "conflict", "Browser view is already controlled");
|
|
5361
5354
|
}
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5355
|
+
try {
|
|
5356
|
+
const lease = await resolved.view.source.control.acquireHumanControl({
|
|
5357
|
+
ownerId: body.ownerId,
|
|
5358
|
+
leaseTimeoutMs: body.leaseTimeoutMs,
|
|
5359
|
+
abortSignal: c.req.raw.signal
|
|
5360
|
+
});
|
|
5361
|
+
leases.set(key, lease);
|
|
5362
|
+
return c.json(leaseResponse(lease), 201);
|
|
5363
|
+
} catch (error) {
|
|
5364
|
+
return controlErrorResponse(c, error);
|
|
5367
5365
|
}
|
|
5368
|
-
|
|
5369
|
-
|
|
5366
|
+
});
|
|
5367
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/renew", async (c) => {
|
|
5368
|
+
const resolved = await authorizedView(c, registry);
|
|
5369
|
+
if (resolved instanceof Response) return resolved;
|
|
5370
|
+
const body = await readLeaseBody(c);
|
|
5371
|
+
if (body instanceof Response) return body;
|
|
5372
|
+
const key = viewKey(resolved);
|
|
5373
|
+
pruneExpiredLease(leases, key);
|
|
5374
|
+
const lease = leases.get(key);
|
|
5375
|
+
if (lease === void 0 || lease.id !== body.leaseId) {
|
|
5376
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is not active");
|
|
5370
5377
|
}
|
|
5371
|
-
|
|
5372
|
-
|
|
5378
|
+
try {
|
|
5379
|
+
lease.renew({ leaseTimeoutMs: body.leaseTimeoutMs });
|
|
5380
|
+
return c.json(leaseResponse(lease));
|
|
5381
|
+
} catch (error) {
|
|
5382
|
+
leases.delete(key);
|
|
5383
|
+
return controlErrorResponse(c, error);
|
|
5373
5384
|
}
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
if (
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5385
|
+
});
|
|
5386
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/release", async (c) => {
|
|
5387
|
+
const resolved = await authorizedView(c, registry);
|
|
5388
|
+
if (resolved instanceof Response) return resolved;
|
|
5389
|
+
const body = await readReleaseBody(c);
|
|
5390
|
+
if (body instanceof Response) return body;
|
|
5391
|
+
const key = viewKey(resolved);
|
|
5392
|
+
const lease = leases.get(key);
|
|
5393
|
+
if (lease === void 0 || lease.id !== body.leaseId) {
|
|
5394
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is not active");
|
|
5395
|
+
}
|
|
5396
|
+
lease.release();
|
|
5397
|
+
leases.delete(key);
|
|
5398
|
+
return c.json({ released: true });
|
|
5399
|
+
});
|
|
5400
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/ws", async (c) => {
|
|
5401
|
+
const resolved = await authorizedView(c, registry);
|
|
5402
|
+
if (resolved instanceof Response) return resolved;
|
|
5403
|
+
const upstreamUrl = `ws://${hostForUrl(resolved.host)}:${resolved.port}/websockify`;
|
|
5404
|
+
return upgradeWebSocket(c, createProxyEvents(upstreamUrl, upstreamSockets), {
|
|
5405
|
+
onError: () => void 0
|
|
5406
|
+
});
|
|
5407
|
+
});
|
|
5408
|
+
return () => {
|
|
5409
|
+
for (const lease of leases.values()) lease.release();
|
|
5410
|
+
leases.clear();
|
|
5411
|
+
for (const socket of upstreamSockets) socket.close(1001, "Studio closed");
|
|
5412
|
+
upstreamSockets.clear();
|
|
5413
|
+
};
|
|
5383
5414
|
}
|
|
5384
|
-
function
|
|
5385
|
-
|
|
5415
|
+
function createProxyEvents(upstreamUrl, sockets) {
|
|
5416
|
+
let upstream;
|
|
5417
|
+
let pendingBytes = 0;
|
|
5418
|
+
const pending = [];
|
|
5386
5419
|
return {
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
questions: prompts.questions
|
|
5403
|
-
};
|
|
5404
|
-
if (toolCallId !== void 0) request.toolCallId = toolCallId;
|
|
5405
|
-
const answers = await requestQuestion(questions, context, request);
|
|
5406
|
-
return control.skip(JSON.stringify({ answers }));
|
|
5407
|
-
}
|
|
5420
|
+
onOpen(_event, downstream) {
|
|
5421
|
+
const protocols = downstream.protocol === null || downstream.protocol.length === 0 ? void 0 : [downstream.protocol];
|
|
5422
|
+
upstream = new WebSocket(upstreamUrl, protocols, {
|
|
5423
|
+
headers: { origin: "http://127.0.0.1" }
|
|
5424
|
+
});
|
|
5425
|
+
sockets.add(upstream);
|
|
5426
|
+
upstream.binaryType = "arraybuffer";
|
|
5427
|
+
upstream.once("open", () => {
|
|
5428
|
+
for (const message of pending) upstream?.send(message);
|
|
5429
|
+
pending.length = 0;
|
|
5430
|
+
pendingBytes = 0;
|
|
5431
|
+
});
|
|
5432
|
+
upstream.on("message", (data, isBinary) => {
|
|
5433
|
+
const value = isBinary ? Uint8Array.from(Buffer.from(data)) : data.toString();
|
|
5434
|
+
downstream.send(value);
|
|
5408
5435
|
});
|
|
5436
|
+
upstream.once("close", (code, reason) => {
|
|
5437
|
+
sockets.delete(upstream);
|
|
5438
|
+
downstream.close(normalizeCloseCode(code), reason.toString("utf8").slice(0, 123));
|
|
5439
|
+
});
|
|
5440
|
+
upstream.once("error", () => downstream.close(1011, "noVNC upstream failed"));
|
|
5409
5441
|
},
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
if (
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
if (options.status === "resolved" && question.status === "pending") {
|
|
5416
|
-
return false;
|
|
5417
|
-
}
|
|
5418
|
-
if (options.runId !== void 0 && question.runId !== options.runId) {
|
|
5419
|
-
return false;
|
|
5420
|
-
}
|
|
5421
|
-
if (options.agentId !== void 0 && question.agentId !== options.agentId) {
|
|
5422
|
-
return false;
|
|
5442
|
+
onMessage(event, downstream) {
|
|
5443
|
+
void normalizeWebSocketMessage(event.data).then((message) => {
|
|
5444
|
+
if (upstream?.readyState === WebSocket.OPEN) {
|
|
5445
|
+
upstream.send(message);
|
|
5446
|
+
return;
|
|
5423
5447
|
}
|
|
5424
|
-
|
|
5425
|
-
|
|
5448
|
+
pendingBytes += typeof message === "string" ? Buffer.byteLength(message) : message.byteLength;
|
|
5449
|
+
if (pendingBytes > maxPendingWebSocketBytes) {
|
|
5450
|
+
downstream.close(1009, "Pending noVNC messages exceeded the limit");
|
|
5451
|
+
return;
|
|
5426
5452
|
}
|
|
5427
|
-
|
|
5428
|
-
}).
|
|
5453
|
+
pending.push(message);
|
|
5454
|
+
}).catch(() => downstream.close(1003, "Unsupported noVNC message"));
|
|
5429
5455
|
},
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
if (!isPendingQuestion(question) || question.runId !== runId) {
|
|
5434
|
-
continue;
|
|
5435
|
-
}
|
|
5436
|
-
const resolved = cancelQuestion(question);
|
|
5437
|
-
questions.set(question.id, resolved);
|
|
5438
|
-
question.emit?.({ type: "tool_question_result", question: resolved });
|
|
5439
|
-
question.resolve([]);
|
|
5440
|
-
cancelled.push(resolved);
|
|
5441
|
-
}
|
|
5442
|
-
return cancelled;
|
|
5456
|
+
onClose() {
|
|
5457
|
+
upstream?.close(1e3, "Viewer disconnected");
|
|
5458
|
+
if (upstream !== void 0) sockets.delete(upstream);
|
|
5443
5459
|
},
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
if (question === void 0) {
|
|
5447
|
-
return "missing";
|
|
5448
|
-
}
|
|
5449
|
-
if (!isPendingQuestion(question)) {
|
|
5450
|
-
return "resolved";
|
|
5451
|
-
}
|
|
5452
|
-
const resolved = resolveQuestion(question, answers);
|
|
5453
|
-
questions.set(id, resolved);
|
|
5454
|
-
question.emit?.({ type: "tool_question_result", question: resolved });
|
|
5455
|
-
question.resolve(answers);
|
|
5456
|
-
return publicQuestion(resolved);
|
|
5460
|
+
onError() {
|
|
5461
|
+
upstream?.close(1011, "Viewer connection failed");
|
|
5457
5462
|
}
|
|
5458
5463
|
};
|
|
5459
5464
|
}
|
|
5460
|
-
async function
|
|
5461
|
-
const
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
};
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
if (
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
}
|
|
5487
|
-
function normalizeQuestionPrompts(args) {
|
|
5488
|
-
if (!isObject(args)) {
|
|
5489
|
-
return { error: "ask_question requires a JSON object with questions." };
|
|
5490
|
-
}
|
|
5491
|
-
const rawQuestions = Array.isArray(args.questions) ? args.questions : [args];
|
|
5492
|
-
if (rawQuestions.length === 0) {
|
|
5493
|
-
return { error: "ask_question requires at least one question." };
|
|
5494
|
-
}
|
|
5495
|
-
const questions = [];
|
|
5496
|
-
for (const [index, question] of rawQuestions.entries()) {
|
|
5497
|
-
const normalized = normalizeQuestionPrompt(question, index);
|
|
5498
|
-
if (normalized === void 0) {
|
|
5499
|
-
return {
|
|
5500
|
-
error: "ask_question requires every question to include text and at least one choice."
|
|
5501
|
-
};
|
|
5465
|
+
async function authorizedView(c, registry) {
|
|
5466
|
+
const entry = registry.get(c.req.param("sandboxRef") ?? "");
|
|
5467
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5468
|
+
const view = entry.views.get(c.req.param("viewId") ?? "");
|
|
5469
|
+
if (view === void 0) return errorResponse(c, 404, "not_found", "Sandbox view not found");
|
|
5470
|
+
if (!await authorize(c, entry, view)) {
|
|
5471
|
+
return errorResponse(c, 403, "forbidden", "Sandbox view access denied");
|
|
5472
|
+
}
|
|
5473
|
+
const published = entry.inspector.publishedPorts?.find(
|
|
5474
|
+
(port) => port.containerPort === view.source.containerPort && port.protocol === "tcp" && isLoopbackAddress(port.host)
|
|
5475
|
+
);
|
|
5476
|
+
if (published === void 0) {
|
|
5477
|
+
return errorResponse(c, 503, "internal_error", "Sandbox view port is unavailable");
|
|
5478
|
+
}
|
|
5479
|
+
return { entry, view, host: published.host, port: published.hostPort };
|
|
5480
|
+
}
|
|
5481
|
+
async function authorize(c, entry, view) {
|
|
5482
|
+
if (view.access.mode === "authorize") {
|
|
5483
|
+
try {
|
|
5484
|
+
return await view.access.authorize({
|
|
5485
|
+
request: c.req.raw,
|
|
5486
|
+
sandboxRef: entry.summary.ref,
|
|
5487
|
+
viewId: view.id
|
|
5488
|
+
}) === true;
|
|
5489
|
+
} catch {
|
|
5490
|
+
return false;
|
|
5502
5491
|
}
|
|
5503
|
-
questions.push(normalized);
|
|
5504
5492
|
}
|
|
5505
|
-
|
|
5493
|
+
let remoteAddress;
|
|
5494
|
+
try {
|
|
5495
|
+
const address = getConnInfo(c).remote.address;
|
|
5496
|
+
if (address === void 0) return false;
|
|
5497
|
+
remoteAddress = address;
|
|
5498
|
+
} catch {
|
|
5499
|
+
return false;
|
|
5500
|
+
}
|
|
5501
|
+
if (!isLoopbackAddress(remoteAddress)) return false;
|
|
5502
|
+
const expectedOrigin = new URL(c.req.url).origin;
|
|
5503
|
+
const origin = c.req.header("origin");
|
|
5504
|
+
return origin === expectedOrigin || origin === void 0 && c.req.header("sec-fetch-site") === "same-origin";
|
|
5506
5505
|
}
|
|
5507
|
-
function
|
|
5508
|
-
|
|
5509
|
-
|
|
5506
|
+
async function readControlBody(c) {
|
|
5507
|
+
const body = await readJsonObject(c);
|
|
5508
|
+
if (body instanceof Response) return body;
|
|
5509
|
+
if (typeof body.ownerId !== "string" || body.ownerId.length === 0 || body.ownerId.length > 200) {
|
|
5510
|
+
return errorResponse(c, 400, "bad_request", "ownerId must be a non-empty string");
|
|
5510
5511
|
}
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
return void 0;
|
|
5512
|
+
if (!isLeaseTimeout(body.leaseTimeoutMs)) {
|
|
5513
|
+
return errorResponse(c, 400, "bad_request", "leaseTimeoutMs is outside the allowed range");
|
|
5514
5514
|
}
|
|
5515
|
-
return {
|
|
5516
|
-
id: typeof value.id === "string" && value.id.trim().length > 0 ? value.id.trim() : `question_${index + 1}`,
|
|
5517
|
-
question: value.question.trim(),
|
|
5518
|
-
choices
|
|
5519
|
-
};
|
|
5515
|
+
return { ownerId: body.ownerId, leaseTimeoutMs: body.leaseTimeoutMs };
|
|
5520
5516
|
}
|
|
5521
|
-
function
|
|
5522
|
-
|
|
5523
|
-
|
|
5517
|
+
async function readLeaseBody(c) {
|
|
5518
|
+
const body = await readJsonObject(c);
|
|
5519
|
+
if (body instanceof Response) return body;
|
|
5520
|
+
if (typeof body.leaseId !== "string" || body.leaseId.length === 0) {
|
|
5521
|
+
return errorResponse(c, 400, "bad_request", "leaseId must be a non-empty string");
|
|
5524
5522
|
}
|
|
5525
|
-
if (!
|
|
5526
|
-
return
|
|
5523
|
+
if (!isLeaseTimeout(body.leaseTimeoutMs)) {
|
|
5524
|
+
return errorResponse(c, 400, "bad_request", "leaseTimeoutMs is outside the allowed range");
|
|
5527
5525
|
}
|
|
5528
|
-
return {
|
|
5529
|
-
label: value.label.trim(),
|
|
5530
|
-
value: typeof value.value === "string" && value.value.trim().length > 0 ? value.value.trim() : value.label.trim()
|
|
5531
|
-
};
|
|
5526
|
+
return { leaseId: body.leaseId, leaseTimeoutMs: body.leaseTimeoutMs };
|
|
5532
5527
|
}
|
|
5533
|
-
function
|
|
5534
|
-
|
|
5528
|
+
async function readReleaseBody(c) {
|
|
5529
|
+
const body = await readJsonObject(c);
|
|
5530
|
+
if (body instanceof Response) return body;
|
|
5531
|
+
if (typeof body.leaseId !== "string" || body.leaseId.length === 0) {
|
|
5532
|
+
return errorResponse(c, 400, "bad_request", "leaseId must be a non-empty string");
|
|
5533
|
+
}
|
|
5534
|
+
return { leaseId: body.leaseId };
|
|
5535
5535
|
}
|
|
5536
|
-
function
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
}
|
|
5536
|
+
async function readJsonObject(c) {
|
|
5537
|
+
let body;
|
|
5538
|
+
try {
|
|
5539
|
+
body = await c.req.json();
|
|
5540
|
+
} catch {
|
|
5541
|
+
return errorResponse(c, 400, "bad_request", "Request body must be valid JSON");
|
|
5542
|
+
}
|
|
5543
|
+
return isRecord4(body) ? body : errorResponse(c, 400, "bad_request", "Request body must be an object");
|
|
5543
5544
|
}
|
|
5544
|
-
function
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5545
|
+
function controlErrorResponse(c, error) {
|
|
5546
|
+
if (isRecord4(error) && error.code === "human_controlled") {
|
|
5547
|
+
return errorResponse(c, 409, "conflict", "Browser view is already controlled");
|
|
5548
|
+
}
|
|
5549
|
+
if (isRecord4(error) && error.code === "invalid_state") {
|
|
5550
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is no longer active");
|
|
5551
|
+
}
|
|
5552
|
+
return errorResponse(c, 500, "internal_error", "Browser control operation failed");
|
|
5553
|
+
}
|
|
5554
|
+
function leaseResponse(lease) {
|
|
5555
|
+
return { id: lease.id, ownerId: lease.ownerId, expiresAt: lease.expiresAt };
|
|
5556
|
+
}
|
|
5557
|
+
function pruneExpiredLease(leases, key) {
|
|
5558
|
+
const lease = leases.get(key);
|
|
5559
|
+
if (lease !== void 0 && Date.parse(lease.expiresAt) <= Date.now()) {
|
|
5560
|
+
lease.release();
|
|
5561
|
+
leases.delete(key);
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
function viewKey(resolved) {
|
|
5565
|
+
return `${resolved.entry.summary.ref}\0${resolved.view.id}`;
|
|
5566
|
+
}
|
|
5567
|
+
function isLeaseTimeout(value) {
|
|
5568
|
+
return Number.isSafeInteger(value) && Number(value) >= minLeaseTimeoutMs && Number(value) <= maxLeaseTimeoutMs;
|
|
5569
|
+
}
|
|
5570
|
+
function isLoopbackAddress(value) {
|
|
5571
|
+
return value === "localhost" || value === "127.0.0.1" || value === "::1" || value.startsWith("127.") || value.startsWith("::ffff:127.");
|
|
5572
|
+
}
|
|
5573
|
+
function hostForUrl(host) {
|
|
5574
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
5575
|
+
}
|
|
5576
|
+
async function normalizeWebSocketMessage(value) {
|
|
5577
|
+
if (typeof value === "string") return value;
|
|
5578
|
+
if (value instanceof ArrayBuffer) return Uint8Array.from(new Uint8Array(value));
|
|
5579
|
+
if (ArrayBuffer.isView(value)) {
|
|
5580
|
+
return Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
5581
|
+
}
|
|
5582
|
+
if (value instanceof Blob) return Uint8Array.from(new Uint8Array(await value.arrayBuffer()));
|
|
5583
|
+
throw new TypeError("Unsupported noVNC WebSocket message type.");
|
|
5584
|
+
}
|
|
5585
|
+
function normalizeCloseCode(code) {
|
|
5586
|
+
return code >= 1e3 && code <= 4999 && code !== 1004 && code !== 1005 && code !== 1006 ? code : 1001;
|
|
5550
5587
|
}
|
|
5551
|
-
function
|
|
5552
|
-
|
|
5553
|
-
void emit;
|
|
5554
|
-
void resolve2;
|
|
5555
|
-
return rest;
|
|
5588
|
+
function isRecord4(value) {
|
|
5589
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5556
5590
|
}
|
|
5557
5591
|
|
|
5558
5592
|
// src/runtime/sandboxes.ts
|
|
5559
5593
|
import path from "path";
|
|
5560
|
-
var sandboxToolMetadataKey = /* @__PURE__ */ Symbol.for("anvia.sandbox.tool.metadata");
|
|
5561
5594
|
var maxFileResponseBytes = 10 * 1024 * 1024;
|
|
5562
5595
|
var defaultProcessLogBytes = 64 * 1024;
|
|
5563
5596
|
var maxProcessLogBytes = 1024 * 1024;
|
|
@@ -5576,51 +5609,41 @@ var StudioSandboxRegistry = class {
|
|
|
5576
5609
|
return this.entries.get(ref);
|
|
5577
5610
|
}
|
|
5578
5611
|
};
|
|
5579
|
-
function createStudioSandboxRegistry(agents) {
|
|
5580
|
-
const
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
draft.toolNames.add(tool.name);
|
|
5594
|
-
drafts.set(session, draft);
|
|
5595
|
-
}
|
|
5596
|
-
}
|
|
5597
|
-
const refCounts = /* @__PURE__ */ new Map();
|
|
5598
|
-
const entries = [...drafts.values()].sort(
|
|
5599
|
-
(left, right) => `${left.session.provider}\0${left.session.id}`.localeCompare(
|
|
5600
|
-
`${right.session.provider}\0${right.session.id}`
|
|
5601
|
-
)
|
|
5602
|
-
).map((draft) => {
|
|
5603
|
-
const baseRef = sandboxRef(draft.session.provider, draft.session.id);
|
|
5604
|
-
const occurrence = (refCounts.get(baseRef) ?? 0) + 1;
|
|
5605
|
-
refCounts.set(baseRef, occurrence);
|
|
5606
|
-
const ref = occurrence === 1 ? baseRef : `${baseRef}.${occurrence}`;
|
|
5612
|
+
function createStudioSandboxRegistry(agents, registrations) {
|
|
5613
|
+
const agentIds = new Set(agents.map((agent) => agent.id));
|
|
5614
|
+
const refs = /* @__PURE__ */ new Set();
|
|
5615
|
+
const entries = registrations.map((registration, index) => {
|
|
5616
|
+
validateRegistration(registration, index, agentIds);
|
|
5617
|
+
const inspector = registration.inspector;
|
|
5618
|
+
const ref = sandboxRef(inspector.provider, inspector.id);
|
|
5619
|
+
if (refs.has(ref)) {
|
|
5620
|
+
throw new TypeError(
|
|
5621
|
+
`sandboxes contains a duplicate provider/id registration: ${inspector.provider}/${inspector.id}`
|
|
5622
|
+
);
|
|
5623
|
+
}
|
|
5624
|
+
refs.add(ref);
|
|
5625
|
+
const views = new Map((registration.views ?? []).map((view) => [view.id, view]));
|
|
5607
5626
|
return {
|
|
5608
|
-
|
|
5627
|
+
inspector,
|
|
5628
|
+
views,
|
|
5609
5629
|
summary: {
|
|
5610
5630
|
ref,
|
|
5611
|
-
id:
|
|
5612
|
-
provider:
|
|
5613
|
-
workdir:
|
|
5614
|
-
agentIds:
|
|
5615
|
-
toolNames:
|
|
5631
|
+
id: inspector.id,
|
|
5632
|
+
provider: inspector.provider,
|
|
5633
|
+
workdir: inspector.workdir,
|
|
5634
|
+
agentIds: sortedUnique(registration.agentIds ?? []),
|
|
5635
|
+
toolNames: sortedUnique(registration.toolNames ?? []),
|
|
5636
|
+
views: [...views.values()].map((view) => ({ id: view.id, label: view.label, protocol: view.source.protocol })).sort((left, right) => left.id.localeCompare(right.id)),
|
|
5616
5637
|
capabilities: {
|
|
5617
|
-
files:
|
|
5618
|
-
ports: supportsPorts(
|
|
5619
|
-
processes: supportsProcesses(
|
|
5638
|
+
files: supportsFiles(inspector),
|
|
5639
|
+
ports: supportsPorts(inspector),
|
|
5640
|
+
processes: supportsProcesses(inspector),
|
|
5641
|
+
views: views.size > 0
|
|
5620
5642
|
}
|
|
5621
5643
|
}
|
|
5622
5644
|
};
|
|
5623
5645
|
});
|
|
5646
|
+
entries.sort((left, right) => left.summary.ref.localeCompare(right.summary.ref));
|
|
5624
5647
|
return new StudioSandboxRegistry(entries);
|
|
5625
5648
|
}
|
|
5626
5649
|
function registerSandboxRoutes(app, registry) {
|
|
@@ -5629,24 +5652,19 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5629
5652
|
app.get("/sandboxes", (c) => c.json({ sandboxes: registry.list() }));
|
|
5630
5653
|
app.get("/sandboxes/:sandboxRef", (c) => {
|
|
5631
5654
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5632
|
-
|
|
5633
|
-
return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5634
|
-
}
|
|
5635
|
-
return c.json(copySummary(entry.summary));
|
|
5655
|
+
return entry === void 0 ? errorResponse(c, 404, "not_found", "Sandbox not found") : c.json(copySummary(entry.summary));
|
|
5636
5656
|
});
|
|
5637
5657
|
app.get("/sandboxes/:sandboxRef/files", async (c) => {
|
|
5638
5658
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5639
|
-
if (entry === void 0)
|
|
5640
|
-
|
|
5641
|
-
}
|
|
5659
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5660
|
+
if (!supportsFiles(entry.inspector)) return unsupportedSandboxOperation(c, "files");
|
|
5642
5661
|
try {
|
|
5643
5662
|
const requestedPath = c.req.query("path") ?? ".";
|
|
5644
5663
|
const normalizedPath = normalizeSandboxPath(requestedPath, true);
|
|
5645
|
-
const entries = await listSandboxFiles(entry.session, normalizedPath);
|
|
5646
5664
|
return c.json({
|
|
5647
5665
|
sandboxRef: entry.summary.ref,
|
|
5648
5666
|
path: normalizedPath,
|
|
5649
|
-
entries
|
|
5667
|
+
entries: await listSandboxFiles(entry.inspector, normalizedPath, c.req.raw.signal)
|
|
5650
5668
|
});
|
|
5651
5669
|
} catch (error) {
|
|
5652
5670
|
return sandboxErrorResponse(c, error);
|
|
@@ -5654,9 +5672,8 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5654
5672
|
});
|
|
5655
5673
|
app.get("/sandboxes/:sandboxRef/files/content", async (c) => {
|
|
5656
5674
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5657
|
-
if (entry === void 0)
|
|
5658
|
-
|
|
5659
|
-
}
|
|
5675
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5676
|
+
if (!supportsFiles(entry.inspector)) return unsupportedSandboxOperation(c, "files.content");
|
|
5660
5677
|
try {
|
|
5661
5678
|
const requestedPath = c.req.query("path");
|
|
5662
5679
|
if (requestedPath === void 0) {
|
|
@@ -5667,8 +5684,11 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5667
5684
|
throw new SandboxRouteError(400, "bad_request", "download must be 1 when provided");
|
|
5668
5685
|
}
|
|
5669
5686
|
const normalizedPath = normalizeSandboxPath(requestedPath, false);
|
|
5670
|
-
const
|
|
5671
|
-
|
|
5687
|
+
const siblings = await listSandboxFiles(
|
|
5688
|
+
entry.inspector,
|
|
5689
|
+
path.posix.dirname(normalizedPath),
|
|
5690
|
+
c.req.raw.signal
|
|
5691
|
+
);
|
|
5672
5692
|
const file = siblings.find((candidate) => candidate.path === normalizedPath);
|
|
5673
5693
|
if (file === void 0) {
|
|
5674
5694
|
throw new SandboxRouteError(404, "not_found", "Sandbox file not found");
|
|
@@ -5680,25 +5700,24 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5680
5700
|
"Sandbox content path must refer to a regular file"
|
|
5681
5701
|
);
|
|
5682
5702
|
}
|
|
5683
|
-
if (file.size !== void 0 && file.size > maxFileResponseBytes)
|
|
5703
|
+
if (file.size !== void 0 && file.size > maxFileResponseBytes)
|
|
5684
5704
|
throw fileTooLarge(file.size);
|
|
5685
|
-
|
|
5686
|
-
|
|
5705
|
+
const rawBytes = await entry.inspector.readFile({
|
|
5706
|
+
path: normalizedPath,
|
|
5707
|
+
abortSignal: c.req.raw.signal
|
|
5708
|
+
});
|
|
5687
5709
|
if (!(rawBytes instanceof Uint8Array)) {
|
|
5688
5710
|
throw new TypeError("Sandbox readFile returned an invalid byte payload");
|
|
5689
5711
|
}
|
|
5690
|
-
if (rawBytes.byteLength > maxFileResponseBytes)
|
|
5691
|
-
|
|
5692
|
-
}
|
|
5693
|
-
const bytes = new Uint8Array(rawBytes.byteLength);
|
|
5694
|
-
bytes.set(rawBytes);
|
|
5712
|
+
if (rawBytes.byteLength > maxFileResponseBytes) throw fileTooLarge(rawBytes.byteLength);
|
|
5713
|
+
const bytes = rawBytes.slice();
|
|
5695
5714
|
const disposition = download === "1" ? "attachment" : "inline";
|
|
5696
5715
|
const filename = encodeURIComponent(path.posix.basename(normalizedPath));
|
|
5697
5716
|
return new Response(bytes, {
|
|
5698
5717
|
headers: {
|
|
5699
5718
|
"cache-control": "no-store",
|
|
5700
5719
|
"content-disposition": `${disposition}; filename*=UTF-8''${filename}`,
|
|
5701
|
-
"content-length":
|
|
5720
|
+
"content-length": `${bytes.byteLength}`,
|
|
5702
5721
|
"content-type": "application/octet-stream",
|
|
5703
5722
|
"x-content-type-options": "nosniff"
|
|
5704
5723
|
}
|
|
@@ -5709,16 +5728,12 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5709
5728
|
});
|
|
5710
5729
|
app.get("/sandboxes/:sandboxRef/ports", (c) => {
|
|
5711
5730
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5712
|
-
if (entry === void 0)
|
|
5713
|
-
|
|
5714
|
-
}
|
|
5715
|
-
if (!supportsPorts(entry.session)) {
|
|
5716
|
-
return unsupportedSandboxOperation(c, "ports");
|
|
5717
|
-
}
|
|
5731
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5732
|
+
if (!supportsPorts(entry.inspector)) return unsupportedSandboxOperation(c, "ports");
|
|
5718
5733
|
try {
|
|
5719
5734
|
return c.json({
|
|
5720
5735
|
sandboxRef: entry.summary.ref,
|
|
5721
|
-
ports: normalizePorts(entry.
|
|
5736
|
+
ports: normalizePorts(entry.inspector.publishedPorts)
|
|
5722
5737
|
});
|
|
5723
5738
|
} catch (error) {
|
|
5724
5739
|
return sandboxErrorResponse(c, error);
|
|
@@ -5726,16 +5741,16 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5726
5741
|
});
|
|
5727
5742
|
app.get("/sandboxes/:sandboxRef/processes", async (c) => {
|
|
5728
5743
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5729
|
-
if (entry === void 0)
|
|
5730
|
-
|
|
5731
|
-
}
|
|
5732
|
-
if (!supportsProcesses(entry.session)) {
|
|
5744
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5745
|
+
if (!supportsProcesses(entry.inspector)) {
|
|
5733
5746
|
return unsupportedSandboxOperation(c, "processes");
|
|
5734
5747
|
}
|
|
5735
5748
|
try {
|
|
5736
5749
|
return c.json({
|
|
5737
5750
|
sandboxRef: entry.summary.ref,
|
|
5738
|
-
processes: normalizeProcesses(
|
|
5751
|
+
processes: normalizeProcesses(
|
|
5752
|
+
await entry.inspector.listProcesses({ abortSignal: c.req.raw.signal })
|
|
5753
|
+
)
|
|
5739
5754
|
});
|
|
5740
5755
|
} catch (error) {
|
|
5741
5756
|
return sandboxErrorResponse(c, error);
|
|
@@ -5743,21 +5758,25 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5743
5758
|
});
|
|
5744
5759
|
app.get("/sandboxes/:sandboxRef/processes/:processId/logs", async (c) => {
|
|
5745
5760
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5746
|
-
if (entry === void 0)
|
|
5747
|
-
|
|
5748
|
-
}
|
|
5749
|
-
if (!supportsProcesses(entry.session)) {
|
|
5761
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5762
|
+
if (!supportsProcesses(entry.inspector)) {
|
|
5750
5763
|
return unsupportedSandboxOperation(c, "processes.logs");
|
|
5751
5764
|
}
|
|
5752
5765
|
try {
|
|
5753
5766
|
const tailBytes = parseTailBytes(c.req.query("tailBytes"));
|
|
5754
5767
|
const processId = c.req.param("processId");
|
|
5755
|
-
const processes = normalizeProcesses(
|
|
5768
|
+
const processes = normalizeProcesses(
|
|
5769
|
+
await entry.inspector.listProcesses({ abortSignal: c.req.raw.signal })
|
|
5770
|
+
);
|
|
5756
5771
|
if (!processes.some((process2) => process2.id === processId)) {
|
|
5757
5772
|
throw new SandboxRouteError(404, "not_found", "Sandbox process not found");
|
|
5758
5773
|
}
|
|
5759
5774
|
const logs = normalizeProcessLogs(
|
|
5760
|
-
await entry.
|
|
5775
|
+
await entry.inspector.readProcessLogs({
|
|
5776
|
+
processId,
|
|
5777
|
+
tailBytes,
|
|
5778
|
+
abortSignal: c.req.raw.signal
|
|
5779
|
+
}),
|
|
5761
5780
|
tailBytes
|
|
5762
5781
|
);
|
|
5763
5782
|
return c.json({
|
|
@@ -5774,21 +5793,86 @@ async function sandboxNoStore(c, next) {
|
|
|
5774
5793
|
c.header("cache-control", "no-store");
|
|
5775
5794
|
await next();
|
|
5776
5795
|
}
|
|
5777
|
-
function
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5796
|
+
function validateRegistration(registration, index, knownAgentIds) {
|
|
5797
|
+
if (!isRecord5(registration) || !isInspector(registration.inspector)) {
|
|
5798
|
+
throw new TypeError(`sandboxes[${index}] must contain a valid inspector.`);
|
|
5799
|
+
}
|
|
5800
|
+
const inspector = registration.inspector;
|
|
5801
|
+
const files = supportsFiles(inspector);
|
|
5802
|
+
if (inspector.listFiles === void 0 !== (inspector.readFile === void 0)) {
|
|
5803
|
+
throw new TypeError(`sandboxes[${index}] must register both file inspector methods.`);
|
|
5804
|
+
}
|
|
5805
|
+
const processes = supportsProcesses(inspector);
|
|
5806
|
+
if (inspector.listProcesses === void 0 !== (inspector.readProcessLogs === void 0)) {
|
|
5807
|
+
throw new TypeError(`sandboxes[${index}] must register both process inspector methods.`);
|
|
5808
|
+
}
|
|
5809
|
+
if (!files && !supportsPorts(inspector) && !processes) {
|
|
5810
|
+
throw new TypeError(`sandboxes[${index}] inspector exposes no capabilities.`);
|
|
5811
|
+
}
|
|
5812
|
+
for (const agentId of sortedUnique(registration.agentIds ?? [])) {
|
|
5813
|
+
if (!knownAgentIds.has(agentId)) {
|
|
5814
|
+
throw new TypeError(`sandboxes[${index}] references an unknown agent: ${agentId}`);
|
|
5815
|
+
}
|
|
5816
|
+
}
|
|
5817
|
+
sortedUnique(registration.toolNames ?? []);
|
|
5818
|
+
validateViews(registration.views, inspector, index);
|
|
5819
|
+
}
|
|
5820
|
+
function validateViews(views, inspector, registrationIndex) {
|
|
5821
|
+
if (views === void 0) return;
|
|
5822
|
+
if (!Array.isArray(views))
|
|
5823
|
+
throw new TypeError(`sandboxes[${registrationIndex}].views must be an array.`);
|
|
5824
|
+
if (!supportsPorts(inspector)) {
|
|
5825
|
+
throw new TypeError(`sandboxes[${registrationIndex}] views require published port inspection.`);
|
|
5826
|
+
}
|
|
5827
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5828
|
+
for (const [viewIndex, view] of views.entries()) {
|
|
5829
|
+
const prefix = `sandboxes[${registrationIndex}].views[${viewIndex}]`;
|
|
5830
|
+
const candidate = view;
|
|
5831
|
+
if (!isRecord5(candidate) || typeof candidate.id !== "string" || !/^[a-z0-9](?:[a-z0-9_-]{0,62})$/.test(candidate.id)) {
|
|
5832
|
+
throw new TypeError(`${prefix}.id must be a stable lowercase identifier.`);
|
|
5833
|
+
}
|
|
5834
|
+
if (ids.has(candidate.id)) throw new TypeError(`${prefix}.id is duplicated: ${candidate.id}`);
|
|
5835
|
+
ids.add(candidate.id);
|
|
5836
|
+
if (typeof candidate.label !== "string" || candidate.label.length === 0) {
|
|
5837
|
+
throw new TypeError(`${prefix}.label must be a non-empty string.`);
|
|
5838
|
+
}
|
|
5839
|
+
const source = candidate.source;
|
|
5840
|
+
if (!isRecord5(source) || source.protocol !== "novnc" || !isPort(source.containerPort) || !isRecord5(source.control) || typeof source.control.snapshot !== "function" || typeof source.control.acquireHumanControl !== "function") {
|
|
5841
|
+
throw new TypeError(`${prefix}.source must be a valid noVNC view source.`);
|
|
5842
|
+
}
|
|
5843
|
+
const port = inspector.publishedPorts?.find(
|
|
5844
|
+
(candidate2) => candidate2.containerPort === source.containerPort && candidate2.protocol === "tcp"
|
|
5845
|
+
);
|
|
5846
|
+
if (port === void 0 || !isLoopbackHost(port.host)) {
|
|
5847
|
+
throw new TypeError(`${prefix}.source must resolve to a loopback-published TCP port.`);
|
|
5848
|
+
}
|
|
5849
|
+
const access = candidate.access;
|
|
5850
|
+
if (!isRecord5(access) || access.mode !== "local" && access.mode !== "authorize") {
|
|
5851
|
+
throw new TypeError(`${prefix}.access must explicitly select local or authorize mode.`);
|
|
5852
|
+
}
|
|
5853
|
+
if (access.mode === "authorize" && typeof access.authorize !== "function") {
|
|
5854
|
+
throw new TypeError(`${prefix}.access.authorize must be a function.`);
|
|
5855
|
+
}
|
|
5856
|
+
const authentication = candidate.authentication;
|
|
5857
|
+
if (!isRecord5(authentication) || authentication.type !== "none" && authentication.type !== "password") {
|
|
5858
|
+
throw new TypeError(`${prefix}.authentication must explicitly select none or password.`);
|
|
5859
|
+
}
|
|
5860
|
+
if (authentication.type === "password" && (typeof authentication.password !== "string" || authentication.password.length === 0)) {
|
|
5861
|
+
throw new TypeError(`${prefix}.authentication.password must be a non-empty string.`);
|
|
5862
|
+
}
|
|
5781
5863
|
}
|
|
5782
|
-
return isSandboxSession(metadata.session) ? metadata.session : void 0;
|
|
5783
5864
|
}
|
|
5784
|
-
function
|
|
5785
|
-
return
|
|
5865
|
+
function isInspector(value) {
|
|
5866
|
+
return isRecord5(value) && typeof value.id === "string" && value.id.length > 0 && typeof value.provider === "string" && value.provider.length > 0 && typeof value.workdir === "string" && value.workdir.length > 0;
|
|
5786
5867
|
}
|
|
5787
|
-
function
|
|
5788
|
-
return
|
|
5868
|
+
function supportsFiles(inspector) {
|
|
5869
|
+
return typeof inspector.listFiles === "function" && typeof inspector.readFile === "function";
|
|
5789
5870
|
}
|
|
5790
|
-
function
|
|
5791
|
-
return
|
|
5871
|
+
function supportsPorts(inspector) {
|
|
5872
|
+
return Array.isArray(inspector.publishedPorts);
|
|
5873
|
+
}
|
|
5874
|
+
function supportsProcesses(inspector) {
|
|
5875
|
+
return typeof inspector.listProcesses === "function" && typeof inspector.readProcessLogs === "function";
|
|
5792
5876
|
}
|
|
5793
5877
|
function sandboxRef(provider, id) {
|
|
5794
5878
|
return Buffer.from(JSON.stringify([provider, id]), "utf8").toString("base64url");
|
|
@@ -5798,22 +5882,26 @@ function copySummary(summary) {
|
|
|
5798
5882
|
...summary,
|
|
5799
5883
|
agentIds: [...summary.agentIds],
|
|
5800
5884
|
toolNames: [...summary.toolNames],
|
|
5885
|
+
views: summary.views.map((view) => ({ ...view })),
|
|
5801
5886
|
capabilities: { ...summary.capabilities }
|
|
5802
5887
|
};
|
|
5803
5888
|
}
|
|
5804
|
-
|
|
5805
|
-
return
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5889
|
+
function isLoopbackHost(host) {
|
|
5890
|
+
return host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
5891
|
+
}
|
|
5892
|
+
async function listSandboxFiles(inspector, filePath, abortSignal) {
|
|
5893
|
+
return normalizeFileEntries(await inspector.listFiles({ path: filePath, abortSignal })).sort(
|
|
5894
|
+
(left, right) => {
|
|
5895
|
+
if (left.type === "directory" && right.type !== "directory") return -1;
|
|
5896
|
+
if (left.type !== "directory" && right.type === "directory") return 1;
|
|
5897
|
+
return left.path.localeCompare(right.path);
|
|
5898
|
+
}
|
|
5899
|
+
);
|
|
5810
5900
|
}
|
|
5811
5901
|
function normalizeFileEntries(value) {
|
|
5812
|
-
if (!Array.isArray(value))
|
|
5813
|
-
throw new TypeError("Sandbox listFiles returned an invalid payload");
|
|
5814
|
-
}
|
|
5902
|
+
if (!Array.isArray(value)) throw new TypeError("Sandbox listFiles returned an invalid payload");
|
|
5815
5903
|
return value.map((item) => {
|
|
5816
|
-
if (!
|
|
5904
|
+
if (!isRecord5(item) || typeof item.path !== "string" || !isFileType(item.type)) {
|
|
5817
5905
|
throw new TypeError("Sandbox listFiles returned an invalid entry");
|
|
5818
5906
|
}
|
|
5819
5907
|
let entryPath;
|
|
@@ -5824,7 +5912,7 @@ function normalizeFileEntries(value) {
|
|
|
5824
5912
|
}
|
|
5825
5913
|
const entry = { path: entryPath, type: item.type };
|
|
5826
5914
|
if (item.size !== void 0) {
|
|
5827
|
-
if (
|
|
5915
|
+
if (!Number.isSafeInteger(item.size) || Number(item.size) < 0) {
|
|
5828
5916
|
throw new TypeError("Sandbox listFiles returned an invalid file size");
|
|
5829
5917
|
}
|
|
5830
5918
|
entry.size = item.size;
|
|
@@ -5833,11 +5921,10 @@ function normalizeFileEntries(value) {
|
|
|
5833
5921
|
});
|
|
5834
5922
|
}
|
|
5835
5923
|
function normalizePorts(value) {
|
|
5836
|
-
if (!Array.isArray(value))
|
|
5924
|
+
if (!Array.isArray(value))
|
|
5837
5925
|
throw new TypeError("Sandbox publishedPorts returned an invalid payload");
|
|
5838
|
-
}
|
|
5839
5926
|
return value.map((item) => {
|
|
5840
|
-
if (!
|
|
5927
|
+
if (!isRecord5(item) || !isPort(item.containerPort) || typeof item.host !== "string" || !isPort(item.hostPort) || typeof item.protocol !== "string") {
|
|
5841
5928
|
throw new TypeError("Sandbox publishedPorts returned an invalid entry");
|
|
5842
5929
|
}
|
|
5843
5930
|
return {
|
|
@@ -5849,17 +5936,16 @@ function normalizePorts(value) {
|
|
|
5849
5936
|
});
|
|
5850
5937
|
}
|
|
5851
5938
|
function normalizeProcesses(value) {
|
|
5852
|
-
if (!Array.isArray(value))
|
|
5939
|
+
if (!Array.isArray(value))
|
|
5853
5940
|
throw new TypeError("Sandbox listProcesses returned an invalid payload");
|
|
5854
|
-
}
|
|
5855
5941
|
return value.map((item) => {
|
|
5856
|
-
if (!
|
|
5942
|
+
if (!isRecord5(item) || typeof item.id !== "string" || typeof item.command !== "string" || !Array.isArray(item.args) || !item.args.every((arg) => typeof arg === "string") || !isProcessStatus(item.status) || typeof item.startedAt !== "string") {
|
|
5857
5943
|
throw new TypeError("Sandbox listProcesses returned an invalid entry");
|
|
5858
5944
|
}
|
|
5859
5945
|
const process2 = {
|
|
5860
5946
|
id: item.id,
|
|
5861
5947
|
command: item.command,
|
|
5862
|
-
args: item.args,
|
|
5948
|
+
args: [...item.args],
|
|
5863
5949
|
status: item.status,
|
|
5864
5950
|
startedAt: item.startedAt
|
|
5865
5951
|
};
|
|
@@ -5870,11 +5956,11 @@ function normalizeProcesses(value) {
|
|
|
5870
5956
|
});
|
|
5871
5957
|
}
|
|
5872
5958
|
function normalizeProcessLogs(value, maxBytes) {
|
|
5873
|
-
if (!
|
|
5959
|
+
if (!isRecord5(value) || !(value.stdout instanceof Uint8Array) || !(value.stderr instanceof Uint8Array) || typeof value.stdoutTruncated !== "boolean" || typeof value.stderrTruncated !== "boolean") {
|
|
5874
5960
|
throw new TypeError("Sandbox readProcessLogs returned an invalid payload");
|
|
5875
5961
|
}
|
|
5876
|
-
const stdout =
|
|
5877
|
-
const stderr =
|
|
5962
|
+
const stdout = boundedLogBytes(value.stdout, maxBytes);
|
|
5963
|
+
const stderr = boundedLogBytes(value.stderr, maxBytes);
|
|
5878
5964
|
return {
|
|
5879
5965
|
stdout: stdout.text,
|
|
5880
5966
|
stderr: stderr.text,
|
|
@@ -5882,21 +5968,13 @@ function normalizeProcessLogs(value, maxBytes) {
|
|
|
5882
5968
|
stderrTruncated: value.stderrTruncated || stderr.truncated
|
|
5883
5969
|
};
|
|
5884
5970
|
}
|
|
5885
|
-
function
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
}
|
|
5890
|
-
if (maxBytes === 0) {
|
|
5891
|
-
return { text: "", truncated: true };
|
|
5892
|
-
}
|
|
5893
|
-
let start = bytes.byteLength - maxBytes;
|
|
5894
|
-
while (start < bytes.byteLength && (bytes[start] ?? 0) >> 6 === 2) {
|
|
5895
|
-
start += 1;
|
|
5896
|
-
}
|
|
5971
|
+
function boundedLogBytes(value, maxBytes) {
|
|
5972
|
+
if (maxBytes === 0) return { text: "", truncated: value.byteLength > 0 };
|
|
5973
|
+
let start = Math.max(0, value.byteLength - maxBytes);
|
|
5974
|
+
while (start < value.byteLength && ((value[start] ?? 0) & 192) === 128) start += 1;
|
|
5897
5975
|
return {
|
|
5898
|
-
text: new TextDecoder().decode(
|
|
5899
|
-
truncated:
|
|
5976
|
+
text: new TextDecoder("utf-8", { fatal: true }).decode(value.subarray(start)),
|
|
5977
|
+
truncated: start > 0
|
|
5900
5978
|
};
|
|
5901
5979
|
}
|
|
5902
5980
|
function normalizeSandboxPath(input, allowRoot) {
|
|
@@ -5919,9 +5997,7 @@ function normalizeSandboxPath(input, allowRoot) {
|
|
|
5919
5997
|
return normalized;
|
|
5920
5998
|
}
|
|
5921
5999
|
function parseTailBytes(value) {
|
|
5922
|
-
if (value === void 0)
|
|
5923
|
-
return defaultProcessLogBytes;
|
|
5924
|
-
}
|
|
6000
|
+
if (value === void 0) return defaultProcessLogBytes;
|
|
5925
6001
|
const parsed = Number(value);
|
|
5926
6002
|
if (!Number.isInteger(parsed) || parsed < 0 || parsed > maxProcessLogBytes) {
|
|
5927
6003
|
throw new SandboxRouteError(
|
|
@@ -5942,15 +6018,13 @@ function sandboxErrorResponse(c, error) {
|
|
|
5942
6018
|
if (error instanceof SandboxRouteError) {
|
|
5943
6019
|
return errorResponse(c, error.status, error.code, error.message);
|
|
5944
6020
|
}
|
|
5945
|
-
const
|
|
5946
|
-
if (
|
|
5947
|
-
|
|
5948
|
-
}
|
|
5949
|
-
if (name === "SandboxFileSizeError") {
|
|
6021
|
+
const code = errorCode(error);
|
|
6022
|
+
if (code === "invalid_path") return errorResponse(c, 400, "bad_request", errorMessage(error));
|
|
6023
|
+
if (code === "file_too_large") {
|
|
5950
6024
|
return errorResponse(c, 413, "payload_too_large", errorMessage(error));
|
|
5951
6025
|
}
|
|
5952
|
-
if (
|
|
5953
|
-
return errorResponse(c, 409, "conflict", "Sandbox
|
|
6026
|
+
if (code === "invalid_state" || code === "sandbox_not_found") {
|
|
6027
|
+
return errorResponse(c, 409, "conflict", "Sandbox is no longer available");
|
|
5954
6028
|
}
|
|
5955
6029
|
return errorResponse(
|
|
5956
6030
|
c,
|
|
@@ -5976,6 +6050,18 @@ var SandboxRouteError = class extends Error {
|
|
|
5976
6050
|
status;
|
|
5977
6051
|
code;
|
|
5978
6052
|
};
|
|
6053
|
+
function sortedUnique(values) {
|
|
6054
|
+
if (!Array.isArray(values)) throw new TypeError("Sandbox associations must be arrays.");
|
|
6055
|
+
const result = /* @__PURE__ */ new Set();
|
|
6056
|
+
for (const value of values) {
|
|
6057
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
6058
|
+
throw new TypeError("Sandbox associations must contain non-empty strings.");
|
|
6059
|
+
}
|
|
6060
|
+
if (result.has(value)) throw new TypeError(`Sandbox association is duplicated: ${value}`);
|
|
6061
|
+
result.add(value);
|
|
6062
|
+
}
|
|
6063
|
+
return [...result].sort();
|
|
6064
|
+
}
|
|
5979
6065
|
function isFileType(value) {
|
|
5980
6066
|
return value === "file" || value === "directory" || value === "symlink" || value === "other";
|
|
5981
6067
|
}
|
|
@@ -5985,14 +6071,14 @@ function isProcessStatus(value) {
|
|
|
5985
6071
|
function isPort(value) {
|
|
5986
6072
|
return Number.isInteger(value) && Number(value) >= 1 && Number(value) <= 65535;
|
|
5987
6073
|
}
|
|
5988
|
-
function
|
|
5989
|
-
return typeof value === "object" && value !== null;
|
|
6074
|
+
function isRecord5(value) {
|
|
6075
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5990
6076
|
}
|
|
5991
|
-
function
|
|
5992
|
-
return
|
|
6077
|
+
function errorCode(error) {
|
|
6078
|
+
return isRecord5(error) && typeof error.code === "string" ? error.code : void 0;
|
|
5993
6079
|
}
|
|
5994
6080
|
function errorMessage(error) {
|
|
5995
|
-
return
|
|
6081
|
+
return isRecord5(error) && typeof error.message === "string" ? error.message : "Sandbox failed";
|
|
5996
6082
|
}
|
|
5997
6083
|
|
|
5998
6084
|
// src/runtime/sessions.ts
|
|
@@ -6143,6 +6229,16 @@ function createInMemoryStudioStore() {
|
|
|
6143
6229
|
}
|
|
6144
6230
|
var InMemoryStudioStore = class {
|
|
6145
6231
|
kind = "memory";
|
|
6232
|
+
compaction = {
|
|
6233
|
+
snapshot: ({ scope }) => {
|
|
6234
|
+
const messages = this.sessions.get(scope.sessionId)?.messages ?? [];
|
|
6235
|
+
return Promise.resolve({
|
|
6236
|
+
revision: memoryRevision(messages),
|
|
6237
|
+
messages: cloneMessages(messages)
|
|
6238
|
+
});
|
|
6239
|
+
},
|
|
6240
|
+
replacePrefix: (options) => this.replaceCompactionPrefix(options)
|
|
6241
|
+
};
|
|
6146
6242
|
sessions = /* @__PURE__ */ new Map();
|
|
6147
6243
|
traces = /* @__PURE__ */ new Map();
|
|
6148
6244
|
pipelineLogs = /* @__PURE__ */ new Map();
|
|
@@ -6184,20 +6280,20 @@ var InMemoryStudioStore = class {
|
|
|
6184
6280
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6185
6281
|
return materializeSession(session);
|
|
6186
6282
|
}
|
|
6187
|
-
load(
|
|
6188
|
-
return Promise.resolve(this.sessions.get(
|
|
6283
|
+
load({ scope }) {
|
|
6284
|
+
return Promise.resolve(cloneMessages(this.sessions.get(scope.sessionId)?.messages ?? []));
|
|
6189
6285
|
}
|
|
6190
6286
|
append(input) {
|
|
6191
|
-
const session = this.sessions.get(input.
|
|
6287
|
+
const session = this.sessions.get(input.scope.sessionId);
|
|
6192
6288
|
if (session !== void 0) {
|
|
6193
|
-
session.messages.push(...input.messages);
|
|
6289
|
+
session.messages.push(...cloneMessages(input.messages));
|
|
6194
6290
|
session.messageCount = session.messages.length;
|
|
6195
6291
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6196
6292
|
}
|
|
6197
6293
|
return Promise.resolve();
|
|
6198
6294
|
}
|
|
6199
|
-
clear(
|
|
6200
|
-
const session = this.sessions.get(
|
|
6295
|
+
clear({ scope }) {
|
|
6296
|
+
const session = this.sessions.get(scope.sessionId);
|
|
6201
6297
|
if (session !== void 0) {
|
|
6202
6298
|
session.messages = [];
|
|
6203
6299
|
session.runs = [];
|
|
@@ -6208,13 +6304,26 @@ var InMemoryStudioStore = class {
|
|
|
6208
6304
|
}
|
|
6209
6305
|
async recordError(input) {
|
|
6210
6306
|
await this.saveSessionRunTranscript({
|
|
6211
|
-
id: input.
|
|
6212
|
-
runId: studioRunId(input.
|
|
6307
|
+
id: input.scope.sessionId,
|
|
6308
|
+
runId: studioRunId(input.scope) ?? input.runId,
|
|
6213
6309
|
transcript: transcriptFromMessages(input.messages),
|
|
6214
6310
|
status: "error",
|
|
6215
6311
|
error: serializeJsonError(input.error)
|
|
6216
6312
|
});
|
|
6217
6313
|
}
|
|
6314
|
+
replaceCompactionPrefix(input) {
|
|
6315
|
+
if (!Number.isSafeInteger(input.messageCount) || input.messageCount < 1) {
|
|
6316
|
+
throw new RangeError("messageCount must be a positive integer.");
|
|
6317
|
+
}
|
|
6318
|
+
const session = this.sessions.get(input.scope.sessionId);
|
|
6319
|
+
if (session === void 0 || memoryRevision(session.messages) !== input.revision || input.messageCount > session.messages.length) {
|
|
6320
|
+
return Promise.resolve({ status: "conflict" });
|
|
6321
|
+
}
|
|
6322
|
+
session.messages.splice(0, input.messageCount, structuredClone(input.replacement));
|
|
6323
|
+
session.messageCount = session.messages.length;
|
|
6324
|
+
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6325
|
+
return Promise.resolve({ status: "committed" });
|
|
6326
|
+
}
|
|
6218
6327
|
saveSessionRunTranscript(input) {
|
|
6219
6328
|
const session = this.sessions.get(input.id);
|
|
6220
6329
|
if (session === void 0) {
|
|
@@ -6340,18 +6449,24 @@ function sessionSummary(session) {
|
|
|
6340
6449
|
function materializeSession(session) {
|
|
6341
6450
|
return {
|
|
6342
6451
|
...sessionSummary(session),
|
|
6343
|
-
messages:
|
|
6452
|
+
messages: cloneMessages(session.messages),
|
|
6344
6453
|
transcript: renumberTranscript(session.runs.flatMap((run) => run.transcript))
|
|
6345
6454
|
};
|
|
6346
6455
|
}
|
|
6456
|
+
function cloneMessages(messages) {
|
|
6457
|
+
return structuredClone([...messages]);
|
|
6458
|
+
}
|
|
6347
6459
|
function traceAgentId(trace) {
|
|
6348
6460
|
const nestedMetadata = trace.metadata?.metadata;
|
|
6349
6461
|
return isJsonObject(nestedMetadata) && typeof nestedMetadata.agentId === "string" ? nestedMetadata.agentId : void 0;
|
|
6350
6462
|
}
|
|
6351
|
-
function studioRunId(
|
|
6352
|
-
const value =
|
|
6463
|
+
function studioRunId(scope) {
|
|
6464
|
+
const value = scope.metadata?.studioRunId;
|
|
6353
6465
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6354
6466
|
}
|
|
6467
|
+
function memoryRevision(messages) {
|
|
6468
|
+
return JSON.stringify(messages);
|
|
6469
|
+
}
|
|
6355
6470
|
function serializeJsonError(error) {
|
|
6356
6471
|
if (error instanceof Error) {
|
|
6357
6472
|
return {
|
|
@@ -6687,6 +6802,7 @@ var Studio = class {
|
|
|
6687
6802
|
options;
|
|
6688
6803
|
studio;
|
|
6689
6804
|
server;
|
|
6805
|
+
websocketServer;
|
|
6690
6806
|
sigintHandler;
|
|
6691
6807
|
constructor(targets = [], options = {}) {
|
|
6692
6808
|
this.options = studioOptionsFromTargets(targets, options);
|
|
@@ -6711,9 +6827,15 @@ var Studio = class {
|
|
|
6711
6827
|
this.studio = createStudioApp(this.options);
|
|
6712
6828
|
const port = serveOptions.port ?? Number(process.env.RUNNER_PORT ?? 4021);
|
|
6713
6829
|
const serverOptions = {
|
|
6714
|
-
fetch: (request) => this.fetch(request),
|
|
6830
|
+
fetch: (request, env) => this.studio.app.fetch(request, env),
|
|
6715
6831
|
port
|
|
6716
6832
|
};
|
|
6833
|
+
this.websocketServer = new WebSocketServer({ noServer: true });
|
|
6834
|
+
serverOptions.websocket = {
|
|
6835
|
+
// @types/ws overloads are stricter than Hono's structural adapter, while Hono officially
|
|
6836
|
+
// supports ws.WebSocketServer. Keep the interop assertion at this single ownership boundary.
|
|
6837
|
+
server: this.websocketServer
|
|
6838
|
+
};
|
|
6717
6839
|
if (serveOptions.hostname !== void 0) serverOptions.hostname = serveOptions.hostname;
|
|
6718
6840
|
this.server = serve(serverOptions);
|
|
6719
6841
|
const log = serveOptions.log ?? true;
|
|
@@ -6758,6 +6880,8 @@ var Studio = class {
|
|
|
6758
6880
|
}
|
|
6759
6881
|
this.server?.close();
|
|
6760
6882
|
this.server = void 0;
|
|
6883
|
+
this.websocketServer?.close();
|
|
6884
|
+
this.websocketServer = void 0;
|
|
6761
6885
|
this.studio.close();
|
|
6762
6886
|
}
|
|
6763
6887
|
logAddress(host, port) {
|
|
@@ -6836,6 +6960,21 @@ function studioOptionsFromTargets(targets, options) {
|
|
|
6836
6960
|
if (options.models !== void 0) runtimeOptions.models = options.models;
|
|
6837
6961
|
if (options.stores !== void 0) runtimeOptions.stores = options.stores;
|
|
6838
6962
|
if (options.ui !== void 0) runtimeOptions.ui = options.ui;
|
|
6963
|
+
if (options.sandboxes !== void 0) {
|
|
6964
|
+
runtimeOptions.sandboxes = options.sandboxes.map((registration) => {
|
|
6965
|
+
let snapshot = { inspector: registration.inspector };
|
|
6966
|
+
if (registration.agentIds !== void 0) {
|
|
6967
|
+
snapshot = { ...snapshot, agentIds: [...registration.agentIds] };
|
|
6968
|
+
}
|
|
6969
|
+
if (registration.toolNames !== void 0) {
|
|
6970
|
+
snapshot = { ...snapshot, toolNames: [...registration.toolNames] };
|
|
6971
|
+
}
|
|
6972
|
+
if (registration.views !== void 0) {
|
|
6973
|
+
snapshot = { ...snapshot, views: [...registration.views] };
|
|
6974
|
+
}
|
|
6975
|
+
return snapshot;
|
|
6976
|
+
});
|
|
6977
|
+
}
|
|
6839
6978
|
return runtimeOptions;
|
|
6840
6979
|
}
|
|
6841
6980
|
function inferStudioAgents(agents, quickPrompts) {
|
|
@@ -6877,11 +7016,11 @@ function uniqueAgentId(baseId, ids) {
|
|
|
6877
7016
|
function agentMetadata(agent) {
|
|
6878
7017
|
const metadata = {
|
|
6879
7018
|
staticContextCount: staticContextDocuments(agent).length,
|
|
6880
|
-
dynamicContextCount:
|
|
7019
|
+
dynamicContextCount: vectorContexts(agent).length,
|
|
6881
7020
|
dynamicToolCount: getAgentToolState3(agent).toolIndexes.length,
|
|
6882
7021
|
hasLifecycle: agent.lifecycle !== void 0,
|
|
6883
7022
|
hasOutputSchema: agent.outputSchema !== void 0,
|
|
6884
|
-
observerCount: agent.observers.length,
|
|
7023
|
+
observerCount: Object.keys(agent.observability?.observers ?? {}).length,
|
|
6885
7024
|
approvalToolCount: agent.tools.filter(toolRequiresApproval).length
|
|
6886
7025
|
};
|
|
6887
7026
|
if (agent.defaultMaxTurns !== void 0) metadata.defaultMaxTurns = agent.defaultMaxTurns;
|
|
@@ -6898,9 +7037,8 @@ function createStudioApp(options) {
|
|
|
6898
7037
|
const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
|
|
6899
7038
|
const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
|
|
6900
7039
|
const evalMap = new Map(options.evals.map((suite) => [suite.id ?? suite.name, suite]));
|
|
6901
|
-
const
|
|
6902
|
-
const
|
|
6903
|
-
const sandboxRegistry = createStudioSandboxRegistry(agents);
|
|
7040
|
+
const continuationRegistry = createStudioContinuationRegistry();
|
|
7041
|
+
const sandboxRegistry = createStudioSandboxRegistry(agents, options.sandboxes ?? []);
|
|
6904
7042
|
const memorySources = createStudioMemorySourceRegistry(agents, stores.sessions);
|
|
6905
7043
|
const app = new HonoApp();
|
|
6906
7044
|
const uiOptions = isStudioUiEnabled(options.ui) ? resolveStudioUiOptions(options.ui) : void 0;
|
|
@@ -6951,8 +7089,7 @@ function createStudioApp(options) {
|
|
|
6951
7089
|
registerMcpRoutes(app, { agentMap });
|
|
6952
7090
|
registerToolRoutes(app, { agentMap });
|
|
6953
7091
|
registerSandboxRoutes(app, sandboxRegistry);
|
|
6954
|
-
|
|
6955
|
-
registerQuestionRoutes(app, questionRuntime);
|
|
7092
|
+
const closeSandboxViews = registerSandboxViewRoutes(app, sandboxRegistry);
|
|
6956
7093
|
registerObservabilityRoutes(app, observabilityHub);
|
|
6957
7094
|
registerEvalRoutes(app, {
|
|
6958
7095
|
evals: options.evals,
|
|
@@ -6972,8 +7109,7 @@ function createStudioApp(options) {
|
|
|
6972
7109
|
agentMap,
|
|
6973
7110
|
stores,
|
|
6974
7111
|
modelRegistry,
|
|
6975
|
-
|
|
6976
|
-
questionRuntime
|
|
7112
|
+
continuationRegistry
|
|
6977
7113
|
});
|
|
6978
7114
|
if (memorySources.size > 0 || stores.sessions !== void 0) {
|
|
6979
7115
|
registerMemoryRoutes(app, {
|
|
@@ -7005,6 +7141,8 @@ function createStudioApp(options) {
|
|
|
7005
7141
|
return buildConfig(options, agents, pipelines, stores, sandboxRegistry.size);
|
|
7006
7142
|
},
|
|
7007
7143
|
close() {
|
|
7144
|
+
continuationRegistry.clear();
|
|
7145
|
+
closeSandboxViews();
|
|
7008
7146
|
}
|
|
7009
7147
|
};
|
|
7010
7148
|
if (stores.sessions !== void 0) Object.assign(studio, { sessionStore: stores.sessions });
|
|
@@ -7012,30 +7150,37 @@ function createStudioApp(options) {
|
|
|
7012
7150
|
return studio;
|
|
7013
7151
|
}
|
|
7014
7152
|
function withStudioTraceObserver(studioAgent, traceStore) {
|
|
7015
|
-
if (traceStore === void 0
|
|
7016
|
-
|
|
7153
|
+
if (traceStore === void 0) return studioAgent;
|
|
7154
|
+
const existing = studioAgent.agent.observability;
|
|
7155
|
+
const studioObserver = existing?.observers.studio;
|
|
7156
|
+
if (studioObserver !== void 0 && !(studioObserver instanceof StudioTraceObserver)) {
|
|
7157
|
+
throw new TypeError('Studio reserves the observer name "studio" for local trace persistence.');
|
|
7158
|
+
}
|
|
7159
|
+
for (const [name, observer] of Object.entries(existing?.observers ?? {})) {
|
|
7160
|
+
if (observer instanceof StudioTraceObserver && name !== "studio") {
|
|
7161
|
+
throw new TypeError('StudioTraceObserver must be registered with the name "studio".');
|
|
7162
|
+
}
|
|
7017
7163
|
}
|
|
7018
7164
|
return {
|
|
7019
7165
|
...studioAgent,
|
|
7020
7166
|
agent: cloneAgent(studioAgent.agent, {
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7167
|
+
observability: {
|
|
7168
|
+
observers: {
|
|
7169
|
+
...existing?.observers ?? {},
|
|
7170
|
+
studio: studioObserver ?? new StudioTraceObserver({ store: traceStore })
|
|
7171
|
+
},
|
|
7172
|
+
primaryTrace: "studio",
|
|
7173
|
+
errorPolicy: existing?.errorPolicy ?? "ignore"
|
|
7174
|
+
}
|
|
7025
7175
|
})
|
|
7026
7176
|
};
|
|
7027
7177
|
}
|
|
7028
|
-
function hasStudioTraceObserver(agent) {
|
|
7029
|
-
return agent.observers.some(
|
|
7030
|
-
(registration) => registration.observer instanceof StudioTraceObserver
|
|
7031
|
-
);
|
|
7032
|
-
}
|
|
7033
7178
|
|
|
7034
7179
|
// src/storage/sqlite-store.ts
|
|
7035
7180
|
import { mkdirSync } from "fs";
|
|
7036
7181
|
import { createRequire } from "module";
|
|
7037
7182
|
import { dirname, resolve } from "path";
|
|
7038
|
-
import { isJsonValue as
|
|
7183
|
+
import { isJsonValue as isJsonValue3 } from "@anvia/core/completion";
|
|
7039
7184
|
var DatabaseSync;
|
|
7040
7185
|
function createSqliteSessionStore(options = {}) {
|
|
7041
7186
|
return new SqliteSessionStore(options.path ?? ":memory:");
|
|
@@ -7046,6 +7191,13 @@ var SqliteSessionStore = class {
|
|
|
7046
7191
|
}
|
|
7047
7192
|
path;
|
|
7048
7193
|
kind = "sqlite";
|
|
7194
|
+
compaction = {
|
|
7195
|
+
snapshot: ({ scope }) => {
|
|
7196
|
+
const messages = this.listSessionMessages(scope.sessionId);
|
|
7197
|
+
return Promise.resolve({ revision: memoryRevision2(messages), messages });
|
|
7198
|
+
},
|
|
7199
|
+
replacePrefix: (options) => this.replaceCompactionPrefix(options)
|
|
7200
|
+
};
|
|
7049
7201
|
db;
|
|
7050
7202
|
listSessions(options) {
|
|
7051
7203
|
const db = this.database();
|
|
@@ -7117,8 +7269,8 @@ var SqliteSessionStore = class {
|
|
|
7117
7269
|
}
|
|
7118
7270
|
return this.getSession(id);
|
|
7119
7271
|
}
|
|
7120
|
-
load(
|
|
7121
|
-
const session = this.getSession(
|
|
7272
|
+
load({ scope }) {
|
|
7273
|
+
const session = this.getSession(scope.sessionId);
|
|
7122
7274
|
return Promise.resolve(session?.messages ?? []);
|
|
7123
7275
|
}
|
|
7124
7276
|
append(input) {
|
|
@@ -7129,20 +7281,20 @@ var SqliteSessionStore = class {
|
|
|
7129
7281
|
`SELECT id, agent_id, title, metadata_json, created_at, updated_at
|
|
7130
7282
|
FROM anvia_studio_sessions
|
|
7131
7283
|
WHERE id = $id`
|
|
7132
|
-
).get({ $id: input.
|
|
7284
|
+
).get({ $id: input.scope.sessionId });
|
|
7133
7285
|
if (row === void 0) {
|
|
7134
7286
|
db.exec("ROLLBACK");
|
|
7135
7287
|
return Promise.resolve();
|
|
7136
7288
|
}
|
|
7137
7289
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7138
|
-
const nextIndex = this.nextMessageIndex(input.
|
|
7139
|
-
this.insertMessages(input.
|
|
7290
|
+
const nextIndex = this.nextMessageIndex(input.scope.sessionId);
|
|
7291
|
+
this.insertMessages(input.scope.sessionId, input.messages, nextIndex, updatedAt);
|
|
7140
7292
|
db.prepare(
|
|
7141
7293
|
`UPDATE anvia_studio_sessions
|
|
7142
7294
|
SET updated_at = $updatedAt
|
|
7143
7295
|
WHERE id = $id`
|
|
7144
7296
|
).run({
|
|
7145
|
-
$id: input.
|
|
7297
|
+
$id: input.scope.sessionId,
|
|
7146
7298
|
$updatedAt: updatedAt
|
|
7147
7299
|
});
|
|
7148
7300
|
db.exec("COMMIT");
|
|
@@ -7154,7 +7306,7 @@ var SqliteSessionStore = class {
|
|
|
7154
7306
|
throw error;
|
|
7155
7307
|
}
|
|
7156
7308
|
}
|
|
7157
|
-
clear(
|
|
7309
|
+
clear({ scope }) {
|
|
7158
7310
|
const db = this.database();
|
|
7159
7311
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7160
7312
|
try {
|
|
@@ -7164,14 +7316,14 @@ var SqliteSessionStore = class {
|
|
|
7164
7316
|
SET updated_at = $updatedAt
|
|
7165
7317
|
WHERE id = $id`
|
|
7166
7318
|
).run({
|
|
7167
|
-
$id:
|
|
7319
|
+
$id: scope.sessionId,
|
|
7168
7320
|
$updatedAt: updatedAt
|
|
7169
7321
|
});
|
|
7170
7322
|
db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
|
|
7171
|
-
$id:
|
|
7323
|
+
$id: scope.sessionId
|
|
7172
7324
|
});
|
|
7173
7325
|
db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({
|
|
7174
|
-
$id:
|
|
7326
|
+
$id: scope.sessionId
|
|
7175
7327
|
});
|
|
7176
7328
|
db.exec("COMMIT");
|
|
7177
7329
|
return Promise.resolve();
|
|
@@ -7183,17 +7335,51 @@ var SqliteSessionStore = class {
|
|
|
7183
7335
|
}
|
|
7184
7336
|
}
|
|
7185
7337
|
async recordError(input) {
|
|
7186
|
-
const runId = studioRunId2(input.
|
|
7187
|
-
const existing = this.getSessionRun(input.
|
|
7338
|
+
const runId = studioRunId2(input.scope) ?? input.runId;
|
|
7339
|
+
const existing = this.getSessionRun(input.scope.sessionId, runId);
|
|
7188
7340
|
const transcript = existing === void 0 || parseJsonArray(existing.transcript_json).length === 0 ? transcriptFromMessages(input.messages) : parseJsonArray(existing.transcript_json);
|
|
7189
7341
|
await this.saveSessionRunTranscript({
|
|
7190
|
-
id: input.
|
|
7342
|
+
id: input.scope.sessionId,
|
|
7191
7343
|
runId,
|
|
7192
7344
|
transcript,
|
|
7193
7345
|
status: "error",
|
|
7194
7346
|
error: serializeJsonError2(input.error)
|
|
7195
7347
|
});
|
|
7196
7348
|
}
|
|
7349
|
+
replaceCompactionPrefix(input) {
|
|
7350
|
+
if (!Number.isSafeInteger(input.messageCount) || input.messageCount < 1) {
|
|
7351
|
+
throw new RangeError("messageCount must be a positive integer.");
|
|
7352
|
+
}
|
|
7353
|
+
const db = this.database();
|
|
7354
|
+
try {
|
|
7355
|
+
db.exec("BEGIN IMMEDIATE");
|
|
7356
|
+
const messages = this.listSessionMessages(input.scope.sessionId);
|
|
7357
|
+
if (memoryRevision2(messages) !== input.revision || input.messageCount > messages.length || this.getSessionRow(input.scope.sessionId) === void 0) {
|
|
7358
|
+
db.exec("ROLLBACK");
|
|
7359
|
+
return Promise.resolve({ status: "conflict" });
|
|
7360
|
+
}
|
|
7361
|
+
db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
|
|
7362
|
+
$id: input.scope.sessionId
|
|
7363
|
+
});
|
|
7364
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7365
|
+
this.insertMessages(
|
|
7366
|
+
input.scope.sessionId,
|
|
7367
|
+
[input.replacement, ...messages.slice(input.messageCount)],
|
|
7368
|
+
0,
|
|
7369
|
+
updatedAt
|
|
7370
|
+
);
|
|
7371
|
+
db.prepare(
|
|
7372
|
+
`UPDATE anvia_studio_sessions
|
|
7373
|
+
SET updated_at = $updatedAt
|
|
7374
|
+
WHERE id = $id`
|
|
7375
|
+
).run({ $id: input.scope.sessionId, $updatedAt: updatedAt });
|
|
7376
|
+
db.exec("COMMIT");
|
|
7377
|
+
return Promise.resolve({ status: "committed" });
|
|
7378
|
+
} catch (error) {
|
|
7379
|
+
if (db.isTransaction) db.exec("ROLLBACK");
|
|
7380
|
+
throw error;
|
|
7381
|
+
}
|
|
7382
|
+
}
|
|
7197
7383
|
saveSessionRunTranscript(input) {
|
|
7198
7384
|
const db = this.database();
|
|
7199
7385
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7889,7 +8075,7 @@ var SqliteSessionStore = class {
|
|
|
7889
8075
|
);
|
|
7890
8076
|
messages.forEach((message, messageOffset) => {
|
|
7891
8077
|
const messageIndex = startIndex + messageOffset;
|
|
7892
|
-
if (message.metadata !== void 0 && !
|
|
8078
|
+
if (message.metadata !== void 0 && !isJsonValue3(message.metadata)) {
|
|
7893
8079
|
throw new TypeError("Studio message metadata must be a strict JSON value.");
|
|
7894
8080
|
}
|
|
7895
8081
|
insertMessage.run({
|
|
@@ -7990,6 +8176,9 @@ function messageParts(message) {
|
|
|
7990
8176
|
if (message.role === "system") {
|
|
7991
8177
|
return [{ type: "text", value: { type: "text", text: message.content } }];
|
|
7992
8178
|
}
|
|
8179
|
+
if (typeof message.content === "string") {
|
|
8180
|
+
return [{ type: "text", value: { type: "text", text: message.content } }];
|
|
8181
|
+
}
|
|
7993
8182
|
return message.content.map((content) => ({
|
|
7994
8183
|
type: content.type,
|
|
7995
8184
|
value: content
|
|
@@ -7998,40 +8187,40 @@ function messageParts(message) {
|
|
|
7998
8187
|
function messageFromRows(row, partRows) {
|
|
7999
8188
|
const parts = partRows.map((partRow) => JSON.parse(partRow.part_json));
|
|
8000
8189
|
const metadata = parseJsonValue(row.metadata_json);
|
|
8001
|
-
if (metadata !== void 0 && !
|
|
8002
|
-
throw new TypeError("Stored Studio message metadata is not a strict JSON
|
|
8190
|
+
if (metadata !== void 0 && (!isJsonValue3(metadata) || typeof metadata !== "object" || metadata === null || Array.isArray(metadata))) {
|
|
8191
|
+
throw new TypeError("Stored Studio message metadata is not a strict JSON object.");
|
|
8003
8192
|
}
|
|
8004
8193
|
if (row.role === "system") {
|
|
8005
|
-
|
|
8194
|
+
let message = {
|
|
8006
8195
|
role: "system",
|
|
8007
8196
|
content: systemContentFromParts(parts)
|
|
8008
8197
|
};
|
|
8009
|
-
if (metadata !== void 0) message
|
|
8198
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8010
8199
|
return message;
|
|
8011
8200
|
}
|
|
8012
8201
|
if (row.role === "user") {
|
|
8013
|
-
|
|
8202
|
+
let message = {
|
|
8014
8203
|
role: "user",
|
|
8015
8204
|
content: parts
|
|
8016
8205
|
};
|
|
8017
|
-
if (metadata !== void 0) message
|
|
8206
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8018
8207
|
return message;
|
|
8019
8208
|
}
|
|
8020
8209
|
if (row.role === "assistant") {
|
|
8021
|
-
|
|
8210
|
+
let message = {
|
|
8022
8211
|
role: "assistant",
|
|
8023
8212
|
content: parts
|
|
8024
8213
|
};
|
|
8025
|
-
if (row.message_id !== null) message
|
|
8026
|
-
if (metadata !== void 0) message
|
|
8214
|
+
if (row.message_id !== null) message = { ...message, id: row.message_id };
|
|
8215
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8027
8216
|
return message;
|
|
8028
8217
|
}
|
|
8029
8218
|
if (row.role === "tool") {
|
|
8030
|
-
|
|
8219
|
+
let message = {
|
|
8031
8220
|
role: "tool",
|
|
8032
8221
|
content: parts
|
|
8033
8222
|
};
|
|
8034
|
-
if (metadata !== void 0) message
|
|
8223
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8035
8224
|
return message;
|
|
8036
8225
|
}
|
|
8037
8226
|
throw new Error(`Unsupported stored message role: ${row.role}`);
|
|
@@ -8116,10 +8305,13 @@ function parseJsonValue(value) {
|
|
|
8116
8305
|
}
|
|
8117
8306
|
return JSON.parse(value);
|
|
8118
8307
|
}
|
|
8119
|
-
function studioRunId2(
|
|
8120
|
-
const value =
|
|
8308
|
+
function studioRunId2(scope) {
|
|
8309
|
+
const value = scope.metadata?.studioRunId;
|
|
8121
8310
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
8122
8311
|
}
|
|
8312
|
+
function memoryRevision2(messages) {
|
|
8313
|
+
return JSON.stringify(messages);
|
|
8314
|
+
}
|
|
8123
8315
|
function serializeJsonError2(error) {
|
|
8124
8316
|
if (error instanceof Error) {
|
|
8125
8317
|
return {
|