@anvia/studio 1.0.0-rc.1 → 1.0.0-rc.11
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 +177 -116
- package/dist/index.js +1418 -1219
- 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-QGX73TSQ-D9QAGlAY.js +65 -0
- package/dist/ui/assets/chunk-QGX73TSQ-D9QAGlAY.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-BtvoCaW7.js +12 -0
- package/dist/ui/assets/index-BtvoCaW7.js.map +1 -0
- package/dist/ui/assets/index-CFbK-pX6.css +2 -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-DZeLh92t.js +2 -0
- package/dist/ui/assets/{knowledge-page-vwFOGhkh.js.map → knowledge-page-DZeLh92t.js.map} +1 -1
- package/dist/ui/assets/mcps-page-CDPMO2x9.js +2 -0
- package/dist/ui/assets/{mcps-page-Bsu_-w7A.js.map → mcps-page-CDPMO2x9.js.map} +1 -1
- package/dist/ui/assets/memory-page-D3UhjpTb.js +4 -0
- package/dist/ui/assets/memory-page-D3UhjpTb.js.map +1 -0
- package/dist/ui/assets/pipelines-page-BP8R6NI7.js +8 -0
- package/dist/ui/assets/pipelines-page-BP8R6NI7.js.map +1 -0
- package/dist/ui/assets/{renderers-5kT5HxmZ.js → renderers-DHMEAblV.js} +3 -3
- package/dist/ui/assets/{renderers-5kT5HxmZ.js.map → renderers-DHMEAblV.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-eA8uRLZQ.js +2 -0
- package/dist/ui/assets/{status-page-D-BUKV_r.js.map → status-page-eA8uRLZQ.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-D3-SlGqQ.js +2 -0
- package/dist/ui/assets/{tool-runner-6-N-OOCI.js.map → tool-runner-D3-SlGqQ.js.map} +1 -1
- package/dist/ui/assets/tools-page-D3gnQf1_.js +2 -0
- package/dist/ui/assets/{tools-page-BEQJgFX0.js.map → tools-page-D3gnQf1_.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-BmjdZLbS.js +3 -0
- package/dist/ui/assets/transcript-item-BmjdZLbS.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 +0 -4
- 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/interactions";
|
|
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]();
|
|
@@ -1809,7 +1870,7 @@ function withAgentRunCancellation(events, onCancel) {
|
|
|
1809
1870
|
}
|
|
1810
1871
|
return next;
|
|
1811
1872
|
}
|
|
1812
|
-
if (next.value
|
|
1873
|
+
if (isTerminalAgentEvent(next.value)) {
|
|
1813
1874
|
terminal = true;
|
|
1814
1875
|
}
|
|
1815
1876
|
return next;
|
|
@@ -1886,10 +1947,10 @@ function createPersistedStreamingSessionTranscript(props) {
|
|
|
1886
1947
|
return;
|
|
1887
1948
|
}
|
|
1888
1949
|
acceptTranscriptStreamEvent(transcript, event);
|
|
1889
|
-
if (event
|
|
1950
|
+
if (isTerminalAgentEvent(event)) {
|
|
1890
1951
|
assignTranscriptRunDuration(transcript, Date.now() - props.startedAt);
|
|
1891
1952
|
}
|
|
1892
|
-
const eventStatus = event.type === "
|
|
1953
|
+
const eventStatus = event.type === "response" || event.type === "interaction" || event.type === "blocked" ? event.type === "interaction" ? "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 === "
|
|
1963
|
+
const generatedMessages = event.type === "response" || event.type === "interaction" || event.type === "blocked" ? event.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 === "interaction") {
|
|
2112
|
+
const interaction = event.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 === "
|
|
2170
|
+
if ((event.type === "response" || event.type === "interaction" || event.type === "blocked") && event.trace?.traceId !== void 0) {
|
|
2118
2171
|
assignTranscriptTraceId(transcript, event.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,17 @@ 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
|
+
}
|
|
2586
|
+
function isTerminalAgentEvent(event) {
|
|
2587
|
+
return event.type === "response" || event.type === "interaction" || event.type === "blocked" || event.type === "error";
|
|
2588
|
+
}
|
|
2496
2589
|
|
|
2497
2590
|
// src/runtime/session-logs.ts
|
|
2498
2591
|
async function appendSessionLog(store, input) {
|
|
@@ -2627,6 +2720,29 @@ function memorySavedLog(props) {
|
|
|
2627
2720
|
metadata
|
|
2628
2721
|
};
|
|
2629
2722
|
}
|
|
2723
|
+
function memoryCompactedLog(props) {
|
|
2724
|
+
const metadata = {
|
|
2725
|
+
originalMessageCount: props.originalMessageCount,
|
|
2726
|
+
compactedMessageCount: props.compactedMessageCount,
|
|
2727
|
+
retainedMessageCount: props.retainedMessageCount,
|
|
2728
|
+
originalTokenCount: props.originalTokenCount,
|
|
2729
|
+
compactedTokenCount: props.compactedTokenCount,
|
|
2730
|
+
retainedTokenCount: props.retainedTokenCount,
|
|
2731
|
+
resultTokenCount: props.resultTokenCount,
|
|
2732
|
+
attempts: props.attempts
|
|
2733
|
+
};
|
|
2734
|
+
const usage = usageSummary(props.usage);
|
|
2735
|
+
if (usage !== void 0) metadata.usage = usage;
|
|
2736
|
+
return {
|
|
2737
|
+
sessionId: props.sessionId,
|
|
2738
|
+
runId: props.runId,
|
|
2739
|
+
level: "info",
|
|
2740
|
+
category: "memory",
|
|
2741
|
+
event: "memory.compacted",
|
|
2742
|
+
message: "Session memory compacted",
|
|
2743
|
+
metadata
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2630
2746
|
function runFailedLog(sessionId, runId, error, startedAt) {
|
|
2631
2747
|
return {
|
|
2632
2748
|
sessionId,
|
|
@@ -2643,6 +2759,9 @@ function runFailedLog(sessionId, runId, error, startedAt) {
|
|
|
2643
2759
|
}
|
|
2644
2760
|
function logsFromStreamEvent(props) {
|
|
2645
2761
|
const { event, sessionId, runId } = props;
|
|
2762
|
+
if (event.type === "memory_compaction") {
|
|
2763
|
+
return [memoryCompactedLog({ sessionId, runId, ...event })];
|
|
2764
|
+
}
|
|
2646
2765
|
if (event.type === "turn_start") {
|
|
2647
2766
|
return [
|
|
2648
2767
|
{
|
|
@@ -2668,12 +2787,12 @@ function logsFromStreamEvent(props) {
|
|
|
2668
2787
|
level: "info",
|
|
2669
2788
|
category: "tool",
|
|
2670
2789
|
event: "tool.called",
|
|
2671
|
-
message: `Tool ${event.toolCall.
|
|
2790
|
+
message: `Tool ${event.toolCall.toolName} called`,
|
|
2672
2791
|
metadata: {
|
|
2673
2792
|
turn: event.turn,
|
|
2674
|
-
toolName: event.toolCall.
|
|
2675
|
-
callId: event.toolCall.callId ?? event.toolCall.
|
|
2676
|
-
argumentBytes: byteLength2(formatUnknown(event.toolCall.
|
|
2793
|
+
toolName: event.toolCall.toolName,
|
|
2794
|
+
callId: event.toolCall.callId ?? event.toolCall.toolCallId,
|
|
2795
|
+
argumentBytes: byteLength2(formatUnknown(event.toolCall.input))
|
|
2677
2796
|
}
|
|
2678
2797
|
}
|
|
2679
2798
|
];
|
|
@@ -2721,103 +2840,80 @@ function logsFromStreamEvent(props) {
|
|
|
2721
2840
|
}
|
|
2722
2841
|
];
|
|
2723
2842
|
}
|
|
2724
|
-
if (event.type === "
|
|
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
|
-
{
|
|
2843
|
+
if (event.type === "response" || event.type === "interaction" || event.type === "blocked") {
|
|
2844
|
+
const result = event;
|
|
2845
|
+
const logs = [];
|
|
2846
|
+
if (result.type === "interaction") {
|
|
2847
|
+
logs.push({
|
|
2751
2848
|
sessionId,
|
|
2752
2849
|
runId,
|
|
2753
2850
|
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
|
-
{
|
|
2851
|
+
category: "run",
|
|
2852
|
+
event: "run.suspended",
|
|
2853
|
+
message: `Run suspended for ${result.interaction.type}`,
|
|
2854
|
+
metadata: {
|
|
2855
|
+
interactionId: result.interaction.id,
|
|
2856
|
+
interactionType: result.interaction.type,
|
|
2857
|
+
toolName: result.interaction.toolName,
|
|
2858
|
+
sourceRunId: result.continuation.sourceRunId
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
} else {
|
|
2862
|
+
logs.push(
|
|
2863
|
+
runCompletedLog({
|
|
2864
|
+
sessionId,
|
|
2865
|
+
runId,
|
|
2866
|
+
durationMs: Date.now() - props.startedAt,
|
|
2867
|
+
usage: result.usage,
|
|
2868
|
+
output: result.text,
|
|
2869
|
+
messageCount: result.messages.length
|
|
2870
|
+
})
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
if (result.resumedFrom !== void 0) {
|
|
2874
|
+
logs.push({
|
|
2792
2875
|
sessionId,
|
|
2793
2876
|
runId,
|
|
2794
2877
|
level: "info",
|
|
2795
|
-
category: "
|
|
2796
|
-
event: "
|
|
2797
|
-
message:
|
|
2798
|
-
metadata
|
|
2799
|
-
|
|
2800
|
-
|
|
2878
|
+
category: "run",
|
|
2879
|
+
event: "interaction.resumed",
|
|
2880
|
+
message: "Interaction resumed in a linked run",
|
|
2881
|
+
metadata: {
|
|
2882
|
+
interactionId: result.resumedFrom.interactionId,
|
|
2883
|
+
sourceRunId: result.resumedFrom.runId,
|
|
2884
|
+
resumedRunId: result.runId
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
logs.push(memorySavedLog({ sessionId, runId, messageCount: result.messages.length }));
|
|
2889
|
+
return logs;
|
|
2801
2890
|
}
|
|
2802
|
-
if (event.type === "
|
|
2803
|
-
|
|
2891
|
+
if (event.type === "error") {
|
|
2892
|
+
return [runFailedLog(sessionId, runId, event.error, props.startedAt)];
|
|
2893
|
+
}
|
|
2894
|
+
if (event.type === "interaction_response") {
|
|
2895
|
+
const response = event.response;
|
|
2804
2896
|
const metadata = {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
argumentBytes: byteLength2(event.question.args),
|
|
2810
|
-
answerCount: event.question.answers?.length ?? 0
|
|
2897
|
+
interactionId: response.interactionId,
|
|
2898
|
+
interactionType: response.type === "tool-approval-response" ? "tool-approval" : "tool-question",
|
|
2899
|
+
toolName: response.toolName,
|
|
2900
|
+
sourceRunId: event.sourceRunId
|
|
2811
2901
|
};
|
|
2812
|
-
if (
|
|
2902
|
+
if (response.callId !== void 0) metadata.callId = response.callId;
|
|
2903
|
+
if (response.type === "tool-approval-response") {
|
|
2904
|
+
metadata.approved = response.approved;
|
|
2905
|
+
metadata.hasReason = response.reason !== void 0;
|
|
2906
|
+
} else {
|
|
2907
|
+
metadata.answerCount = response.answers.length;
|
|
2908
|
+
}
|
|
2813
2909
|
return [
|
|
2814
2910
|
{
|
|
2815
2911
|
sessionId,
|
|
2816
2912
|
runId,
|
|
2817
2913
|
level: "info",
|
|
2818
|
-
category: "
|
|
2819
|
-
event:
|
|
2820
|
-
message:
|
|
2914
|
+
category: "run",
|
|
2915
|
+
event: "interaction.responded",
|
|
2916
|
+
message: `Interaction response received for ${response.toolName}`,
|
|
2821
2917
|
metadata
|
|
2822
2918
|
}
|
|
2823
2919
|
];
|
|
@@ -2843,16 +2939,16 @@ function childAgentLog(event, sessionId, runId) {
|
|
|
2843
2939
|
level: "debug",
|
|
2844
2940
|
category: "tool",
|
|
2845
2941
|
event: "child_tool.called",
|
|
2846
|
-
message: `Child agent ${event.agentName ?? event.agentId} called ${child.toolCall.
|
|
2942
|
+
message: `Child agent ${event.agentName ?? event.agentId} called ${child.toolCall.toolName}`,
|
|
2847
2943
|
metadata: {
|
|
2848
2944
|
parentToolName: event.toolName,
|
|
2849
2945
|
agentId: event.agentId,
|
|
2850
2946
|
hasAgentName: event.agentName !== void 0,
|
|
2851
2947
|
turn: event.turn,
|
|
2852
2948
|
childTurn: child.turn,
|
|
2853
|
-
toolName: child.toolCall.
|
|
2854
|
-
callId: child.toolCall.callId ?? child.toolCall.
|
|
2855
|
-
argumentBytes: byteLength2(formatUnknown(child.toolCall.
|
|
2949
|
+
toolName: child.toolCall.toolName,
|
|
2950
|
+
callId: child.toolCall.callId ?? child.toolCall.toolCallId,
|
|
2951
|
+
argumentBytes: byteLength2(formatUnknown(child.toolCall.input))
|
|
2856
2952
|
}
|
|
2857
2953
|
}
|
|
2858
2954
|
];
|
|
@@ -2902,15 +2998,17 @@ function childAgentLog(event, sessionId, runId) {
|
|
|
2902
2998
|
}
|
|
2903
2999
|
];
|
|
2904
3000
|
}
|
|
2905
|
-
if (child.type === "
|
|
3001
|
+
if (child.type === "response" || child.type === "interaction" || child.type === "blocked") {
|
|
3002
|
+
const result = child;
|
|
2906
3003
|
const metadata = {
|
|
2907
3004
|
parentToolName: event.toolName,
|
|
2908
3005
|
agentId: event.agentId,
|
|
2909
3006
|
hasAgentName: event.agentName !== void 0,
|
|
2910
|
-
outputBytes: byteLength2(
|
|
2911
|
-
messageCount:
|
|
3007
|
+
outputBytes: byteLength2(result.text),
|
|
3008
|
+
messageCount: result.messages.length,
|
|
3009
|
+
status: result.type
|
|
2912
3010
|
};
|
|
2913
|
-
const usage = usageSummary(
|
|
3011
|
+
const usage = usageSummary(result.usage);
|
|
2914
3012
|
if (usage !== void 0) metadata.usage = usage;
|
|
2915
3013
|
return [
|
|
2916
3014
|
{
|
|
@@ -3010,6 +3108,56 @@ async function prepareAgentRun(c, props) {
|
|
|
3010
3108
|
if ("error" in body) {
|
|
3011
3109
|
return body.error;
|
|
3012
3110
|
}
|
|
3111
|
+
if (body.type === "interaction_response") {
|
|
3112
|
+
const registration = props.continuationRegistry.take(
|
|
3113
|
+
body.interactionId,
|
|
3114
|
+
agentId,
|
|
3115
|
+
body.response
|
|
3116
|
+
);
|
|
3117
|
+
if (registration.status === "missing") {
|
|
3118
|
+
return errorResponse(c, 404, "not_found", "Interaction continuation is unavailable");
|
|
3119
|
+
}
|
|
3120
|
+
if (registration.status === "claimed") {
|
|
3121
|
+
return errorResponse(c, 409, "conflict", "Interaction response was already claimed");
|
|
3122
|
+
}
|
|
3123
|
+
if (registration.status === "invalid") {
|
|
3124
|
+
return errorResponse(c, 400, "bad_request", registration.error.message);
|
|
3125
|
+
}
|
|
3126
|
+
const { registration: claimed } = registration;
|
|
3127
|
+
const source = claimed.context;
|
|
3128
|
+
const runId2 = globalThis.crypto.randomUUID();
|
|
3129
|
+
let resumedBody = {
|
|
3130
|
+
...source.body
|
|
3131
|
+
};
|
|
3132
|
+
if (body.stream !== void 0) resumedBody = { ...resumedBody, stream: body.stream };
|
|
3133
|
+
if (body.metadata !== void 0) resumedBody = { ...resumedBody, metadata: body.metadata };
|
|
3134
|
+
if (body.trace !== void 0) resumedBody = { ...resumedBody, trace: body.trace };
|
|
3135
|
+
const runOptions = createRunOptions(resumedBody, agentId, source.session, c.req.raw.signal);
|
|
3136
|
+
const execution2 = {
|
|
3137
|
+
generate: (options) => source.runAgent.generate({
|
|
3138
|
+
continuation: claimed.continuation,
|
|
3139
|
+
response: body.response,
|
|
3140
|
+
...options
|
|
3141
|
+
}),
|
|
3142
|
+
stream: (options) => source.runAgent.stream({
|
|
3143
|
+
continuation: claimed.continuation,
|
|
3144
|
+
response: body.response,
|
|
3145
|
+
...options
|
|
3146
|
+
})
|
|
3147
|
+
};
|
|
3148
|
+
return {
|
|
3149
|
+
...source,
|
|
3150
|
+
body: resumedBody,
|
|
3151
|
+
execution: execution2,
|
|
3152
|
+
failureMessages: void 0,
|
|
3153
|
+
memoryCompactionLogged: false,
|
|
3154
|
+
options: runOptions,
|
|
3155
|
+
runId: runId2,
|
|
3156
|
+
runStartedAt: Date.now(),
|
|
3157
|
+
generatedMessagesStartIndex: 0,
|
|
3158
|
+
shouldPersistSessionMessages: source.shouldPersistSessionMessages
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3013
3161
|
const session = await resolveRunSession(c, body, agentId, props.stores);
|
|
3014
3162
|
if (session instanceof Response) {
|
|
3015
3163
|
return session;
|
|
@@ -3036,12 +3184,12 @@ async function prepareAgentRun(c, props) {
|
|
|
3036
3184
|
store: props.stores.sessions
|
|
3037
3185
|
});
|
|
3038
3186
|
const memoryMetadata = runMemoryMetadata(agentId, body, selectedModel, runId);
|
|
3039
|
-
const promptMessage =
|
|
3187
|
+
const promptMessage = body.messages.at(-1);
|
|
3040
3188
|
const sessionStore = props.stores.sessions;
|
|
3041
3189
|
const shouldPersistSessionMessages = session !== void 0 && sessionStore !== void 0 && !usesStoreAsAgentMemory(runAgent, sessionStore);
|
|
3042
3190
|
if (shouldPersistSessionMessages) {
|
|
3043
3191
|
await sessionStore.append({
|
|
3044
|
-
|
|
3192
|
+
scope: { sessionId: session.id, metadata: memoryMetadata },
|
|
3045
3193
|
runId,
|
|
3046
3194
|
turn: 1,
|
|
3047
3195
|
messages: [promptMessage]
|
|
@@ -3061,10 +3209,13 @@ async function prepareAgentRun(c, props) {
|
|
|
3061
3209
|
body,
|
|
3062
3210
|
memoryMetadata,
|
|
3063
3211
|
execution,
|
|
3064
|
-
|
|
3212
|
+
failureMessages: void 0,
|
|
3213
|
+
memoryCompactionLogged: false,
|
|
3214
|
+
options: createRunOptions(body, agentId, session, c.req.raw.signal),
|
|
3065
3215
|
runAgent,
|
|
3066
3216
|
runId,
|
|
3067
3217
|
runStartedAt,
|
|
3218
|
+
generatedMessagesStartIndex: 1,
|
|
3068
3219
|
selectedModel,
|
|
3069
3220
|
session,
|
|
3070
3221
|
sessionStore,
|
|
@@ -3106,7 +3257,7 @@ async function recordRunReceived(props) {
|
|
|
3106
3257
|
sessionId: props.session.id,
|
|
3107
3258
|
runId: props.runId,
|
|
3108
3259
|
agentId: props.agentId,
|
|
3109
|
-
message: props.body.
|
|
3260
|
+
message: props.body.messages.at(-1),
|
|
3110
3261
|
stream: props.body.stream === true,
|
|
3111
3262
|
hasTrace: props.body.trace !== void 0
|
|
3112
3263
|
};
|
|
@@ -3154,23 +3305,33 @@ function runMemoryMetadata(agentId, body, selectedModel, runId) {
|
|
|
3154
3305
|
return metadata;
|
|
3155
3306
|
}
|
|
3156
3307
|
function createRunExecution(props) {
|
|
3157
|
-
|
|
3158
|
-
|
|
3308
|
+
const session = props.session;
|
|
3309
|
+
if (session !== void 0 && props.runAgent.memory !== void 0) {
|
|
3159
3310
|
return {
|
|
3160
|
-
generate: (options) =>
|
|
3161
|
-
|
|
3311
|
+
generate: (options) => props.runAgent.generate({
|
|
3312
|
+
prompt: props.promptMessage,
|
|
3313
|
+
session: { sessionId: session.id, metadata: props.memoryMetadata },
|
|
3314
|
+
...options
|
|
3315
|
+
}),
|
|
3316
|
+
stream: (options) => props.runAgent.stream({
|
|
3317
|
+
prompt: props.promptMessage,
|
|
3318
|
+
session: { sessionId: session.id, metadata: props.memoryMetadata },
|
|
3319
|
+
...options
|
|
3320
|
+
})
|
|
3162
3321
|
};
|
|
3163
3322
|
}
|
|
3164
|
-
const
|
|
3323
|
+
const transcript = session !== void 0 ? [...session.messages, props.promptMessage] : props.body.messages;
|
|
3165
3324
|
return {
|
|
3166
|
-
generate: (options) => props.runAgent.generate(
|
|
3167
|
-
stream: (options) => props.runAgent.stream(
|
|
3325
|
+
generate: (options) => props.runAgent.generate({ messages: transcript, ...options }),
|
|
3326
|
+
stream: (options) => props.runAgent.stream({ messages: transcript, ...options })
|
|
3168
3327
|
};
|
|
3169
3328
|
}
|
|
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)
|
|
3329
|
+
function createRunOptions(body, agentId, session, abortSignal) {
|
|
3330
|
+
const options = { abortSignal };
|
|
3331
|
+
if (body.type === "messages" && body.maxTurns !== void 0) options.maxTurns = body.maxTurns;
|
|
3332
|
+
if (body.type === "messages" && body.toolConcurrency !== void 0) {
|
|
3333
|
+
options.toolConcurrency = body.toolConcurrency;
|
|
3334
|
+
}
|
|
3174
3335
|
if (body.trace !== void 0) {
|
|
3175
3336
|
options.trace = traceForRun(body.trace, agentId, session);
|
|
3176
3337
|
} else if (session !== void 0) {
|
|
@@ -3179,34 +3340,11 @@ function createRunOptions(body, agentId, session) {
|
|
|
3179
3340
|
return options;
|
|
3180
3341
|
}
|
|
3181
3342
|
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
|
|
3343
|
+
const streamOptions = withInternalAgentRunOptions({ ...run.options }, { runId: run.runId });
|
|
3344
|
+
const runStream = registerStreamingContinuation(
|
|
3345
|
+
run.execution.stream(streamOptions),
|
|
3346
|
+
props.continuationRegistry,
|
|
3347
|
+
run
|
|
3210
3348
|
);
|
|
3211
3349
|
let stream = runStream;
|
|
3212
3350
|
let persistedRun;
|
|
@@ -3221,17 +3359,17 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3221
3359
|
}),
|
|
3222
3360
|
store: run.sessionStore,
|
|
3223
3361
|
session: run.session,
|
|
3224
|
-
message: run.body.
|
|
3362
|
+
message: run.body.messages.at(-1),
|
|
3225
3363
|
runId: run.runId,
|
|
3226
3364
|
startedAt: run.runStartedAt,
|
|
3227
|
-
persistGeneratedMessages: run.shouldPersistSessionMessages
|
|
3365
|
+
persistGeneratedMessages: run.shouldPersistSessionMessages,
|
|
3366
|
+
generatedMessagesStartIndex: run.generatedMessagesStartIndex
|
|
3228
3367
|
});
|
|
3229
3368
|
stream = persistedRun.events;
|
|
3230
3369
|
}
|
|
3231
3370
|
return streamAgentRunEvents(c, stream, {
|
|
3371
|
+
runId: run.runId,
|
|
3232
3372
|
onCancel: async () => {
|
|
3233
|
-
props.approvalRuntime.cancelRun(run.runId);
|
|
3234
|
-
props.questionRuntime.cancelRun(run.runId);
|
|
3235
3373
|
const persistence = [];
|
|
3236
3374
|
if (persistedRun !== void 0) {
|
|
3237
3375
|
persistence.push(persistedRun.cancel());
|
|
@@ -3250,59 +3388,57 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3250
3388
|
}
|
|
3251
3389
|
async function handleBufferedAgentRun(c, run, props) {
|
|
3252
3390
|
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);
|
|
3391
|
+
const runtimeOptions = await startBufferedSessionRun(run);
|
|
3392
|
+
const result = await run.execution.generate(runtimeOptions);
|
|
3393
|
+
if (result.type === "interaction") {
|
|
3394
|
+
props.continuationRegistry.register({ continuation: result.continuation, context: run });
|
|
3395
|
+
}
|
|
3396
|
+
await completeBufferedSessionRun(run, result);
|
|
3397
|
+
return c.json(agentRunResponse(result));
|
|
3263
3398
|
} catch (error) {
|
|
3264
3399
|
await failBufferedSessionRun(run, error);
|
|
3265
3400
|
return errorResponse(c, 500, "internal_error", "Agent run failed", serializeError(error));
|
|
3266
3401
|
}
|
|
3267
3402
|
}
|
|
3268
|
-
|
|
3403
|
+
function agentRunResponse(result) {
|
|
3404
|
+
if (result.type !== "interaction") return result;
|
|
3405
|
+
const { continuation: _continuation, messages: _messages, ...response } = result;
|
|
3406
|
+
return response;
|
|
3407
|
+
}
|
|
3408
|
+
async function startBufferedSessionRun(run) {
|
|
3269
3409
|
if (run.session !== void 0) {
|
|
3270
3410
|
await appendSessionLog(run.sessionStore, runStartedLog(run.session, run.runId));
|
|
3271
3411
|
await appendSessionLog(run.sessionStore, memoryLoadedLog(run.session, run.runId));
|
|
3272
3412
|
}
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3413
|
+
const internalOptions = {
|
|
3414
|
+
onFailure: ({ messages }) => {
|
|
3415
|
+
run.failureMessages = structuredClone(messages);
|
|
3416
|
+
},
|
|
3417
|
+
runId: run.runId
|
|
3276
3418
|
};
|
|
3277
|
-
if (run.session !== void 0
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3419
|
+
if (run.session !== void 0 && run.sessionStore !== void 0) {
|
|
3420
|
+
const sessionId = run.session.id;
|
|
3421
|
+
const sessionStore = run.sessionStore;
|
|
3422
|
+
internalOptions.onMemoryCompaction = async (compaction) => {
|
|
3423
|
+
await appendSessionLog(
|
|
3424
|
+
sessionStore,
|
|
3425
|
+
memoryCompactedLog({
|
|
3426
|
+
sessionId,
|
|
3427
|
+
runId: run.runId,
|
|
3428
|
+
...compaction
|
|
3429
|
+
})
|
|
3430
|
+
);
|
|
3431
|
+
run.memoryCompactionLogged = true;
|
|
3432
|
+
};
|
|
3433
|
+
}
|
|
3434
|
+
return withInternalAgentRunOptions({ ...run.options }, internalOptions);
|
|
3281
3435
|
}
|
|
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;
|
|
3436
|
+
async function* registerStreamingContinuation(stream, registry, run) {
|
|
3437
|
+
for await (const event of stream) {
|
|
3438
|
+
if (event.type === "interaction") {
|
|
3439
|
+
registry.register({ continuation: event.continuation, context: run });
|
|
3302
3440
|
}
|
|
3303
|
-
|
|
3304
|
-
const decision = await approvalRuntime.request(context, pending.approval);
|
|
3305
|
-
stream = agent.resume(pending, decision);
|
|
3441
|
+
yield event;
|
|
3306
3442
|
}
|
|
3307
3443
|
}
|
|
3308
3444
|
async function completeBufferedSessionRun(run, response) {
|
|
@@ -3310,10 +3446,10 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3310
3446
|
return;
|
|
3311
3447
|
}
|
|
3312
3448
|
if (run.shouldPersistSessionMessages) {
|
|
3313
|
-
const generatedMessages = response.messages.slice(
|
|
3449
|
+
const generatedMessages = response.messages.slice(run.generatedMessagesStartIndex);
|
|
3314
3450
|
if (generatedMessages.length > 0) {
|
|
3315
3451
|
await run.sessionStore.append({
|
|
3316
|
-
|
|
3452
|
+
scope: { sessionId: run.session.id, metadata: run.memoryMetadata },
|
|
3317
3453
|
runId: run.runId,
|
|
3318
3454
|
turn: 1,
|
|
3319
3455
|
messages: generatedMessages
|
|
@@ -3326,21 +3462,85 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3326
3462
|
await run.sessionStore.saveSessionRunTranscript({
|
|
3327
3463
|
id: run.session.id,
|
|
3328
3464
|
runId: run.runId,
|
|
3329
|
-
...optionalTitle(run.body.
|
|
3465
|
+
...optionalTitle(run.body.messages.at(-1)),
|
|
3330
3466
|
transcript,
|
|
3331
|
-
status: "success"
|
|
3467
|
+
status: response.type === "interaction" ? "suspended" : "success"
|
|
3332
3468
|
});
|
|
3469
|
+
if (response.memoryCompaction !== void 0 && !run.memoryCompactionLogged) {
|
|
3470
|
+
await appendSessionLog(
|
|
3471
|
+
run.sessionStore,
|
|
3472
|
+
memoryCompactedLog({
|
|
3473
|
+
sessionId: run.session.id,
|
|
3474
|
+
runId: run.runId,
|
|
3475
|
+
...response.memoryCompaction
|
|
3476
|
+
})
|
|
3477
|
+
);
|
|
3478
|
+
run.memoryCompactionLogged = true;
|
|
3479
|
+
}
|
|
3333
3480
|
await appendSessionLog(
|
|
3334
3481
|
run.sessionStore,
|
|
3335
|
-
|
|
3482
|
+
response.type === "interaction" ? {
|
|
3483
|
+
sessionId: run.session.id,
|
|
3484
|
+
runId: run.runId,
|
|
3485
|
+
level: "info",
|
|
3486
|
+
category: "run",
|
|
3487
|
+
event: "run.suspended",
|
|
3488
|
+
message: `Run suspended for ${response.interaction.type}`,
|
|
3489
|
+
metadata: {
|
|
3490
|
+
interactionId: response.interaction.id,
|
|
3491
|
+
interactionType: response.interaction.type,
|
|
3492
|
+
toolName: response.interaction.toolName,
|
|
3493
|
+
sourceRunId: response.continuation.sourceRunId
|
|
3494
|
+
}
|
|
3495
|
+
} : runCompletedLog({
|
|
3336
3496
|
sessionId: run.session.id,
|
|
3337
3497
|
runId: run.runId,
|
|
3338
3498
|
durationMs: durationMs2,
|
|
3339
3499
|
usage: response.usage,
|
|
3340
|
-
output: response.
|
|
3500
|
+
output: response.text,
|
|
3341
3501
|
messageCount: response.messages.length
|
|
3342
3502
|
})
|
|
3343
3503
|
);
|
|
3504
|
+
if (response.resumedFrom !== void 0) {
|
|
3505
|
+
const responseMessage = response.messages[0];
|
|
3506
|
+
const responsePart = responseMessage?.role === "tool" ? responseMessage.content[0] : void 0;
|
|
3507
|
+
if (responsePart !== void 0 && responsePart.type !== "tool-result") {
|
|
3508
|
+
const metadata = {
|
|
3509
|
+
interactionId: responsePart.interactionId,
|
|
3510
|
+
interactionType: responsePart.type === "tool-approval-response" ? "tool-approval" : "tool-question",
|
|
3511
|
+
toolName: responsePart.toolName,
|
|
3512
|
+
sourceRunId: response.resumedFrom.runId
|
|
3513
|
+
};
|
|
3514
|
+
if (responsePart.type === "tool-approval-response") {
|
|
3515
|
+
metadata.approved = responsePart.approved;
|
|
3516
|
+
metadata.hasReason = responsePart.reason !== void 0;
|
|
3517
|
+
} else {
|
|
3518
|
+
metadata.answerCount = responsePart.answers.length;
|
|
3519
|
+
}
|
|
3520
|
+
await appendSessionLog(run.sessionStore, {
|
|
3521
|
+
sessionId: run.session.id,
|
|
3522
|
+
runId: run.runId,
|
|
3523
|
+
level: "info",
|
|
3524
|
+
category: "run",
|
|
3525
|
+
event: "interaction.responded",
|
|
3526
|
+
message: `Interaction response received for ${responsePart.toolName}`,
|
|
3527
|
+
metadata
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
await appendSessionLog(run.sessionStore, {
|
|
3531
|
+
sessionId: run.session.id,
|
|
3532
|
+
runId: run.runId,
|
|
3533
|
+
level: "info",
|
|
3534
|
+
category: "run",
|
|
3535
|
+
event: "interaction.resumed",
|
|
3536
|
+
message: "Interaction resumed in a linked run",
|
|
3537
|
+
metadata: {
|
|
3538
|
+
interactionId: response.resumedFrom.interactionId,
|
|
3539
|
+
sourceRunId: response.resumedFrom.runId,
|
|
3540
|
+
resumedRunId: response.runId
|
|
3541
|
+
}
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3344
3544
|
await appendSessionLog(
|
|
3345
3545
|
run.sessionStore,
|
|
3346
3546
|
memorySavedLog({
|
|
@@ -3354,17 +3554,14 @@ async function failBufferedSessionRun(run, error) {
|
|
|
3354
3554
|
if (run.session === void 0 || run.sessionStore === void 0) {
|
|
3355
3555
|
return;
|
|
3356
3556
|
}
|
|
3357
|
-
const messages = await run.sessionStore.load({
|
|
3358
|
-
sessionId: run.session.id,
|
|
3359
|
-
metadata: run.memoryMetadata
|
|
3360
|
-
});
|
|
3361
3557
|
const durationMs2 = Date.now() - run.runStartedAt;
|
|
3362
|
-
const
|
|
3558
|
+
const messages = run.failureMessages ?? [run.body.messages.at(-1)];
|
|
3559
|
+
const transcript = transcriptFromMessages([...messages]);
|
|
3363
3560
|
assignTranscriptRunDuration(transcript, durationMs2);
|
|
3364
3561
|
await run.sessionStore.saveSessionRunTranscript({
|
|
3365
3562
|
id: run.session.id,
|
|
3366
3563
|
runId: run.runId,
|
|
3367
|
-
...optionalTitle(run.body.
|
|
3564
|
+
...optionalTitle(run.body.messages.at(-1)),
|
|
3368
3565
|
transcript,
|
|
3369
3566
|
status: "error",
|
|
3370
3567
|
error: serializeError(error)
|
|
@@ -3374,254 +3571,13 @@ async function failBufferedSessionRun(run, error) {
|
|
|
3374
3571
|
runFailedLog(run.session.id, run.runId, error, run.runStartedAt)
|
|
3375
3572
|
);
|
|
3376
3573
|
}
|
|
3377
|
-
function normalizePromptMessage(message) {
|
|
3378
|
-
return typeof message === "string" ? Message.user(message) : message;
|
|
3379
|
-
}
|
|
3380
3574
|
function usesStoreAsAgentMemory(agent, store) {
|
|
3381
3575
|
const memoryStore = agent.memory?.store;
|
|
3382
3576
|
return memoryStore !== void 0 && rawObservedStore(memoryStore) === rawObservedStore(store);
|
|
3383
3577
|
}
|
|
3384
3578
|
|
|
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
|
-
}
|
|
3579
|
+
// src/runtime/config.ts
|
|
3580
|
+
import { isQuestionTool } from "@anvia/core/tool";
|
|
3625
3581
|
|
|
3626
3582
|
// src/runtime/eval-config.ts
|
|
3627
3583
|
var CASE_PREVIEW_LIMIT = 5;
|
|
@@ -3703,6 +3659,41 @@ function isRecord2(value) {
|
|
|
3703
3659
|
|
|
3704
3660
|
// src/runtime/knowledge.ts
|
|
3705
3661
|
import { getAgentToolState } from "@anvia/core/internal/agent";
|
|
3662
|
+
|
|
3663
|
+
// src/runtime/query.ts
|
|
3664
|
+
function optionalQueryString(value) {
|
|
3665
|
+
const trimmed = value?.trim();
|
|
3666
|
+
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
3667
|
+
}
|
|
3668
|
+
function parseLimit(value, defaultMax = 50, max = 100) {
|
|
3669
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3670
|
+
return defaultMax;
|
|
3671
|
+
}
|
|
3672
|
+
const limit = Number(value);
|
|
3673
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
3674
|
+
return void 0;
|
|
3675
|
+
}
|
|
3676
|
+
return Math.min(limit, max);
|
|
3677
|
+
}
|
|
3678
|
+
function parseTraceStatus(value) {
|
|
3679
|
+
const status = optionalQueryString(value);
|
|
3680
|
+
if (status === void 0) {
|
|
3681
|
+
return void 0;
|
|
3682
|
+
}
|
|
3683
|
+
return status === "running" || status === "success" || status === "suspended" || status === "error" ? status : false;
|
|
3684
|
+
}
|
|
3685
|
+
function parseAfter(value) {
|
|
3686
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3687
|
+
return void 0;
|
|
3688
|
+
}
|
|
3689
|
+
const after = Number(value);
|
|
3690
|
+
if (!Number.isInteger(after) || after < 0) {
|
|
3691
|
+
return false;
|
|
3692
|
+
}
|
|
3693
|
+
return after;
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
// src/runtime/knowledge.ts
|
|
3706
3697
|
function registerKnowledgeRoutes(app, props) {
|
|
3707
3698
|
app.get("/knowledge", async (c) => {
|
|
3708
3699
|
const limit = parseLimit(c.req.query("limit"));
|
|
@@ -3763,7 +3754,7 @@ async function agentKnowledgeConfig(agent) {
|
|
|
3763
3754
|
async function knowledgeSources(agent) {
|
|
3764
3755
|
const toolIndexes = getAgentToolState(agent.agent).toolIndexes;
|
|
3765
3756
|
const staticContext = staticContextDocuments(agent.agent);
|
|
3766
|
-
const indexedContext =
|
|
3757
|
+
const indexedContext = vectorContexts(agent.agent);
|
|
3767
3758
|
const sources = [
|
|
3768
3759
|
{
|
|
3769
3760
|
sourceId: staticSourceId(),
|
|
@@ -3775,20 +3766,20 @@ async function knowledgeSources(agent) {
|
|
|
3775
3766
|
}
|
|
3776
3767
|
];
|
|
3777
3768
|
const dynamicContextSources = await Promise.all(
|
|
3778
|
-
indexedContext.map(async (
|
|
3779
|
-
const inspect = inspectFn(
|
|
3780
|
-
const count = await inspectableCount(inspect,
|
|
3769
|
+
indexedContext.map(async (vectorContext, index) => {
|
|
3770
|
+
const inspect = inspectFn(vectorContext.store);
|
|
3771
|
+
const count = await inspectableCount(inspect, vectorContext.filter);
|
|
3781
3772
|
const source = {
|
|
3782
3773
|
sourceId: dynamicContextSourceId(index),
|
|
3783
3774
|
kind: "dynamic_context",
|
|
3784
3775
|
label: `Dynamic context ${index + 1}`,
|
|
3785
3776
|
count: 1,
|
|
3786
3777
|
registrationIndex: index,
|
|
3787
|
-
topK:
|
|
3778
|
+
topK: vectorContext.topK,
|
|
3788
3779
|
inspectable: inspect !== void 0
|
|
3789
3780
|
};
|
|
3790
|
-
if (
|
|
3791
|
-
source.
|
|
3781
|
+
if (vectorContext.minScore !== void 0) {
|
|
3782
|
+
source.minScore = vectorContext.minScore;
|
|
3792
3783
|
}
|
|
3793
3784
|
if (count !== void 0) source.itemCount = count;
|
|
3794
3785
|
return source;
|
|
@@ -3807,8 +3798,8 @@ async function knowledgeSources(agent) {
|
|
|
3807
3798
|
topK: toolIndex.topK,
|
|
3808
3799
|
inspectable: inspect !== void 0
|
|
3809
3800
|
};
|
|
3810
|
-
if (toolIndex.
|
|
3811
|
-
source.
|
|
3801
|
+
if (toolIndex.minScore !== void 0) {
|
|
3802
|
+
source.minScore = toolIndex.minScore;
|
|
3812
3803
|
}
|
|
3813
3804
|
if (count !== void 0) source.itemCount = count;
|
|
3814
3805
|
return source;
|
|
@@ -3827,20 +3818,20 @@ async function knowledgeItemsPage(agent, sourceId, request) {
|
|
|
3827
3818
|
if (sourceId === staticSourceId()) {
|
|
3828
3819
|
return staticKnowledgeItemsPage(agent, request);
|
|
3829
3820
|
}
|
|
3830
|
-
const
|
|
3831
|
-
if (
|
|
3832
|
-
const
|
|
3833
|
-
if (
|
|
3821
|
+
const vectorContextIndex = dynamicSourceIndex(sourceId, "dynamic_context");
|
|
3822
|
+
if (vectorContextIndex !== void 0) {
|
|
3823
|
+
const vectorContext = vectorContexts(agent.agent)[vectorContextIndex];
|
|
3824
|
+
if (vectorContext === void 0) {
|
|
3834
3825
|
return void 0;
|
|
3835
3826
|
}
|
|
3836
|
-
const inspect = inspectFn(
|
|
3827
|
+
const inspect = inspectFn(vectorContext.store);
|
|
3837
3828
|
if (inspect === void 0) {
|
|
3838
3829
|
return nonInspectablePage(agent.id, sourceId, "dynamic_context");
|
|
3839
3830
|
}
|
|
3840
3831
|
const page = await inspect({
|
|
3841
3832
|
limit: request.limit,
|
|
3842
3833
|
cursor: request.cursor,
|
|
3843
|
-
filter:
|
|
3834
|
+
filter: vectorContext.filter
|
|
3844
3835
|
});
|
|
3845
3836
|
const result = {
|
|
3846
3837
|
agentId: agent.id,
|
|
@@ -3881,12 +3872,12 @@ async function knowledgeItemsPage(agent, sourceId, request) {
|
|
|
3881
3872
|
}
|
|
3882
3873
|
return void 0;
|
|
3883
3874
|
}
|
|
3884
|
-
function inspectFn(
|
|
3885
|
-
if (!isRecord3(
|
|
3875
|
+
function inspectFn(store) {
|
|
3876
|
+
if (!isRecord3(store) || typeof store.inspect !== "function") {
|
|
3886
3877
|
return void 0;
|
|
3887
3878
|
}
|
|
3888
|
-
const inspect =
|
|
3889
|
-
return (request) => inspect.call(
|
|
3879
|
+
const inspect = store.inspect;
|
|
3880
|
+
return (request) => inspect.call(store, request);
|
|
3890
3881
|
}
|
|
3891
3882
|
function staticKnowledgeItemsPage(agent, request) {
|
|
3892
3883
|
const staticContext = staticContextDocuments(agent.agent);
|
|
@@ -4102,7 +4093,7 @@ function isRecord3(value) {
|
|
|
4102
4093
|
|
|
4103
4094
|
// src/runtime/tool-metadata.ts
|
|
4104
4095
|
import { getAgentToolState as getAgentToolState2 } from "@anvia/core/internal/agent";
|
|
4105
|
-
|
|
4096
|
+
import { isMcpTool } from "@anvia/core/mcp";
|
|
4106
4097
|
function agentToolItems(agent) {
|
|
4107
4098
|
const state = getAgentToolState2(agent.agent);
|
|
4108
4099
|
const items = state.staticTools.map((tool) => ({ tool, source: "static" }));
|
|
@@ -4135,15 +4126,10 @@ function toolRequiresApproval(tool) {
|
|
|
4135
4126
|
return approval !== void 0 && approval !== false;
|
|
4136
4127
|
}
|
|
4137
4128
|
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;
|
|
4129
|
+
return isMcpTool(tool) ? tool.mcp.serverName : void 0;
|
|
4144
4130
|
}
|
|
4145
|
-
function
|
|
4146
|
-
return
|
|
4131
|
+
function agentHasMcpServers(agent) {
|
|
4132
|
+
return agent.agent.mcpServers.length > 0;
|
|
4147
4133
|
}
|
|
4148
4134
|
|
|
4149
4135
|
// src/runtime/config.ts
|
|
@@ -4165,7 +4151,7 @@ function agentConfig(agent) {
|
|
|
4165
4151
|
function agentRuntimeSummary(agent) {
|
|
4166
4152
|
const tools = agentToolItems(agent);
|
|
4167
4153
|
const staticContext = staticContextDocuments(agent.agent);
|
|
4168
|
-
const indexedContext =
|
|
4154
|
+
const indexedContext = vectorContexts(agent.agent);
|
|
4169
4155
|
const name = agent.name ?? agent.agent.name;
|
|
4170
4156
|
const description = agent.description ?? agent.agent.description;
|
|
4171
4157
|
const summary = {
|
|
@@ -4178,7 +4164,7 @@ function agentRuntimeSummary(agent) {
|
|
|
4178
4164
|
mcpToolCount: tools.filter((item) => mcpServerName(item.tool) !== void 0).length,
|
|
4179
4165
|
staticContextCount: staticContext.length,
|
|
4180
4166
|
dynamicContextCount: indexedContext.length,
|
|
4181
|
-
observerCount: agent.agent.observers.length,
|
|
4167
|
+
observerCount: Object.keys(agent.agent.observability?.observers ?? {}).length,
|
|
4182
4168
|
hasMemory: agent.agent.memory !== void 0,
|
|
4183
4169
|
hasLifecycle: agent.agent.lifecycle !== void 0,
|
|
4184
4170
|
hasOutputSchema: agent.agent.outputSchema !== void 0
|
|
@@ -4250,14 +4236,16 @@ function capabilityConfig(_options, agents, pipelines, stores, sandboxCount = 0)
|
|
|
4250
4236
|
if (agents.some((agent) => agent.agent.tools.length > 0)) {
|
|
4251
4237
|
capabilities.tools = { enabled: true };
|
|
4252
4238
|
}
|
|
4253
|
-
if (agents.some(
|
|
4239
|
+
if (agents.some(agentHasMcpServers)) {
|
|
4254
4240
|
capabilities.mcps = { enabled: true };
|
|
4255
4241
|
}
|
|
4256
4242
|
if (sandboxCount > 0) {
|
|
4257
4243
|
capabilities.sandboxes = { enabled: true };
|
|
4258
4244
|
}
|
|
4259
|
-
if (agents.some(
|
|
4260
|
-
|
|
4245
|
+
if (agents.some(
|
|
4246
|
+
(agent) => agent.agent.tools.some((tool) => toolRequiresApproval(tool) || isQuestionTool(tool))
|
|
4247
|
+
)) {
|
|
4248
|
+
capabilities.interactions = { enabled: true };
|
|
4261
4249
|
}
|
|
4262
4250
|
if (agents.some(agentHasKnowledge)) {
|
|
4263
4251
|
capabilities.knowledge = { enabled: true };
|
|
@@ -4339,6 +4327,47 @@ async function parseEvalRunRequest(c) {
|
|
|
4339
4327
|
return request;
|
|
4340
4328
|
}
|
|
4341
4329
|
|
|
4330
|
+
// src/runtime/interactions.ts
|
|
4331
|
+
import {
|
|
4332
|
+
assertAgentInteractionResponse
|
|
4333
|
+
} from "@anvia/core/agent/interactions";
|
|
4334
|
+
function createStudioContinuationRegistry() {
|
|
4335
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4336
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
4337
|
+
return {
|
|
4338
|
+
register(registration) {
|
|
4339
|
+
const id = registration.continuation.interaction.id;
|
|
4340
|
+
if (pending.has(id) || claimed.has(id)) {
|
|
4341
|
+
throw new TypeError(`Duplicate Studio interaction ID "${id}".`);
|
|
4342
|
+
}
|
|
4343
|
+
pending.set(id, registration);
|
|
4344
|
+
},
|
|
4345
|
+
take(interactionId, agentId, response) {
|
|
4346
|
+
if (claimed.has(interactionId)) return { status: "claimed" };
|
|
4347
|
+
const registration = pending.get(interactionId);
|
|
4348
|
+
if (registration === void 0 || registration.continuation.agentId !== agentId) {
|
|
4349
|
+
return { status: "missing" };
|
|
4350
|
+
}
|
|
4351
|
+
const request = registration.continuation.interaction;
|
|
4352
|
+
try {
|
|
4353
|
+
assertAgentInteractionResponse(request, response);
|
|
4354
|
+
} catch (error) {
|
|
4355
|
+
return {
|
|
4356
|
+
status: "invalid",
|
|
4357
|
+
error: error instanceof TypeError ? error : new TypeError(String(error))
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4360
|
+
pending.delete(interactionId);
|
|
4361
|
+
claimed.add(interactionId);
|
|
4362
|
+
return { status: "ready", registration };
|
|
4363
|
+
},
|
|
4364
|
+
clear() {
|
|
4365
|
+
pending.clear();
|
|
4366
|
+
claimed.clear();
|
|
4367
|
+
}
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4342
4371
|
// src/runtime/mcps.ts
|
|
4343
4372
|
function registerMcpRoutes(app, props) {
|
|
4344
4373
|
app.get("/agents/:agentId/mcps", async (c) => {
|
|
@@ -4354,43 +4383,29 @@ function registerMcpRoutes(app, props) {
|
|
|
4354
4383
|
});
|
|
4355
4384
|
}
|
|
4356
4385
|
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));
|
|
4386
|
+
return Promise.all(
|
|
4387
|
+
agent.agent.mcpServers.map(async (server) => {
|
|
4388
|
+
const tools = await Promise.all(
|
|
4389
|
+
server.tools.map(async (tool) => {
|
|
4390
|
+
const definition = await tool.definition("");
|
|
4391
|
+
return {
|
|
4392
|
+
name: definition.name,
|
|
4393
|
+
description: definition.description,
|
|
4394
|
+
parameters: definition.parameters,
|
|
4395
|
+
source: "static",
|
|
4396
|
+
approval: approvalMetadata(tool)
|
|
4397
|
+
};
|
|
4398
|
+
})
|
|
4399
|
+
);
|
|
4400
|
+
const sortedTools = tools.sort((left, right) => left.name.localeCompare(right.name));
|
|
4401
|
+
return {
|
|
4402
|
+
agentId: agent.id,
|
|
4403
|
+
name: server.name,
|
|
4404
|
+
toolCount: sortedTools.length,
|
|
4405
|
+
tools: sortedTools
|
|
4406
|
+
};
|
|
4407
|
+
})
|
|
4408
|
+
).then((servers) => servers.sort((left, right) => left.name.localeCompare(right.name)));
|
|
4394
4409
|
}
|
|
4395
4410
|
|
|
4396
4411
|
// src/runtime/memory.ts
|
|
@@ -4612,7 +4627,7 @@ function agentMemorySource(ref, label, agentId, storeKindValue, inspector) {
|
|
|
4612
4627
|
).slice(0, options.limit);
|
|
4613
4628
|
},
|
|
4614
4629
|
async getConversation(conversationRef) {
|
|
4615
|
-
const conversation = await inspector.getConversation(conversationRef);
|
|
4630
|
+
const conversation = await inspector.getConversation({ ref: conversationRef });
|
|
4616
4631
|
return conversation === void 0 ? void 0 : inspectorConversationDetail(conversation, summary.agentIds);
|
|
4617
4632
|
}
|
|
4618
4633
|
};
|
|
@@ -4835,12 +4850,12 @@ function pipelineRunFailedLog(pipelineId, runId, error, startedAt) {
|
|
|
4835
4850
|
}
|
|
4836
4851
|
};
|
|
4837
4852
|
}
|
|
4838
|
-
function pipelineStageLog(
|
|
4853
|
+
function pipelineStageLog(event, studioPipelineId = event.pipelineId) {
|
|
4839
4854
|
const category = stageCategory(event.node);
|
|
4840
4855
|
if (event.type === "stage_started") {
|
|
4841
4856
|
return {
|
|
4842
|
-
pipelineId,
|
|
4843
|
-
runId,
|
|
4857
|
+
pipelineId: studioPipelineId,
|
|
4858
|
+
runId: event.runId,
|
|
4844
4859
|
level: "debug",
|
|
4845
4860
|
category,
|
|
4846
4861
|
event: `${event.node.kind}.started`,
|
|
@@ -4850,8 +4865,8 @@ function pipelineStageLog(pipelineId, runId, event) {
|
|
|
4850
4865
|
}
|
|
4851
4866
|
if (event.type === "stage_completed") {
|
|
4852
4867
|
return {
|
|
4853
|
-
pipelineId,
|
|
4854
|
-
runId,
|
|
4868
|
+
pipelineId: studioPipelineId,
|
|
4869
|
+
runId: event.runId,
|
|
4855
4870
|
level: "debug",
|
|
4856
4871
|
category,
|
|
4857
4872
|
event: `${event.node.kind}.completed`,
|
|
@@ -4863,8 +4878,8 @@ function pipelineStageLog(pipelineId, runId, event) {
|
|
|
4863
4878
|
};
|
|
4864
4879
|
}
|
|
4865
4880
|
return {
|
|
4866
|
-
pipelineId,
|
|
4867
|
-
runId,
|
|
4881
|
+
pipelineId: studioPipelineId,
|
|
4882
|
+
runId: event.runId,
|
|
4868
4883
|
level: "error",
|
|
4869
4884
|
category,
|
|
4870
4885
|
event: `${event.node.kind}.failed`,
|
|
@@ -4891,6 +4906,7 @@ function stageCategory(node) {
|
|
|
4891
4906
|
function nodeMetadata(node) {
|
|
4892
4907
|
const metadata = {
|
|
4893
4908
|
nodeId: node.id,
|
|
4909
|
+
nodePath: [...node.path],
|
|
4894
4910
|
kind: node.kind,
|
|
4895
4911
|
label: node.label
|
|
4896
4912
|
};
|
|
@@ -5052,17 +5068,24 @@ async function executePipelineRun(c, props, pipeline, body) {
|
|
|
5052
5068
|
if (body.metadata !== void 0) streamOptions.metadata = body.metadata;
|
|
5053
5069
|
if (props.logStore !== void 0) streamOptions.logStore = props.logStore;
|
|
5054
5070
|
if (props.runStore !== void 0) streamOptions.runStore = props.runStore;
|
|
5071
|
+
streamOptions.abortSignal = c.req.raw.signal;
|
|
5055
5072
|
return streamPipelineRun(c, streamOptions);
|
|
5056
5073
|
}
|
|
5057
5074
|
try {
|
|
5058
5075
|
await appendPipelineLog(props.logStore, pipelineRunStartedLog(pipeline, runId));
|
|
5059
|
-
const
|
|
5076
|
+
const result = await pipeline.pipeline.run({
|
|
5077
|
+
input: body.input,
|
|
5078
|
+
runId,
|
|
5079
|
+
metadata: body.metadata,
|
|
5080
|
+
abortSignal: c.req.raw.signal,
|
|
5060
5081
|
observer: {
|
|
5061
5082
|
async onEvent(event) {
|
|
5062
|
-
await appendPipelineLog(props.logStore, pipelineStageLog(pipeline.id
|
|
5083
|
+
await appendPipelineLog(props.logStore, pipelineStageLog(event, pipeline.id));
|
|
5063
5084
|
}
|
|
5064
|
-
}
|
|
5085
|
+
},
|
|
5086
|
+
failOnObserverError: true
|
|
5065
5087
|
});
|
|
5088
|
+
const output = result.output;
|
|
5066
5089
|
const jsonOutput = toJsonValue(output);
|
|
5067
5090
|
const endedAt = Date.now();
|
|
5068
5091
|
const successRecord = {
|
|
@@ -5127,19 +5150,24 @@ function streamPipelineRun(_c, props) {
|
|
|
5127
5150
|
async function* pipelineRunEvents(props) {
|
|
5128
5151
|
yield* emitPipelineLog(props.logStore, pipelineRunStartedLog(props.pipeline, props.runId));
|
|
5129
5152
|
const events = new AsyncEventQueue();
|
|
5130
|
-
const run = props.pipeline.pipeline.run(
|
|
5153
|
+
const run = props.pipeline.pipeline.run({
|
|
5154
|
+
input: props.input,
|
|
5155
|
+
runId: props.runId,
|
|
5156
|
+
metadata: props.metadata,
|
|
5157
|
+
abortSignal: props.abortSignal,
|
|
5131
5158
|
observer: {
|
|
5132
5159
|
async onEvent(event) {
|
|
5133
5160
|
const log = await appendPipelineLog(
|
|
5134
5161
|
props.logStore,
|
|
5135
|
-
pipelineStageLog(props.pipeline.id
|
|
5162
|
+
pipelineStageLog(event, props.pipeline.id)
|
|
5136
5163
|
);
|
|
5137
5164
|
if (log !== void 0) {
|
|
5138
5165
|
events.push({ type: "pipeline_log", log });
|
|
5139
5166
|
}
|
|
5140
5167
|
}
|
|
5141
|
-
}
|
|
5142
|
-
|
|
5168
|
+
},
|
|
5169
|
+
failOnObserverError: true
|
|
5170
|
+
}).then(async ({ output }) => {
|
|
5143
5171
|
const jsonOutput = toJsonValue(output);
|
|
5144
5172
|
const endedAt = Date.now();
|
|
5145
5173
|
const successRecord = {
|
|
@@ -5292,272 +5320,284 @@ function parsePipelineLogAfter(value) {
|
|
|
5292
5320
|
return after;
|
|
5293
5321
|
}
|
|
5294
5322
|
|
|
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) });
|
|
5323
|
+
// src/runtime/sandbox-views.ts
|
|
5324
|
+
import { upgradeWebSocket } from "@hono/node-server";
|
|
5325
|
+
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
5326
|
+
import { WebSocket } from "ws";
|
|
5327
|
+
var minLeaseTimeoutMs = 5e3;
|
|
5328
|
+
var maxLeaseTimeoutMs = 12e4;
|
|
5329
|
+
var maxPendingWebSocketBytes = 1024 * 1024;
|
|
5330
|
+
function registerSandboxViewRoutes(app, registry) {
|
|
5331
|
+
const leases = /* @__PURE__ */ new Map();
|
|
5332
|
+
const upstreamSockets = /* @__PURE__ */ new Set();
|
|
5333
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/control", async (c) => {
|
|
5334
|
+
const resolved = await authorizedView(c, registry);
|
|
5335
|
+
if (resolved instanceof Response) return resolved;
|
|
5336
|
+
pruneExpiredLease(leases, viewKey(resolved));
|
|
5337
|
+
return c.json(resolved.view.source.control.snapshot());
|
|
5321
5338
|
});
|
|
5322
|
-
app.
|
|
5323
|
-
const
|
|
5324
|
-
if (
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
return c.json(result);
|
|
5339
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/connection", async (c) => {
|
|
5340
|
+
const resolved = await authorizedView(c, registry);
|
|
5341
|
+
if (resolved instanceof Response) return resolved;
|
|
5342
|
+
const response = {
|
|
5343
|
+
sandboxRef: resolved.entry.summary.ref,
|
|
5344
|
+
viewId: resolved.view.id,
|
|
5345
|
+
protocol: "novnc",
|
|
5346
|
+
websocketPath: `/sandboxes/${encodeURIComponent(resolved.entry.summary.ref)}/views/${encodeURIComponent(resolved.view.id)}/ws`,
|
|
5347
|
+
authentication: resolved.view.authentication.type === "password" ? { type: "password", password: resolved.view.authentication.password } : { type: "none" }
|
|
5348
|
+
};
|
|
5349
|
+
c.header("cache-control", "no-store");
|
|
5350
|
+
return c.json(response);
|
|
5335
5351
|
});
|
|
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") };
|
|
5352
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/acquire", async (c) => {
|
|
5353
|
+
const resolved = await authorizedView(c, registry);
|
|
5354
|
+
if (resolved instanceof Response) return resolved;
|
|
5355
|
+
const body = await readControlBody(c);
|
|
5356
|
+
if (body instanceof Response) return body;
|
|
5357
|
+
const key = viewKey(resolved);
|
|
5358
|
+
pruneExpiredLease(leases, key);
|
|
5359
|
+
if (leases.has(key)) {
|
|
5360
|
+
return errorResponse(c, 409, "conflict", "Browser view is already controlled");
|
|
5361
5361
|
}
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5362
|
+
try {
|
|
5363
|
+
const lease = await resolved.view.source.control.acquireHumanControl({
|
|
5364
|
+
ownerId: body.ownerId,
|
|
5365
|
+
leaseTimeoutMs: body.leaseTimeoutMs,
|
|
5366
|
+
abortSignal: c.req.raw.signal
|
|
5367
|
+
});
|
|
5368
|
+
leases.set(key, lease);
|
|
5369
|
+
return c.json(leaseResponse(lease), 201);
|
|
5370
|
+
} catch (error) {
|
|
5371
|
+
return controlErrorResponse(c, error);
|
|
5367
5372
|
}
|
|
5368
|
-
|
|
5369
|
-
|
|
5373
|
+
});
|
|
5374
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/renew", async (c) => {
|
|
5375
|
+
const resolved = await authorizedView(c, registry);
|
|
5376
|
+
if (resolved instanceof Response) return resolved;
|
|
5377
|
+
const body = await readLeaseBody(c);
|
|
5378
|
+
if (body instanceof Response) return body;
|
|
5379
|
+
const key = viewKey(resolved);
|
|
5380
|
+
pruneExpiredLease(leases, key);
|
|
5381
|
+
const lease = leases.get(key);
|
|
5382
|
+
if (lease === void 0 || lease.id !== body.leaseId) {
|
|
5383
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is not active");
|
|
5370
5384
|
}
|
|
5371
|
-
|
|
5372
|
-
|
|
5385
|
+
try {
|
|
5386
|
+
lease.renew({ leaseTimeoutMs: body.leaseTimeoutMs });
|
|
5387
|
+
return c.json(leaseResponse(lease));
|
|
5388
|
+
} catch (error) {
|
|
5389
|
+
leases.delete(key);
|
|
5390
|
+
return controlErrorResponse(c, error);
|
|
5373
5391
|
}
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
if (
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5392
|
+
});
|
|
5393
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/release", async (c) => {
|
|
5394
|
+
const resolved = await authorizedView(c, registry);
|
|
5395
|
+
if (resolved instanceof Response) return resolved;
|
|
5396
|
+
const body = await readReleaseBody(c);
|
|
5397
|
+
if (body instanceof Response) return body;
|
|
5398
|
+
const key = viewKey(resolved);
|
|
5399
|
+
const lease = leases.get(key);
|
|
5400
|
+
if (lease === void 0 || lease.id !== body.leaseId) {
|
|
5401
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is not active");
|
|
5402
|
+
}
|
|
5403
|
+
lease.release();
|
|
5404
|
+
leases.delete(key);
|
|
5405
|
+
return c.json({ released: true });
|
|
5406
|
+
});
|
|
5407
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/ws", async (c) => {
|
|
5408
|
+
const resolved = await authorizedView(c, registry);
|
|
5409
|
+
if (resolved instanceof Response) return resolved;
|
|
5410
|
+
const upstreamUrl = `ws://${hostForUrl(resolved.host)}:${resolved.port}/websockify`;
|
|
5411
|
+
return upgradeWebSocket(c, createProxyEvents(upstreamUrl, upstreamSockets), {
|
|
5412
|
+
onError: () => void 0
|
|
5413
|
+
});
|
|
5414
|
+
});
|
|
5415
|
+
return () => {
|
|
5416
|
+
for (const lease of leases.values()) lease.release();
|
|
5417
|
+
leases.clear();
|
|
5418
|
+
for (const socket of upstreamSockets) socket.close(1001, "Studio closed");
|
|
5419
|
+
upstreamSockets.clear();
|
|
5420
|
+
};
|
|
5383
5421
|
}
|
|
5384
|
-
function
|
|
5385
|
-
|
|
5422
|
+
function createProxyEvents(upstreamUrl, sockets) {
|
|
5423
|
+
let upstream;
|
|
5424
|
+
let pendingBytes = 0;
|
|
5425
|
+
const pending = [];
|
|
5386
5426
|
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
|
-
}
|
|
5427
|
+
onOpen(_event, downstream) {
|
|
5428
|
+
const protocols = downstream.protocol === null || downstream.protocol.length === 0 ? void 0 : [downstream.protocol];
|
|
5429
|
+
upstream = new WebSocket(upstreamUrl, protocols, {
|
|
5430
|
+
headers: { origin: "http://127.0.0.1" }
|
|
5431
|
+
});
|
|
5432
|
+
sockets.add(upstream);
|
|
5433
|
+
upstream.binaryType = "arraybuffer";
|
|
5434
|
+
upstream.once("open", () => {
|
|
5435
|
+
for (const message of pending) upstream?.send(message);
|
|
5436
|
+
pending.length = 0;
|
|
5437
|
+
pendingBytes = 0;
|
|
5438
|
+
});
|
|
5439
|
+
upstream.on("message", (data, isBinary) => {
|
|
5440
|
+
const value = isBinary ? Uint8Array.from(Buffer.from(data)) : data.toString();
|
|
5441
|
+
downstream.send(value);
|
|
5408
5442
|
});
|
|
5443
|
+
upstream.once("close", (code, reason) => {
|
|
5444
|
+
sockets.delete(upstream);
|
|
5445
|
+
downstream.close(normalizeCloseCode(code), reason.toString("utf8").slice(0, 123));
|
|
5446
|
+
});
|
|
5447
|
+
upstream.once("error", () => downstream.close(1011, "noVNC upstream failed"));
|
|
5409
5448
|
},
|
|
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;
|
|
5449
|
+
onMessage(event, downstream) {
|
|
5450
|
+
void normalizeWebSocketMessage(event.data).then((message) => {
|
|
5451
|
+
if (upstream?.readyState === WebSocket.OPEN) {
|
|
5452
|
+
upstream.send(message);
|
|
5453
|
+
return;
|
|
5423
5454
|
}
|
|
5424
|
-
|
|
5425
|
-
|
|
5455
|
+
pendingBytes += typeof message === "string" ? Buffer.byteLength(message) : message.byteLength;
|
|
5456
|
+
if (pendingBytes > maxPendingWebSocketBytes) {
|
|
5457
|
+
downstream.close(1009, "Pending noVNC messages exceeded the limit");
|
|
5458
|
+
return;
|
|
5426
5459
|
}
|
|
5427
|
-
|
|
5428
|
-
}).
|
|
5460
|
+
pending.push(message);
|
|
5461
|
+
}).catch(() => downstream.close(1003, "Unsupported noVNC message"));
|
|
5429
5462
|
},
|
|
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;
|
|
5463
|
+
onClose() {
|
|
5464
|
+
upstream?.close(1e3, "Viewer disconnected");
|
|
5465
|
+
if (upstream !== void 0) sockets.delete(upstream);
|
|
5443
5466
|
},
|
|
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);
|
|
5467
|
+
onError() {
|
|
5468
|
+
upstream?.close(1011, "Viewer connection failed");
|
|
5457
5469
|
}
|
|
5458
5470
|
};
|
|
5459
5471
|
}
|
|
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
|
-
};
|
|
5472
|
+
async function authorizedView(c, registry) {
|
|
5473
|
+
const entry = registry.get(c.req.param("sandboxRef") ?? "");
|
|
5474
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5475
|
+
const view = entry.views.get(c.req.param("viewId") ?? "");
|
|
5476
|
+
if (view === void 0) return errorResponse(c, 404, "not_found", "Sandbox view not found");
|
|
5477
|
+
if (!await authorize(c, entry, view)) {
|
|
5478
|
+
return errorResponse(c, 403, "forbidden", "Sandbox view access denied");
|
|
5479
|
+
}
|
|
5480
|
+
const published = entry.inspector.publishedPorts?.find(
|
|
5481
|
+
(port) => port.containerPort === view.source.containerPort && port.protocol === "tcp" && isLoopbackAddress(port.host)
|
|
5482
|
+
);
|
|
5483
|
+
if (published === void 0) {
|
|
5484
|
+
return errorResponse(c, 503, "internal_error", "Sandbox view port is unavailable");
|
|
5485
|
+
}
|
|
5486
|
+
return { entry, view, host: published.host, port: published.hostPort };
|
|
5487
|
+
}
|
|
5488
|
+
async function authorize(c, entry, view) {
|
|
5489
|
+
if (view.access.mode === "authorize") {
|
|
5490
|
+
try {
|
|
5491
|
+
return await view.access.authorize({
|
|
5492
|
+
request: c.req.raw,
|
|
5493
|
+
sandboxRef: entry.summary.ref,
|
|
5494
|
+
viewId: view.id
|
|
5495
|
+
}) === true;
|
|
5496
|
+
} catch {
|
|
5497
|
+
return false;
|
|
5502
5498
|
}
|
|
5503
|
-
questions.push(normalized);
|
|
5504
5499
|
}
|
|
5505
|
-
|
|
5500
|
+
let remoteAddress;
|
|
5501
|
+
try {
|
|
5502
|
+
const address = getConnInfo(c).remote.address;
|
|
5503
|
+
if (address === void 0) return false;
|
|
5504
|
+
remoteAddress = address;
|
|
5505
|
+
} catch {
|
|
5506
|
+
return false;
|
|
5507
|
+
}
|
|
5508
|
+
if (!isLoopbackAddress(remoteAddress)) return false;
|
|
5509
|
+
const expectedOrigin = new URL(c.req.url).origin;
|
|
5510
|
+
const origin = c.req.header("origin");
|
|
5511
|
+
return origin === expectedOrigin || origin === void 0 && c.req.header("sec-fetch-site") === "same-origin";
|
|
5506
5512
|
}
|
|
5507
|
-
function
|
|
5508
|
-
|
|
5509
|
-
|
|
5513
|
+
async function readControlBody(c) {
|
|
5514
|
+
const body = await readJsonObject(c);
|
|
5515
|
+
if (body instanceof Response) return body;
|
|
5516
|
+
if (typeof body.ownerId !== "string" || body.ownerId.length === 0 || body.ownerId.length > 200) {
|
|
5517
|
+
return errorResponse(c, 400, "bad_request", "ownerId must be a non-empty string");
|
|
5510
5518
|
}
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
return void 0;
|
|
5519
|
+
if (!isLeaseTimeout(body.leaseTimeoutMs)) {
|
|
5520
|
+
return errorResponse(c, 400, "bad_request", "leaseTimeoutMs is outside the allowed range");
|
|
5514
5521
|
}
|
|
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
|
-
};
|
|
5522
|
+
return { ownerId: body.ownerId, leaseTimeoutMs: body.leaseTimeoutMs };
|
|
5520
5523
|
}
|
|
5521
|
-
function
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
+
async function readLeaseBody(c) {
|
|
5525
|
+
const body = await readJsonObject(c);
|
|
5526
|
+
if (body instanceof Response) return body;
|
|
5527
|
+
if (typeof body.leaseId !== "string" || body.leaseId.length === 0) {
|
|
5528
|
+
return errorResponse(c, 400, "bad_request", "leaseId must be a non-empty string");
|
|
5524
5529
|
}
|
|
5525
|
-
if (!
|
|
5526
|
-
return
|
|
5530
|
+
if (!isLeaseTimeout(body.leaseTimeoutMs)) {
|
|
5531
|
+
return errorResponse(c, 400, "bad_request", "leaseTimeoutMs is outside the allowed range");
|
|
5527
5532
|
}
|
|
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
|
-
};
|
|
5533
|
+
return { leaseId: body.leaseId, leaseTimeoutMs: body.leaseTimeoutMs };
|
|
5532
5534
|
}
|
|
5533
|
-
function
|
|
5534
|
-
|
|
5535
|
+
async function readReleaseBody(c) {
|
|
5536
|
+
const body = await readJsonObject(c);
|
|
5537
|
+
if (body instanceof Response) return body;
|
|
5538
|
+
if (typeof body.leaseId !== "string" || body.leaseId.length === 0) {
|
|
5539
|
+
return errorResponse(c, 400, "bad_request", "leaseId must be a non-empty string");
|
|
5540
|
+
}
|
|
5541
|
+
return { leaseId: body.leaseId };
|
|
5535
5542
|
}
|
|
5536
|
-
function
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
}
|
|
5543
|
+
async function readJsonObject(c) {
|
|
5544
|
+
let body;
|
|
5545
|
+
try {
|
|
5546
|
+
body = await c.req.json();
|
|
5547
|
+
} catch {
|
|
5548
|
+
return errorResponse(c, 400, "bad_request", "Request body must be valid JSON");
|
|
5549
|
+
}
|
|
5550
|
+
return isRecord4(body) ? body : errorResponse(c, 400, "bad_request", "Request body must be an object");
|
|
5543
5551
|
}
|
|
5544
|
-
function
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5552
|
+
function controlErrorResponse(c, error) {
|
|
5553
|
+
if (isRecord4(error) && error.code === "human_controlled") {
|
|
5554
|
+
return errorResponse(c, 409, "conflict", "Browser view is already controlled");
|
|
5555
|
+
}
|
|
5556
|
+
if (isRecord4(error) && error.code === "invalid_state") {
|
|
5557
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is no longer active");
|
|
5558
|
+
}
|
|
5559
|
+
return errorResponse(c, 500, "internal_error", "Browser control operation failed");
|
|
5560
|
+
}
|
|
5561
|
+
function leaseResponse(lease) {
|
|
5562
|
+
return { id: lease.id, ownerId: lease.ownerId, expiresAt: lease.expiresAt };
|
|
5563
|
+
}
|
|
5564
|
+
function pruneExpiredLease(leases, key) {
|
|
5565
|
+
const lease = leases.get(key);
|
|
5566
|
+
if (lease !== void 0 && Date.parse(lease.expiresAt) <= Date.now()) {
|
|
5567
|
+
lease.release();
|
|
5568
|
+
leases.delete(key);
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
function viewKey(resolved) {
|
|
5572
|
+
return `${resolved.entry.summary.ref}\0${resolved.view.id}`;
|
|
5573
|
+
}
|
|
5574
|
+
function isLeaseTimeout(value) {
|
|
5575
|
+
return Number.isSafeInteger(value) && Number(value) >= minLeaseTimeoutMs && Number(value) <= maxLeaseTimeoutMs;
|
|
5576
|
+
}
|
|
5577
|
+
function isLoopbackAddress(value) {
|
|
5578
|
+
return value === "localhost" || value === "127.0.0.1" || value === "::1" || value.startsWith("127.") || value.startsWith("::ffff:127.");
|
|
5579
|
+
}
|
|
5580
|
+
function hostForUrl(host) {
|
|
5581
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
5582
|
+
}
|
|
5583
|
+
async function normalizeWebSocketMessage(value) {
|
|
5584
|
+
if (typeof value === "string") return value;
|
|
5585
|
+
if (value instanceof ArrayBuffer) return Uint8Array.from(new Uint8Array(value));
|
|
5586
|
+
if (ArrayBuffer.isView(value)) {
|
|
5587
|
+
return Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
5588
|
+
}
|
|
5589
|
+
if (value instanceof Blob) return Uint8Array.from(new Uint8Array(await value.arrayBuffer()));
|
|
5590
|
+
throw new TypeError("Unsupported noVNC WebSocket message type.");
|
|
5591
|
+
}
|
|
5592
|
+
function normalizeCloseCode(code) {
|
|
5593
|
+
return code >= 1e3 && code <= 4999 && code !== 1004 && code !== 1005 && code !== 1006 ? code : 1001;
|
|
5550
5594
|
}
|
|
5551
|
-
function
|
|
5552
|
-
|
|
5553
|
-
void emit;
|
|
5554
|
-
void resolve2;
|
|
5555
|
-
return rest;
|
|
5595
|
+
function isRecord4(value) {
|
|
5596
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5556
5597
|
}
|
|
5557
5598
|
|
|
5558
5599
|
// src/runtime/sandboxes.ts
|
|
5559
5600
|
import path from "path";
|
|
5560
|
-
var sandboxToolMetadataKey = /* @__PURE__ */ Symbol.for("anvia.sandbox.tool.metadata");
|
|
5561
5601
|
var maxFileResponseBytes = 10 * 1024 * 1024;
|
|
5562
5602
|
var defaultProcessLogBytes = 64 * 1024;
|
|
5563
5603
|
var maxProcessLogBytes = 1024 * 1024;
|
|
@@ -5576,51 +5616,41 @@ var StudioSandboxRegistry = class {
|
|
|
5576
5616
|
return this.entries.get(ref);
|
|
5577
5617
|
}
|
|
5578
5618
|
};
|
|
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}`;
|
|
5619
|
+
function createStudioSandboxRegistry(agents, registrations) {
|
|
5620
|
+
const agentIds = new Set(agents.map((agent) => agent.id));
|
|
5621
|
+
const refs = /* @__PURE__ */ new Set();
|
|
5622
|
+
const entries = registrations.map((registration, index) => {
|
|
5623
|
+
validateRegistration(registration, index, agentIds);
|
|
5624
|
+
const inspector = registration.inspector;
|
|
5625
|
+
const ref = sandboxRef(inspector.provider, inspector.id);
|
|
5626
|
+
if (refs.has(ref)) {
|
|
5627
|
+
throw new TypeError(
|
|
5628
|
+
`sandboxes contains a duplicate provider/id registration: ${inspector.provider}/${inspector.id}`
|
|
5629
|
+
);
|
|
5630
|
+
}
|
|
5631
|
+
refs.add(ref);
|
|
5632
|
+
const views = new Map((registration.views ?? []).map((view) => [view.id, view]));
|
|
5607
5633
|
return {
|
|
5608
|
-
|
|
5634
|
+
inspector,
|
|
5635
|
+
views,
|
|
5609
5636
|
summary: {
|
|
5610
5637
|
ref,
|
|
5611
|
-
id:
|
|
5612
|
-
provider:
|
|
5613
|
-
workdir:
|
|
5614
|
-
agentIds:
|
|
5615
|
-
toolNames:
|
|
5638
|
+
id: inspector.id,
|
|
5639
|
+
provider: inspector.provider,
|
|
5640
|
+
workdir: inspector.workdir,
|
|
5641
|
+
agentIds: sortedUnique(registration.agentIds ?? []),
|
|
5642
|
+
toolNames: sortedUnique(registration.toolNames ?? []),
|
|
5643
|
+
views: [...views.values()].map((view) => ({ id: view.id, label: view.label, protocol: view.source.protocol })).sort((left, right) => left.id.localeCompare(right.id)),
|
|
5616
5644
|
capabilities: {
|
|
5617
|
-
files:
|
|
5618
|
-
ports: supportsPorts(
|
|
5619
|
-
processes: supportsProcesses(
|
|
5645
|
+
files: supportsFiles(inspector),
|
|
5646
|
+
ports: supportsPorts(inspector),
|
|
5647
|
+
processes: supportsProcesses(inspector),
|
|
5648
|
+
views: views.size > 0
|
|
5620
5649
|
}
|
|
5621
5650
|
}
|
|
5622
5651
|
};
|
|
5623
5652
|
});
|
|
5653
|
+
entries.sort((left, right) => left.summary.ref.localeCompare(right.summary.ref));
|
|
5624
5654
|
return new StudioSandboxRegistry(entries);
|
|
5625
5655
|
}
|
|
5626
5656
|
function registerSandboxRoutes(app, registry) {
|
|
@@ -5629,24 +5659,19 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5629
5659
|
app.get("/sandboxes", (c) => c.json({ sandboxes: registry.list() }));
|
|
5630
5660
|
app.get("/sandboxes/:sandboxRef", (c) => {
|
|
5631
5661
|
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));
|
|
5662
|
+
return entry === void 0 ? errorResponse(c, 404, "not_found", "Sandbox not found") : c.json(copySummary(entry.summary));
|
|
5636
5663
|
});
|
|
5637
5664
|
app.get("/sandboxes/:sandboxRef/files", async (c) => {
|
|
5638
5665
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5639
|
-
if (entry === void 0)
|
|
5640
|
-
|
|
5641
|
-
}
|
|
5666
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5667
|
+
if (!supportsFiles(entry.inspector)) return unsupportedSandboxOperation(c, "files");
|
|
5642
5668
|
try {
|
|
5643
5669
|
const requestedPath = c.req.query("path") ?? ".";
|
|
5644
5670
|
const normalizedPath = normalizeSandboxPath(requestedPath, true);
|
|
5645
|
-
const entries = await listSandboxFiles(entry.session, normalizedPath);
|
|
5646
5671
|
return c.json({
|
|
5647
5672
|
sandboxRef: entry.summary.ref,
|
|
5648
5673
|
path: normalizedPath,
|
|
5649
|
-
entries
|
|
5674
|
+
entries: await listSandboxFiles(entry.inspector, normalizedPath, c.req.raw.signal)
|
|
5650
5675
|
});
|
|
5651
5676
|
} catch (error) {
|
|
5652
5677
|
return sandboxErrorResponse(c, error);
|
|
@@ -5654,9 +5679,8 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5654
5679
|
});
|
|
5655
5680
|
app.get("/sandboxes/:sandboxRef/files/content", async (c) => {
|
|
5656
5681
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5657
|
-
if (entry === void 0)
|
|
5658
|
-
|
|
5659
|
-
}
|
|
5682
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5683
|
+
if (!supportsFiles(entry.inspector)) return unsupportedSandboxOperation(c, "files.content");
|
|
5660
5684
|
try {
|
|
5661
5685
|
const requestedPath = c.req.query("path");
|
|
5662
5686
|
if (requestedPath === void 0) {
|
|
@@ -5667,8 +5691,11 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5667
5691
|
throw new SandboxRouteError(400, "bad_request", "download must be 1 when provided");
|
|
5668
5692
|
}
|
|
5669
5693
|
const normalizedPath = normalizeSandboxPath(requestedPath, false);
|
|
5670
|
-
const
|
|
5671
|
-
|
|
5694
|
+
const siblings = await listSandboxFiles(
|
|
5695
|
+
entry.inspector,
|
|
5696
|
+
path.posix.dirname(normalizedPath),
|
|
5697
|
+
c.req.raw.signal
|
|
5698
|
+
);
|
|
5672
5699
|
const file = siblings.find((candidate) => candidate.path === normalizedPath);
|
|
5673
5700
|
if (file === void 0) {
|
|
5674
5701
|
throw new SandboxRouteError(404, "not_found", "Sandbox file not found");
|
|
@@ -5680,25 +5707,24 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5680
5707
|
"Sandbox content path must refer to a regular file"
|
|
5681
5708
|
);
|
|
5682
5709
|
}
|
|
5683
|
-
if (file.size !== void 0 && file.size > maxFileResponseBytes)
|
|
5710
|
+
if (file.size !== void 0 && file.size > maxFileResponseBytes)
|
|
5684
5711
|
throw fileTooLarge(file.size);
|
|
5685
|
-
|
|
5686
|
-
|
|
5712
|
+
const rawBytes = await entry.inspector.readFile({
|
|
5713
|
+
path: normalizedPath,
|
|
5714
|
+
abortSignal: c.req.raw.signal
|
|
5715
|
+
});
|
|
5687
5716
|
if (!(rawBytes instanceof Uint8Array)) {
|
|
5688
5717
|
throw new TypeError("Sandbox readFile returned an invalid byte payload");
|
|
5689
5718
|
}
|
|
5690
|
-
if (rawBytes.byteLength > maxFileResponseBytes)
|
|
5691
|
-
|
|
5692
|
-
}
|
|
5693
|
-
const bytes = new Uint8Array(rawBytes.byteLength);
|
|
5694
|
-
bytes.set(rawBytes);
|
|
5719
|
+
if (rawBytes.byteLength > maxFileResponseBytes) throw fileTooLarge(rawBytes.byteLength);
|
|
5720
|
+
const bytes = rawBytes.slice();
|
|
5695
5721
|
const disposition = download === "1" ? "attachment" : "inline";
|
|
5696
5722
|
const filename = encodeURIComponent(path.posix.basename(normalizedPath));
|
|
5697
5723
|
return new Response(bytes, {
|
|
5698
5724
|
headers: {
|
|
5699
5725
|
"cache-control": "no-store",
|
|
5700
5726
|
"content-disposition": `${disposition}; filename*=UTF-8''${filename}`,
|
|
5701
|
-
"content-length":
|
|
5727
|
+
"content-length": `${bytes.byteLength}`,
|
|
5702
5728
|
"content-type": "application/octet-stream",
|
|
5703
5729
|
"x-content-type-options": "nosniff"
|
|
5704
5730
|
}
|
|
@@ -5709,16 +5735,12 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5709
5735
|
});
|
|
5710
5736
|
app.get("/sandboxes/:sandboxRef/ports", (c) => {
|
|
5711
5737
|
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
|
-
}
|
|
5738
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5739
|
+
if (!supportsPorts(entry.inspector)) return unsupportedSandboxOperation(c, "ports");
|
|
5718
5740
|
try {
|
|
5719
5741
|
return c.json({
|
|
5720
5742
|
sandboxRef: entry.summary.ref,
|
|
5721
|
-
ports: normalizePorts(entry.
|
|
5743
|
+
ports: normalizePorts(entry.inspector.publishedPorts)
|
|
5722
5744
|
});
|
|
5723
5745
|
} catch (error) {
|
|
5724
5746
|
return sandboxErrorResponse(c, error);
|
|
@@ -5726,16 +5748,16 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5726
5748
|
});
|
|
5727
5749
|
app.get("/sandboxes/:sandboxRef/processes", async (c) => {
|
|
5728
5750
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5729
|
-
if (entry === void 0)
|
|
5730
|
-
|
|
5731
|
-
}
|
|
5732
|
-
if (!supportsProcesses(entry.session)) {
|
|
5751
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5752
|
+
if (!supportsProcesses(entry.inspector)) {
|
|
5733
5753
|
return unsupportedSandboxOperation(c, "processes");
|
|
5734
5754
|
}
|
|
5735
5755
|
try {
|
|
5736
5756
|
return c.json({
|
|
5737
5757
|
sandboxRef: entry.summary.ref,
|
|
5738
|
-
processes: normalizeProcesses(
|
|
5758
|
+
processes: normalizeProcesses(
|
|
5759
|
+
await entry.inspector.listProcesses({ abortSignal: c.req.raw.signal })
|
|
5760
|
+
)
|
|
5739
5761
|
});
|
|
5740
5762
|
} catch (error) {
|
|
5741
5763
|
return sandboxErrorResponse(c, error);
|
|
@@ -5743,21 +5765,25 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5743
5765
|
});
|
|
5744
5766
|
app.get("/sandboxes/:sandboxRef/processes/:processId/logs", async (c) => {
|
|
5745
5767
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5746
|
-
if (entry === void 0)
|
|
5747
|
-
|
|
5748
|
-
}
|
|
5749
|
-
if (!supportsProcesses(entry.session)) {
|
|
5768
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5769
|
+
if (!supportsProcesses(entry.inspector)) {
|
|
5750
5770
|
return unsupportedSandboxOperation(c, "processes.logs");
|
|
5751
5771
|
}
|
|
5752
5772
|
try {
|
|
5753
5773
|
const tailBytes = parseTailBytes(c.req.query("tailBytes"));
|
|
5754
5774
|
const processId = c.req.param("processId");
|
|
5755
|
-
const processes = normalizeProcesses(
|
|
5775
|
+
const processes = normalizeProcesses(
|
|
5776
|
+
await entry.inspector.listProcesses({ abortSignal: c.req.raw.signal })
|
|
5777
|
+
);
|
|
5756
5778
|
if (!processes.some((process2) => process2.id === processId)) {
|
|
5757
5779
|
throw new SandboxRouteError(404, "not_found", "Sandbox process not found");
|
|
5758
5780
|
}
|
|
5759
5781
|
const logs = normalizeProcessLogs(
|
|
5760
|
-
await entry.
|
|
5782
|
+
await entry.inspector.readProcessLogs({
|
|
5783
|
+
processId,
|
|
5784
|
+
tailBytes,
|
|
5785
|
+
abortSignal: c.req.raw.signal
|
|
5786
|
+
}),
|
|
5761
5787
|
tailBytes
|
|
5762
5788
|
);
|
|
5763
5789
|
return c.json({
|
|
@@ -5774,21 +5800,86 @@ async function sandboxNoStore(c, next) {
|
|
|
5774
5800
|
c.header("cache-control", "no-store");
|
|
5775
5801
|
await next();
|
|
5776
5802
|
}
|
|
5777
|
-
function
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5803
|
+
function validateRegistration(registration, index, knownAgentIds) {
|
|
5804
|
+
if (!isRecord5(registration) || !isInspector(registration.inspector)) {
|
|
5805
|
+
throw new TypeError(`sandboxes[${index}] must contain a valid inspector.`);
|
|
5806
|
+
}
|
|
5807
|
+
const inspector = registration.inspector;
|
|
5808
|
+
const files = supportsFiles(inspector);
|
|
5809
|
+
if (inspector.listFiles === void 0 !== (inspector.readFile === void 0)) {
|
|
5810
|
+
throw new TypeError(`sandboxes[${index}] must register both file inspector methods.`);
|
|
5811
|
+
}
|
|
5812
|
+
const processes = supportsProcesses(inspector);
|
|
5813
|
+
if (inspector.listProcesses === void 0 !== (inspector.readProcessLogs === void 0)) {
|
|
5814
|
+
throw new TypeError(`sandboxes[${index}] must register both process inspector methods.`);
|
|
5815
|
+
}
|
|
5816
|
+
if (!files && !supportsPorts(inspector) && !processes) {
|
|
5817
|
+
throw new TypeError(`sandboxes[${index}] inspector exposes no capabilities.`);
|
|
5818
|
+
}
|
|
5819
|
+
for (const agentId of sortedUnique(registration.agentIds ?? [])) {
|
|
5820
|
+
if (!knownAgentIds.has(agentId)) {
|
|
5821
|
+
throw new TypeError(`sandboxes[${index}] references an unknown agent: ${agentId}`);
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
sortedUnique(registration.toolNames ?? []);
|
|
5825
|
+
validateViews(registration.views, inspector, index);
|
|
5826
|
+
}
|
|
5827
|
+
function validateViews(views, inspector, registrationIndex) {
|
|
5828
|
+
if (views === void 0) return;
|
|
5829
|
+
if (!Array.isArray(views))
|
|
5830
|
+
throw new TypeError(`sandboxes[${registrationIndex}].views must be an array.`);
|
|
5831
|
+
if (!supportsPorts(inspector)) {
|
|
5832
|
+
throw new TypeError(`sandboxes[${registrationIndex}] views require published port inspection.`);
|
|
5833
|
+
}
|
|
5834
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5835
|
+
for (const [viewIndex, view] of views.entries()) {
|
|
5836
|
+
const prefix = `sandboxes[${registrationIndex}].views[${viewIndex}]`;
|
|
5837
|
+
const candidate = view;
|
|
5838
|
+
if (!isRecord5(candidate) || typeof candidate.id !== "string" || !/^[a-z0-9](?:[a-z0-9_-]{0,62})$/.test(candidate.id)) {
|
|
5839
|
+
throw new TypeError(`${prefix}.id must be a stable lowercase identifier.`);
|
|
5840
|
+
}
|
|
5841
|
+
if (ids.has(candidate.id)) throw new TypeError(`${prefix}.id is duplicated: ${candidate.id}`);
|
|
5842
|
+
ids.add(candidate.id);
|
|
5843
|
+
if (typeof candidate.label !== "string" || candidate.label.length === 0) {
|
|
5844
|
+
throw new TypeError(`${prefix}.label must be a non-empty string.`);
|
|
5845
|
+
}
|
|
5846
|
+
const source = candidate.source;
|
|
5847
|
+
if (!isRecord5(source) || source.protocol !== "novnc" || !isPort(source.containerPort) || !isRecord5(source.control) || typeof source.control.snapshot !== "function" || typeof source.control.acquireHumanControl !== "function") {
|
|
5848
|
+
throw new TypeError(`${prefix}.source must be a valid noVNC view source.`);
|
|
5849
|
+
}
|
|
5850
|
+
const port = inspector.publishedPorts?.find(
|
|
5851
|
+
(candidate2) => candidate2.containerPort === source.containerPort && candidate2.protocol === "tcp"
|
|
5852
|
+
);
|
|
5853
|
+
if (port === void 0 || !isLoopbackHost(port.host)) {
|
|
5854
|
+
throw new TypeError(`${prefix}.source must resolve to a loopback-published TCP port.`);
|
|
5855
|
+
}
|
|
5856
|
+
const access = candidate.access;
|
|
5857
|
+
if (!isRecord5(access) || access.mode !== "local" && access.mode !== "authorize") {
|
|
5858
|
+
throw new TypeError(`${prefix}.access must explicitly select local or authorize mode.`);
|
|
5859
|
+
}
|
|
5860
|
+
if (access.mode === "authorize" && typeof access.authorize !== "function") {
|
|
5861
|
+
throw new TypeError(`${prefix}.access.authorize must be a function.`);
|
|
5862
|
+
}
|
|
5863
|
+
const authentication = candidate.authentication;
|
|
5864
|
+
if (!isRecord5(authentication) || authentication.type !== "none" && authentication.type !== "password") {
|
|
5865
|
+
throw new TypeError(`${prefix}.authentication must explicitly select none or password.`);
|
|
5866
|
+
}
|
|
5867
|
+
if (authentication.type === "password" && (typeof authentication.password !== "string" || authentication.password.length === 0)) {
|
|
5868
|
+
throw new TypeError(`${prefix}.authentication.password must be a non-empty string.`);
|
|
5869
|
+
}
|
|
5781
5870
|
}
|
|
5782
|
-
return isSandboxSession(metadata.session) ? metadata.session : void 0;
|
|
5783
5871
|
}
|
|
5784
|
-
function
|
|
5785
|
-
return
|
|
5872
|
+
function isInspector(value) {
|
|
5873
|
+
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
5874
|
}
|
|
5787
|
-
function
|
|
5788
|
-
return
|
|
5875
|
+
function supportsFiles(inspector) {
|
|
5876
|
+
return typeof inspector.listFiles === "function" && typeof inspector.readFile === "function";
|
|
5789
5877
|
}
|
|
5790
|
-
function
|
|
5791
|
-
return
|
|
5878
|
+
function supportsPorts(inspector) {
|
|
5879
|
+
return Array.isArray(inspector.publishedPorts);
|
|
5880
|
+
}
|
|
5881
|
+
function supportsProcesses(inspector) {
|
|
5882
|
+
return typeof inspector.listProcesses === "function" && typeof inspector.readProcessLogs === "function";
|
|
5792
5883
|
}
|
|
5793
5884
|
function sandboxRef(provider, id) {
|
|
5794
5885
|
return Buffer.from(JSON.stringify([provider, id]), "utf8").toString("base64url");
|
|
@@ -5798,22 +5889,26 @@ function copySummary(summary) {
|
|
|
5798
5889
|
...summary,
|
|
5799
5890
|
agentIds: [...summary.agentIds],
|
|
5800
5891
|
toolNames: [...summary.toolNames],
|
|
5892
|
+
views: summary.views.map((view) => ({ ...view })),
|
|
5801
5893
|
capabilities: { ...summary.capabilities }
|
|
5802
5894
|
};
|
|
5803
5895
|
}
|
|
5804
|
-
|
|
5805
|
-
return
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5896
|
+
function isLoopbackHost(host) {
|
|
5897
|
+
return host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
5898
|
+
}
|
|
5899
|
+
async function listSandboxFiles(inspector, filePath, abortSignal) {
|
|
5900
|
+
return normalizeFileEntries(await inspector.listFiles({ path: filePath, abortSignal })).sort(
|
|
5901
|
+
(left, right) => {
|
|
5902
|
+
if (left.type === "directory" && right.type !== "directory") return -1;
|
|
5903
|
+
if (left.type !== "directory" && right.type === "directory") return 1;
|
|
5904
|
+
return left.path.localeCompare(right.path);
|
|
5905
|
+
}
|
|
5906
|
+
);
|
|
5810
5907
|
}
|
|
5811
5908
|
function normalizeFileEntries(value) {
|
|
5812
|
-
if (!Array.isArray(value))
|
|
5813
|
-
throw new TypeError("Sandbox listFiles returned an invalid payload");
|
|
5814
|
-
}
|
|
5909
|
+
if (!Array.isArray(value)) throw new TypeError("Sandbox listFiles returned an invalid payload");
|
|
5815
5910
|
return value.map((item) => {
|
|
5816
|
-
if (!
|
|
5911
|
+
if (!isRecord5(item) || typeof item.path !== "string" || !isFileType(item.type)) {
|
|
5817
5912
|
throw new TypeError("Sandbox listFiles returned an invalid entry");
|
|
5818
5913
|
}
|
|
5819
5914
|
let entryPath;
|
|
@@ -5824,7 +5919,7 @@ function normalizeFileEntries(value) {
|
|
|
5824
5919
|
}
|
|
5825
5920
|
const entry = { path: entryPath, type: item.type };
|
|
5826
5921
|
if (item.size !== void 0) {
|
|
5827
|
-
if (
|
|
5922
|
+
if (!Number.isSafeInteger(item.size) || Number(item.size) < 0) {
|
|
5828
5923
|
throw new TypeError("Sandbox listFiles returned an invalid file size");
|
|
5829
5924
|
}
|
|
5830
5925
|
entry.size = item.size;
|
|
@@ -5833,11 +5928,10 @@ function normalizeFileEntries(value) {
|
|
|
5833
5928
|
});
|
|
5834
5929
|
}
|
|
5835
5930
|
function normalizePorts(value) {
|
|
5836
|
-
if (!Array.isArray(value))
|
|
5931
|
+
if (!Array.isArray(value))
|
|
5837
5932
|
throw new TypeError("Sandbox publishedPorts returned an invalid payload");
|
|
5838
|
-
}
|
|
5839
5933
|
return value.map((item) => {
|
|
5840
|
-
if (!
|
|
5934
|
+
if (!isRecord5(item) || !isPort(item.containerPort) || typeof item.host !== "string" || !isPort(item.hostPort) || typeof item.protocol !== "string") {
|
|
5841
5935
|
throw new TypeError("Sandbox publishedPorts returned an invalid entry");
|
|
5842
5936
|
}
|
|
5843
5937
|
return {
|
|
@@ -5849,17 +5943,16 @@ function normalizePorts(value) {
|
|
|
5849
5943
|
});
|
|
5850
5944
|
}
|
|
5851
5945
|
function normalizeProcesses(value) {
|
|
5852
|
-
if (!Array.isArray(value))
|
|
5946
|
+
if (!Array.isArray(value))
|
|
5853
5947
|
throw new TypeError("Sandbox listProcesses returned an invalid payload");
|
|
5854
|
-
}
|
|
5855
5948
|
return value.map((item) => {
|
|
5856
|
-
if (!
|
|
5949
|
+
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
5950
|
throw new TypeError("Sandbox listProcesses returned an invalid entry");
|
|
5858
5951
|
}
|
|
5859
5952
|
const process2 = {
|
|
5860
5953
|
id: item.id,
|
|
5861
5954
|
command: item.command,
|
|
5862
|
-
args: item.args,
|
|
5955
|
+
args: [...item.args],
|
|
5863
5956
|
status: item.status,
|
|
5864
5957
|
startedAt: item.startedAt
|
|
5865
5958
|
};
|
|
@@ -5870,11 +5963,11 @@ function normalizeProcesses(value) {
|
|
|
5870
5963
|
});
|
|
5871
5964
|
}
|
|
5872
5965
|
function normalizeProcessLogs(value, maxBytes) {
|
|
5873
|
-
if (!
|
|
5966
|
+
if (!isRecord5(value) || !(value.stdout instanceof Uint8Array) || !(value.stderr instanceof Uint8Array) || typeof value.stdoutTruncated !== "boolean" || typeof value.stderrTruncated !== "boolean") {
|
|
5874
5967
|
throw new TypeError("Sandbox readProcessLogs returned an invalid payload");
|
|
5875
5968
|
}
|
|
5876
|
-
const stdout =
|
|
5877
|
-
const stderr =
|
|
5969
|
+
const stdout = boundedLogBytes(value.stdout, maxBytes);
|
|
5970
|
+
const stderr = boundedLogBytes(value.stderr, maxBytes);
|
|
5878
5971
|
return {
|
|
5879
5972
|
stdout: stdout.text,
|
|
5880
5973
|
stderr: stderr.text,
|
|
@@ -5882,21 +5975,13 @@ function normalizeProcessLogs(value, maxBytes) {
|
|
|
5882
5975
|
stderrTruncated: value.stderrTruncated || stderr.truncated
|
|
5883
5976
|
};
|
|
5884
5977
|
}
|
|
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
|
-
}
|
|
5978
|
+
function boundedLogBytes(value, maxBytes) {
|
|
5979
|
+
if (maxBytes === 0) return { text: "", truncated: value.byteLength > 0 };
|
|
5980
|
+
let start = Math.max(0, value.byteLength - maxBytes);
|
|
5981
|
+
while (start < value.byteLength && ((value[start] ?? 0) & 192) === 128) start += 1;
|
|
5897
5982
|
return {
|
|
5898
|
-
text: new TextDecoder().decode(
|
|
5899
|
-
truncated:
|
|
5983
|
+
text: new TextDecoder("utf-8", { fatal: true }).decode(value.subarray(start)),
|
|
5984
|
+
truncated: start > 0
|
|
5900
5985
|
};
|
|
5901
5986
|
}
|
|
5902
5987
|
function normalizeSandboxPath(input, allowRoot) {
|
|
@@ -5919,9 +6004,7 @@ function normalizeSandboxPath(input, allowRoot) {
|
|
|
5919
6004
|
return normalized;
|
|
5920
6005
|
}
|
|
5921
6006
|
function parseTailBytes(value) {
|
|
5922
|
-
if (value === void 0)
|
|
5923
|
-
return defaultProcessLogBytes;
|
|
5924
|
-
}
|
|
6007
|
+
if (value === void 0) return defaultProcessLogBytes;
|
|
5925
6008
|
const parsed = Number(value);
|
|
5926
6009
|
if (!Number.isInteger(parsed) || parsed < 0 || parsed > maxProcessLogBytes) {
|
|
5927
6010
|
throw new SandboxRouteError(
|
|
@@ -5942,15 +6025,13 @@ function sandboxErrorResponse(c, error) {
|
|
|
5942
6025
|
if (error instanceof SandboxRouteError) {
|
|
5943
6026
|
return errorResponse(c, error.status, error.code, error.message);
|
|
5944
6027
|
}
|
|
5945
|
-
const
|
|
5946
|
-
if (
|
|
5947
|
-
|
|
5948
|
-
}
|
|
5949
|
-
if (name === "SandboxFileSizeError") {
|
|
6028
|
+
const code = errorCode(error);
|
|
6029
|
+
if (code === "invalid_path") return errorResponse(c, 400, "bad_request", errorMessage(error));
|
|
6030
|
+
if (code === "file_too_large") {
|
|
5950
6031
|
return errorResponse(c, 413, "payload_too_large", errorMessage(error));
|
|
5951
6032
|
}
|
|
5952
|
-
if (
|
|
5953
|
-
return errorResponse(c, 409, "conflict", "Sandbox
|
|
6033
|
+
if (code === "invalid_state" || code === "sandbox_not_found") {
|
|
6034
|
+
return errorResponse(c, 409, "conflict", "Sandbox is no longer available");
|
|
5954
6035
|
}
|
|
5955
6036
|
return errorResponse(
|
|
5956
6037
|
c,
|
|
@@ -5976,6 +6057,18 @@ var SandboxRouteError = class extends Error {
|
|
|
5976
6057
|
status;
|
|
5977
6058
|
code;
|
|
5978
6059
|
};
|
|
6060
|
+
function sortedUnique(values) {
|
|
6061
|
+
if (!Array.isArray(values)) throw new TypeError("Sandbox associations must be arrays.");
|
|
6062
|
+
const result = /* @__PURE__ */ new Set();
|
|
6063
|
+
for (const value of values) {
|
|
6064
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
6065
|
+
throw new TypeError("Sandbox associations must contain non-empty strings.");
|
|
6066
|
+
}
|
|
6067
|
+
if (result.has(value)) throw new TypeError(`Sandbox association is duplicated: ${value}`);
|
|
6068
|
+
result.add(value);
|
|
6069
|
+
}
|
|
6070
|
+
return [...result].sort();
|
|
6071
|
+
}
|
|
5979
6072
|
function isFileType(value) {
|
|
5980
6073
|
return value === "file" || value === "directory" || value === "symlink" || value === "other";
|
|
5981
6074
|
}
|
|
@@ -5985,14 +6078,14 @@ function isProcessStatus(value) {
|
|
|
5985
6078
|
function isPort(value) {
|
|
5986
6079
|
return Number.isInteger(value) && Number(value) >= 1 && Number(value) <= 65535;
|
|
5987
6080
|
}
|
|
5988
|
-
function
|
|
5989
|
-
return typeof value === "object" && value !== null;
|
|
6081
|
+
function isRecord5(value) {
|
|
6082
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5990
6083
|
}
|
|
5991
|
-
function
|
|
5992
|
-
return
|
|
6084
|
+
function errorCode(error) {
|
|
6085
|
+
return isRecord5(error) && typeof error.code === "string" ? error.code : void 0;
|
|
5993
6086
|
}
|
|
5994
6087
|
function errorMessage(error) {
|
|
5995
|
-
return
|
|
6088
|
+
return isRecord5(error) && typeof error.message === "string" ? error.message : "Sandbox failed";
|
|
5996
6089
|
}
|
|
5997
6090
|
|
|
5998
6091
|
// src/runtime/sessions.ts
|
|
@@ -6143,6 +6236,16 @@ function createInMemoryStudioStore() {
|
|
|
6143
6236
|
}
|
|
6144
6237
|
var InMemoryStudioStore = class {
|
|
6145
6238
|
kind = "memory";
|
|
6239
|
+
compaction = {
|
|
6240
|
+
snapshot: ({ scope }) => {
|
|
6241
|
+
const messages = this.sessions.get(scope.sessionId)?.messages ?? [];
|
|
6242
|
+
return Promise.resolve({
|
|
6243
|
+
revision: memoryRevision(messages),
|
|
6244
|
+
messages: cloneMessages(messages)
|
|
6245
|
+
});
|
|
6246
|
+
},
|
|
6247
|
+
replacePrefix: (options) => this.replaceCompactionPrefix(options)
|
|
6248
|
+
};
|
|
6146
6249
|
sessions = /* @__PURE__ */ new Map();
|
|
6147
6250
|
traces = /* @__PURE__ */ new Map();
|
|
6148
6251
|
pipelineLogs = /* @__PURE__ */ new Map();
|
|
@@ -6184,20 +6287,20 @@ var InMemoryStudioStore = class {
|
|
|
6184
6287
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6185
6288
|
return materializeSession(session);
|
|
6186
6289
|
}
|
|
6187
|
-
load(
|
|
6188
|
-
return Promise.resolve(this.sessions.get(
|
|
6290
|
+
load({ scope }) {
|
|
6291
|
+
return Promise.resolve(cloneMessages(this.sessions.get(scope.sessionId)?.messages ?? []));
|
|
6189
6292
|
}
|
|
6190
6293
|
append(input) {
|
|
6191
|
-
const session = this.sessions.get(input.
|
|
6294
|
+
const session = this.sessions.get(input.scope.sessionId);
|
|
6192
6295
|
if (session !== void 0) {
|
|
6193
|
-
session.messages.push(...input.messages);
|
|
6296
|
+
session.messages.push(...cloneMessages(input.messages));
|
|
6194
6297
|
session.messageCount = session.messages.length;
|
|
6195
6298
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6196
6299
|
}
|
|
6197
6300
|
return Promise.resolve();
|
|
6198
6301
|
}
|
|
6199
|
-
clear(
|
|
6200
|
-
const session = this.sessions.get(
|
|
6302
|
+
clear({ scope }) {
|
|
6303
|
+
const session = this.sessions.get(scope.sessionId);
|
|
6201
6304
|
if (session !== void 0) {
|
|
6202
6305
|
session.messages = [];
|
|
6203
6306
|
session.runs = [];
|
|
@@ -6208,13 +6311,26 @@ var InMemoryStudioStore = class {
|
|
|
6208
6311
|
}
|
|
6209
6312
|
async recordError(input) {
|
|
6210
6313
|
await this.saveSessionRunTranscript({
|
|
6211
|
-
id: input.
|
|
6212
|
-
runId: studioRunId(input.
|
|
6314
|
+
id: input.scope.sessionId,
|
|
6315
|
+
runId: studioRunId(input.scope) ?? input.runId,
|
|
6213
6316
|
transcript: transcriptFromMessages(input.messages),
|
|
6214
6317
|
status: "error",
|
|
6215
6318
|
error: serializeJsonError(input.error)
|
|
6216
6319
|
});
|
|
6217
6320
|
}
|
|
6321
|
+
replaceCompactionPrefix(input) {
|
|
6322
|
+
if (!Number.isSafeInteger(input.messageCount) || input.messageCount < 1) {
|
|
6323
|
+
throw new RangeError("messageCount must be a positive integer.");
|
|
6324
|
+
}
|
|
6325
|
+
const session = this.sessions.get(input.scope.sessionId);
|
|
6326
|
+
if (session === void 0 || memoryRevision(session.messages) !== input.revision || input.messageCount > session.messages.length) {
|
|
6327
|
+
return Promise.resolve({ status: "conflict" });
|
|
6328
|
+
}
|
|
6329
|
+
session.messages.splice(0, input.messageCount, structuredClone(input.replacement));
|
|
6330
|
+
session.messageCount = session.messages.length;
|
|
6331
|
+
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6332
|
+
return Promise.resolve({ status: "committed" });
|
|
6333
|
+
}
|
|
6218
6334
|
saveSessionRunTranscript(input) {
|
|
6219
6335
|
const session = this.sessions.get(input.id);
|
|
6220
6336
|
if (session === void 0) {
|
|
@@ -6340,18 +6456,24 @@ function sessionSummary(session) {
|
|
|
6340
6456
|
function materializeSession(session) {
|
|
6341
6457
|
return {
|
|
6342
6458
|
...sessionSummary(session),
|
|
6343
|
-
messages:
|
|
6459
|
+
messages: cloneMessages(session.messages),
|
|
6344
6460
|
transcript: renumberTranscript(session.runs.flatMap((run) => run.transcript))
|
|
6345
6461
|
};
|
|
6346
6462
|
}
|
|
6463
|
+
function cloneMessages(messages) {
|
|
6464
|
+
return structuredClone([...messages]);
|
|
6465
|
+
}
|
|
6347
6466
|
function traceAgentId(trace) {
|
|
6348
6467
|
const nestedMetadata = trace.metadata?.metadata;
|
|
6349
6468
|
return isJsonObject(nestedMetadata) && typeof nestedMetadata.agentId === "string" ? nestedMetadata.agentId : void 0;
|
|
6350
6469
|
}
|
|
6351
|
-
function studioRunId(
|
|
6352
|
-
const value =
|
|
6470
|
+
function studioRunId(scope) {
|
|
6471
|
+
const value = scope.metadata?.studioRunId;
|
|
6353
6472
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6354
6473
|
}
|
|
6474
|
+
function memoryRevision(messages) {
|
|
6475
|
+
return JSON.stringify(messages);
|
|
6476
|
+
}
|
|
6355
6477
|
function serializeJsonError(error) {
|
|
6356
6478
|
if (error instanceof Error) {
|
|
6357
6479
|
return {
|
|
@@ -6687,6 +6809,7 @@ var Studio = class {
|
|
|
6687
6809
|
options;
|
|
6688
6810
|
studio;
|
|
6689
6811
|
server;
|
|
6812
|
+
websocketServer;
|
|
6690
6813
|
sigintHandler;
|
|
6691
6814
|
constructor(targets = [], options = {}) {
|
|
6692
6815
|
this.options = studioOptionsFromTargets(targets, options);
|
|
@@ -6711,9 +6834,15 @@ var Studio = class {
|
|
|
6711
6834
|
this.studio = createStudioApp(this.options);
|
|
6712
6835
|
const port = serveOptions.port ?? Number(process.env.RUNNER_PORT ?? 4021);
|
|
6713
6836
|
const serverOptions = {
|
|
6714
|
-
fetch: (request) => this.fetch(request),
|
|
6837
|
+
fetch: (request, env) => this.studio.app.fetch(request, env),
|
|
6715
6838
|
port
|
|
6716
6839
|
};
|
|
6840
|
+
this.websocketServer = new WebSocketServer({ noServer: true });
|
|
6841
|
+
serverOptions.websocket = {
|
|
6842
|
+
// @types/ws overloads are stricter than Hono's structural adapter, while Hono officially
|
|
6843
|
+
// supports ws.WebSocketServer. Keep the interop assertion at this single ownership boundary.
|
|
6844
|
+
server: this.websocketServer
|
|
6845
|
+
};
|
|
6717
6846
|
if (serveOptions.hostname !== void 0) serverOptions.hostname = serveOptions.hostname;
|
|
6718
6847
|
this.server = serve(serverOptions);
|
|
6719
6848
|
const log = serveOptions.log ?? true;
|
|
@@ -6758,6 +6887,8 @@ var Studio = class {
|
|
|
6758
6887
|
}
|
|
6759
6888
|
this.server?.close();
|
|
6760
6889
|
this.server = void 0;
|
|
6890
|
+
this.websocketServer?.close();
|
|
6891
|
+
this.websocketServer = void 0;
|
|
6761
6892
|
this.studio.close();
|
|
6762
6893
|
}
|
|
6763
6894
|
logAddress(host, port) {
|
|
@@ -6836,6 +6967,21 @@ function studioOptionsFromTargets(targets, options) {
|
|
|
6836
6967
|
if (options.models !== void 0) runtimeOptions.models = options.models;
|
|
6837
6968
|
if (options.stores !== void 0) runtimeOptions.stores = options.stores;
|
|
6838
6969
|
if (options.ui !== void 0) runtimeOptions.ui = options.ui;
|
|
6970
|
+
if (options.sandboxes !== void 0) {
|
|
6971
|
+
runtimeOptions.sandboxes = options.sandboxes.map((registration) => {
|
|
6972
|
+
let snapshot = { inspector: registration.inspector };
|
|
6973
|
+
if (registration.agentIds !== void 0) {
|
|
6974
|
+
snapshot = { ...snapshot, agentIds: [...registration.agentIds] };
|
|
6975
|
+
}
|
|
6976
|
+
if (registration.toolNames !== void 0) {
|
|
6977
|
+
snapshot = { ...snapshot, toolNames: [...registration.toolNames] };
|
|
6978
|
+
}
|
|
6979
|
+
if (registration.views !== void 0) {
|
|
6980
|
+
snapshot = { ...snapshot, views: [...registration.views] };
|
|
6981
|
+
}
|
|
6982
|
+
return snapshot;
|
|
6983
|
+
});
|
|
6984
|
+
}
|
|
6839
6985
|
return runtimeOptions;
|
|
6840
6986
|
}
|
|
6841
6987
|
function inferStudioAgents(agents, quickPrompts) {
|
|
@@ -6877,11 +7023,11 @@ function uniqueAgentId(baseId, ids) {
|
|
|
6877
7023
|
function agentMetadata(agent) {
|
|
6878
7024
|
const metadata = {
|
|
6879
7025
|
staticContextCount: staticContextDocuments(agent).length,
|
|
6880
|
-
dynamicContextCount:
|
|
7026
|
+
dynamicContextCount: vectorContexts(agent).length,
|
|
6881
7027
|
dynamicToolCount: getAgentToolState3(agent).toolIndexes.length,
|
|
6882
7028
|
hasLifecycle: agent.lifecycle !== void 0,
|
|
6883
7029
|
hasOutputSchema: agent.outputSchema !== void 0,
|
|
6884
|
-
observerCount: agent.observers.length,
|
|
7030
|
+
observerCount: Object.keys(agent.observability?.observers ?? {}).length,
|
|
6885
7031
|
approvalToolCount: agent.tools.filter(toolRequiresApproval).length
|
|
6886
7032
|
};
|
|
6887
7033
|
if (agent.defaultMaxTurns !== void 0) metadata.defaultMaxTurns = agent.defaultMaxTurns;
|
|
@@ -6898,9 +7044,8 @@ function createStudioApp(options) {
|
|
|
6898
7044
|
const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
|
|
6899
7045
|
const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
|
|
6900
7046
|
const evalMap = new Map(options.evals.map((suite) => [suite.id ?? suite.name, suite]));
|
|
6901
|
-
const
|
|
6902
|
-
const
|
|
6903
|
-
const sandboxRegistry = createStudioSandboxRegistry(agents);
|
|
7047
|
+
const continuationRegistry = createStudioContinuationRegistry();
|
|
7048
|
+
const sandboxRegistry = createStudioSandboxRegistry(agents, options.sandboxes ?? []);
|
|
6904
7049
|
const memorySources = createStudioMemorySourceRegistry(agents, stores.sessions);
|
|
6905
7050
|
const app = new HonoApp();
|
|
6906
7051
|
const uiOptions = isStudioUiEnabled(options.ui) ? resolveStudioUiOptions(options.ui) : void 0;
|
|
@@ -6951,8 +7096,7 @@ function createStudioApp(options) {
|
|
|
6951
7096
|
registerMcpRoutes(app, { agentMap });
|
|
6952
7097
|
registerToolRoutes(app, { agentMap });
|
|
6953
7098
|
registerSandboxRoutes(app, sandboxRegistry);
|
|
6954
|
-
|
|
6955
|
-
registerQuestionRoutes(app, questionRuntime);
|
|
7099
|
+
const closeSandboxViews = registerSandboxViewRoutes(app, sandboxRegistry);
|
|
6956
7100
|
registerObservabilityRoutes(app, observabilityHub);
|
|
6957
7101
|
registerEvalRoutes(app, {
|
|
6958
7102
|
evals: options.evals,
|
|
@@ -6972,8 +7116,7 @@ function createStudioApp(options) {
|
|
|
6972
7116
|
agentMap,
|
|
6973
7117
|
stores,
|
|
6974
7118
|
modelRegistry,
|
|
6975
|
-
|
|
6976
|
-
questionRuntime
|
|
7119
|
+
continuationRegistry
|
|
6977
7120
|
});
|
|
6978
7121
|
if (memorySources.size > 0 || stores.sessions !== void 0) {
|
|
6979
7122
|
registerMemoryRoutes(app, {
|
|
@@ -7005,6 +7148,8 @@ function createStudioApp(options) {
|
|
|
7005
7148
|
return buildConfig(options, agents, pipelines, stores, sandboxRegistry.size);
|
|
7006
7149
|
},
|
|
7007
7150
|
close() {
|
|
7151
|
+
continuationRegistry.clear();
|
|
7152
|
+
closeSandboxViews();
|
|
7008
7153
|
}
|
|
7009
7154
|
};
|
|
7010
7155
|
if (stores.sessions !== void 0) Object.assign(studio, { sessionStore: stores.sessions });
|
|
@@ -7012,30 +7157,37 @@ function createStudioApp(options) {
|
|
|
7012
7157
|
return studio;
|
|
7013
7158
|
}
|
|
7014
7159
|
function withStudioTraceObserver(studioAgent, traceStore) {
|
|
7015
|
-
if (traceStore === void 0
|
|
7016
|
-
|
|
7160
|
+
if (traceStore === void 0) return studioAgent;
|
|
7161
|
+
const existing = studioAgent.agent.observability;
|
|
7162
|
+
const studioObserver = existing?.observers.studio;
|
|
7163
|
+
if (studioObserver !== void 0 && !(studioObserver instanceof StudioTraceObserver)) {
|
|
7164
|
+
throw new TypeError('Studio reserves the observer name "studio" for local trace persistence.');
|
|
7165
|
+
}
|
|
7166
|
+
for (const [name, observer] of Object.entries(existing?.observers ?? {})) {
|
|
7167
|
+
if (observer instanceof StudioTraceObserver && name !== "studio") {
|
|
7168
|
+
throw new TypeError('StudioTraceObserver must be registered with the name "studio".');
|
|
7169
|
+
}
|
|
7017
7170
|
}
|
|
7018
7171
|
return {
|
|
7019
7172
|
...studioAgent,
|
|
7020
7173
|
agent: cloneAgent(studioAgent.agent, {
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7174
|
+
observability: {
|
|
7175
|
+
observers: {
|
|
7176
|
+
...existing?.observers ?? {},
|
|
7177
|
+
studio: studioObserver ?? new StudioTraceObserver({ store: traceStore })
|
|
7178
|
+
},
|
|
7179
|
+
primaryTrace: "studio",
|
|
7180
|
+
errorPolicy: existing?.errorPolicy ?? "ignore"
|
|
7181
|
+
}
|
|
7025
7182
|
})
|
|
7026
7183
|
};
|
|
7027
7184
|
}
|
|
7028
|
-
function hasStudioTraceObserver(agent) {
|
|
7029
|
-
return agent.observers.some(
|
|
7030
|
-
(registration) => registration.observer instanceof StudioTraceObserver
|
|
7031
|
-
);
|
|
7032
|
-
}
|
|
7033
7185
|
|
|
7034
7186
|
// src/storage/sqlite-store.ts
|
|
7035
7187
|
import { mkdirSync } from "fs";
|
|
7036
7188
|
import { createRequire } from "module";
|
|
7037
7189
|
import { dirname, resolve } from "path";
|
|
7038
|
-
import { isJsonValue as
|
|
7190
|
+
import { isJsonValue as isJsonValue3 } from "@anvia/core/completion";
|
|
7039
7191
|
var DatabaseSync;
|
|
7040
7192
|
function createSqliteSessionStore(options = {}) {
|
|
7041
7193
|
return new SqliteSessionStore(options.path ?? ":memory:");
|
|
@@ -7046,6 +7198,13 @@ var SqliteSessionStore = class {
|
|
|
7046
7198
|
}
|
|
7047
7199
|
path;
|
|
7048
7200
|
kind = "sqlite";
|
|
7201
|
+
compaction = {
|
|
7202
|
+
snapshot: ({ scope }) => {
|
|
7203
|
+
const messages = this.listSessionMessages(scope.sessionId);
|
|
7204
|
+
return Promise.resolve({ revision: memoryRevision2(messages), messages });
|
|
7205
|
+
},
|
|
7206
|
+
replacePrefix: (options) => this.replaceCompactionPrefix(options)
|
|
7207
|
+
};
|
|
7049
7208
|
db;
|
|
7050
7209
|
listSessions(options) {
|
|
7051
7210
|
const db = this.database();
|
|
@@ -7117,8 +7276,8 @@ var SqliteSessionStore = class {
|
|
|
7117
7276
|
}
|
|
7118
7277
|
return this.getSession(id);
|
|
7119
7278
|
}
|
|
7120
|
-
load(
|
|
7121
|
-
const session = this.getSession(
|
|
7279
|
+
load({ scope }) {
|
|
7280
|
+
const session = this.getSession(scope.sessionId);
|
|
7122
7281
|
return Promise.resolve(session?.messages ?? []);
|
|
7123
7282
|
}
|
|
7124
7283
|
append(input) {
|
|
@@ -7129,20 +7288,20 @@ var SqliteSessionStore = class {
|
|
|
7129
7288
|
`SELECT id, agent_id, title, metadata_json, created_at, updated_at
|
|
7130
7289
|
FROM anvia_studio_sessions
|
|
7131
7290
|
WHERE id = $id`
|
|
7132
|
-
).get({ $id: input.
|
|
7291
|
+
).get({ $id: input.scope.sessionId });
|
|
7133
7292
|
if (row === void 0) {
|
|
7134
7293
|
db.exec("ROLLBACK");
|
|
7135
7294
|
return Promise.resolve();
|
|
7136
7295
|
}
|
|
7137
7296
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7138
|
-
const nextIndex = this.nextMessageIndex(input.
|
|
7139
|
-
this.insertMessages(input.
|
|
7297
|
+
const nextIndex = this.nextMessageIndex(input.scope.sessionId);
|
|
7298
|
+
this.insertMessages(input.scope.sessionId, input.messages, nextIndex, updatedAt);
|
|
7140
7299
|
db.prepare(
|
|
7141
7300
|
`UPDATE anvia_studio_sessions
|
|
7142
7301
|
SET updated_at = $updatedAt
|
|
7143
7302
|
WHERE id = $id`
|
|
7144
7303
|
).run({
|
|
7145
|
-
$id: input.
|
|
7304
|
+
$id: input.scope.sessionId,
|
|
7146
7305
|
$updatedAt: updatedAt
|
|
7147
7306
|
});
|
|
7148
7307
|
db.exec("COMMIT");
|
|
@@ -7154,7 +7313,7 @@ var SqliteSessionStore = class {
|
|
|
7154
7313
|
throw error;
|
|
7155
7314
|
}
|
|
7156
7315
|
}
|
|
7157
|
-
clear(
|
|
7316
|
+
clear({ scope }) {
|
|
7158
7317
|
const db = this.database();
|
|
7159
7318
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7160
7319
|
try {
|
|
@@ -7164,14 +7323,14 @@ var SqliteSessionStore = class {
|
|
|
7164
7323
|
SET updated_at = $updatedAt
|
|
7165
7324
|
WHERE id = $id`
|
|
7166
7325
|
).run({
|
|
7167
|
-
$id:
|
|
7326
|
+
$id: scope.sessionId,
|
|
7168
7327
|
$updatedAt: updatedAt
|
|
7169
7328
|
});
|
|
7170
7329
|
db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
|
|
7171
|
-
$id:
|
|
7330
|
+
$id: scope.sessionId
|
|
7172
7331
|
});
|
|
7173
7332
|
db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({
|
|
7174
|
-
$id:
|
|
7333
|
+
$id: scope.sessionId
|
|
7175
7334
|
});
|
|
7176
7335
|
db.exec("COMMIT");
|
|
7177
7336
|
return Promise.resolve();
|
|
@@ -7183,17 +7342,51 @@ var SqliteSessionStore = class {
|
|
|
7183
7342
|
}
|
|
7184
7343
|
}
|
|
7185
7344
|
async recordError(input) {
|
|
7186
|
-
const runId = studioRunId2(input.
|
|
7187
|
-
const existing = this.getSessionRun(input.
|
|
7345
|
+
const runId = studioRunId2(input.scope) ?? input.runId;
|
|
7346
|
+
const existing = this.getSessionRun(input.scope.sessionId, runId);
|
|
7188
7347
|
const transcript = existing === void 0 || parseJsonArray(existing.transcript_json).length === 0 ? transcriptFromMessages(input.messages) : parseJsonArray(existing.transcript_json);
|
|
7189
7348
|
await this.saveSessionRunTranscript({
|
|
7190
|
-
id: input.
|
|
7349
|
+
id: input.scope.sessionId,
|
|
7191
7350
|
runId,
|
|
7192
7351
|
transcript,
|
|
7193
7352
|
status: "error",
|
|
7194
7353
|
error: serializeJsonError2(input.error)
|
|
7195
7354
|
});
|
|
7196
7355
|
}
|
|
7356
|
+
replaceCompactionPrefix(input) {
|
|
7357
|
+
if (!Number.isSafeInteger(input.messageCount) || input.messageCount < 1) {
|
|
7358
|
+
throw new RangeError("messageCount must be a positive integer.");
|
|
7359
|
+
}
|
|
7360
|
+
const db = this.database();
|
|
7361
|
+
try {
|
|
7362
|
+
db.exec("BEGIN IMMEDIATE");
|
|
7363
|
+
const messages = this.listSessionMessages(input.scope.sessionId);
|
|
7364
|
+
if (memoryRevision2(messages) !== input.revision || input.messageCount > messages.length || this.getSessionRow(input.scope.sessionId) === void 0) {
|
|
7365
|
+
db.exec("ROLLBACK");
|
|
7366
|
+
return Promise.resolve({ status: "conflict" });
|
|
7367
|
+
}
|
|
7368
|
+
db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
|
|
7369
|
+
$id: input.scope.sessionId
|
|
7370
|
+
});
|
|
7371
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7372
|
+
this.insertMessages(
|
|
7373
|
+
input.scope.sessionId,
|
|
7374
|
+
[input.replacement, ...messages.slice(input.messageCount)],
|
|
7375
|
+
0,
|
|
7376
|
+
updatedAt
|
|
7377
|
+
);
|
|
7378
|
+
db.prepare(
|
|
7379
|
+
`UPDATE anvia_studio_sessions
|
|
7380
|
+
SET updated_at = $updatedAt
|
|
7381
|
+
WHERE id = $id`
|
|
7382
|
+
).run({ $id: input.scope.sessionId, $updatedAt: updatedAt });
|
|
7383
|
+
db.exec("COMMIT");
|
|
7384
|
+
return Promise.resolve({ status: "committed" });
|
|
7385
|
+
} catch (error) {
|
|
7386
|
+
if (db.isTransaction) db.exec("ROLLBACK");
|
|
7387
|
+
throw error;
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
7197
7390
|
saveSessionRunTranscript(input) {
|
|
7198
7391
|
const db = this.database();
|
|
7199
7392
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7889,7 +8082,7 @@ var SqliteSessionStore = class {
|
|
|
7889
8082
|
);
|
|
7890
8083
|
messages.forEach((message, messageOffset) => {
|
|
7891
8084
|
const messageIndex = startIndex + messageOffset;
|
|
7892
|
-
if (message.metadata !== void 0 && !
|
|
8085
|
+
if (message.metadata !== void 0 && !isJsonValue3(message.metadata)) {
|
|
7893
8086
|
throw new TypeError("Studio message metadata must be a strict JSON value.");
|
|
7894
8087
|
}
|
|
7895
8088
|
insertMessage.run({
|
|
@@ -7990,6 +8183,9 @@ function messageParts(message) {
|
|
|
7990
8183
|
if (message.role === "system") {
|
|
7991
8184
|
return [{ type: "text", value: { type: "text", text: message.content } }];
|
|
7992
8185
|
}
|
|
8186
|
+
if (typeof message.content === "string") {
|
|
8187
|
+
return [{ type: "text", value: { type: "text", text: message.content } }];
|
|
8188
|
+
}
|
|
7993
8189
|
return message.content.map((content) => ({
|
|
7994
8190
|
type: content.type,
|
|
7995
8191
|
value: content
|
|
@@ -7998,40 +8194,40 @@ function messageParts(message) {
|
|
|
7998
8194
|
function messageFromRows(row, partRows) {
|
|
7999
8195
|
const parts = partRows.map((partRow) => JSON.parse(partRow.part_json));
|
|
8000
8196
|
const metadata = parseJsonValue(row.metadata_json);
|
|
8001
|
-
if (metadata !== void 0 && !
|
|
8002
|
-
throw new TypeError("Stored Studio message metadata is not a strict JSON
|
|
8197
|
+
if (metadata !== void 0 && (!isJsonValue3(metadata) || typeof metadata !== "object" || metadata === null || Array.isArray(metadata))) {
|
|
8198
|
+
throw new TypeError("Stored Studio message metadata is not a strict JSON object.");
|
|
8003
8199
|
}
|
|
8004
8200
|
if (row.role === "system") {
|
|
8005
|
-
|
|
8201
|
+
let message = {
|
|
8006
8202
|
role: "system",
|
|
8007
8203
|
content: systemContentFromParts(parts)
|
|
8008
8204
|
};
|
|
8009
|
-
if (metadata !== void 0) message
|
|
8205
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8010
8206
|
return message;
|
|
8011
8207
|
}
|
|
8012
8208
|
if (row.role === "user") {
|
|
8013
|
-
|
|
8209
|
+
let message = {
|
|
8014
8210
|
role: "user",
|
|
8015
8211
|
content: parts
|
|
8016
8212
|
};
|
|
8017
|
-
if (metadata !== void 0) message
|
|
8213
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8018
8214
|
return message;
|
|
8019
8215
|
}
|
|
8020
8216
|
if (row.role === "assistant") {
|
|
8021
|
-
|
|
8217
|
+
let message = {
|
|
8022
8218
|
role: "assistant",
|
|
8023
8219
|
content: parts
|
|
8024
8220
|
};
|
|
8025
|
-
if (row.message_id !== null) message
|
|
8026
|
-
if (metadata !== void 0) message
|
|
8221
|
+
if (row.message_id !== null) message = { ...message, id: row.message_id };
|
|
8222
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8027
8223
|
return message;
|
|
8028
8224
|
}
|
|
8029
8225
|
if (row.role === "tool") {
|
|
8030
|
-
|
|
8226
|
+
let message = {
|
|
8031
8227
|
role: "tool",
|
|
8032
8228
|
content: parts
|
|
8033
8229
|
};
|
|
8034
|
-
if (metadata !== void 0) message
|
|
8230
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8035
8231
|
return message;
|
|
8036
8232
|
}
|
|
8037
8233
|
throw new Error(`Unsupported stored message role: ${row.role}`);
|
|
@@ -8116,10 +8312,13 @@ function parseJsonValue(value) {
|
|
|
8116
8312
|
}
|
|
8117
8313
|
return JSON.parse(value);
|
|
8118
8314
|
}
|
|
8119
|
-
function studioRunId2(
|
|
8120
|
-
const value =
|
|
8315
|
+
function studioRunId2(scope) {
|
|
8316
|
+
const value = scope.metadata?.studioRunId;
|
|
8121
8317
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
8122
8318
|
}
|
|
8319
|
+
function memoryRevision2(messages) {
|
|
8320
|
+
return JSON.stringify(messages);
|
|
8321
|
+
}
|
|
8123
8322
|
function serializeJsonError2(error) {
|
|
8124
8323
|
if (error instanceof Error) {
|
|
8125
8324
|
return {
|