@anvia/studio 1.0.0-rc.2 → 1.0.0-rc.4
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 +38 -0
- package/dist/index.d.ts +152 -67
- package/dist/index.js +921 -1001
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/ArrowClockwise.es-CSEY3Qr6.js +2 -0
- package/dist/ui/assets/{ArrowClockwise.es-DMJj7Hv0.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-CbQpO3Tg.js.map → agents-page-DQqt45If.js.map} +1 -1
- package/dist/ui/assets/{badge-BIWy1K8W.js → badge-CcajoiAt.js} +1 -1
- package/dist/ui/assets/{badge-BIWy1K8W.js.map → badge-CcajoiAt.js.map} +1 -1
- package/dist/ui/assets/button-07gzSO47.js +2 -0
- package/dist/ui/assets/button-07gzSO47.js.map +1 -0
- package/dist/ui/assets/chunk-D2ECOWN5-fs3ih0yA.js +65 -0
- package/dist/ui/assets/chunk-D2ECOWN5-fs3ih0yA.js.map +1 -0
- package/dist/ui/assets/dialog-CNNQ3hoF.js +2 -0
- package/dist/ui/assets/dialog-CNNQ3hoF.js.map +1 -0
- package/dist/ui/assets/dist-CqMdRzwk.js +2 -0
- package/dist/ui/assets/dist-CqMdRzwk.js.map +1 -0
- package/dist/ui/assets/dist-DWqJwN1i.js +2 -0
- package/dist/ui/assets/dist-DWqJwN1i.js.map +1 -0
- package/dist/ui/assets/es2015-DsZq7PFO.js +42 -0
- package/dist/ui/assets/es2015-DsZq7PFO.js.map +1 -0
- package/dist/ui/assets/icon-CqkMSRbt.js +2 -0
- package/dist/ui/assets/icon-CqkMSRbt.js.map +1 -0
- package/dist/ui/assets/index-BGNC3dLD.css +1 -0
- package/dist/ui/assets/index-BLaXiUWO.js +12 -0
- package/dist/ui/assets/index-BLaXiUWO.js.map +1 -0
- package/dist/ui/assets/{input-BTHDDFpv.js → input-BiI6gsxA.js} +1 -1
- package/dist/ui/assets/{input-BTHDDFpv.js.map → input-BiI6gsxA.js.map} +1 -1
- package/dist/ui/assets/knowledge-model-S51n2rKh.js +2 -0
- package/dist/ui/assets/knowledge-model-S51n2rKh.js.map +1 -0
- package/dist/ui/assets/knowledge-page-DzRhA9qQ.js +2 -0
- package/dist/ui/assets/{knowledge-page-C8zg8uTL.js.map → knowledge-page-DzRhA9qQ.js.map} +1 -1
- package/dist/ui/assets/mcps-page-ZVngljXI.js +2 -0
- package/dist/ui/assets/{mcps-page-gNYeqSPB.js.map → mcps-page-ZVngljXI.js.map} +1 -1
- package/dist/ui/assets/{memory-page-BLrfOqyU.js → memory-page-DAJk0Zof.js} +3 -3
- package/dist/ui/assets/{memory-page-BLrfOqyU.js.map → memory-page-DAJk0Zof.js.map} +1 -1
- package/dist/ui/assets/pipelines-page-8osYA129.js +8 -0
- package/dist/ui/assets/{pipelines-page-hTEhFZGg.js.map → pipelines-page-8osYA129.js.map} +1 -1
- package/dist/ui/assets/{renderers-DC_-TD_X.js → renderers-BPGo4d2i.js} +2 -2
- package/dist/ui/assets/{renderers-DC_-TD_X.js.map → renderers-BPGo4d2i.js.map} +1 -1
- package/dist/ui/assets/request-7oUPS8zY.js +4 -0
- package/dist/ui/assets/request-7oUPS8zY.js.map +1 -0
- package/dist/ui/assets/rfb-DiW3PDuT.js +6 -0
- package/dist/ui/assets/rfb-DiW3PDuT.js.map +1 -0
- package/dist/ui/assets/sandboxes-page-C6kokYwc.js +2 -0
- package/dist/ui/assets/sandboxes-page-C6kokYwc.js.map +1 -0
- package/dist/ui/assets/select-BOYfrfck.js +2 -0
- package/dist/ui/assets/select-BOYfrfck.js.map +1 -0
- package/dist/ui/assets/sessions-page-ClXzRM9K.js +2 -0
- package/dist/ui/assets/{sessions-page-DBzoP7MV.js.map → sessions-page-ClXzRM9K.js.map} +1 -1
- package/dist/ui/assets/status-page-DfuQ0fpa.js +2 -0
- package/dist/ui/assets/{status-page-Bf1lq444.js.map → status-page-DfuQ0fpa.js.map} +1 -1
- package/dist/ui/assets/studio-CeTsQ4gf.js +2 -0
- package/dist/ui/assets/studio-CeTsQ4gf.js.map +1 -0
- package/dist/ui/assets/textarea-DESKg_ZK.js +2 -0
- package/dist/ui/assets/textarea-DESKg_ZK.js.map +1 -0
- package/dist/ui/assets/tool-runner-Cdrcs2v4.js +2 -0
- package/dist/ui/assets/{tool-runner-BbUlfPEO.js.map → tool-runner-Cdrcs2v4.js.map} +1 -1
- package/dist/ui/assets/tools-page-C865VQmM.js +2 -0
- package/dist/ui/assets/{tools-page-BgUz-79v.js.map → tools-page-C865VQmM.js.map} +1 -1
- package/dist/ui/assets/trace-browser-D93H7Xxs.js +10 -0
- package/dist/ui/assets/{trace-browser-qfYPcdR2.js.map → trace-browser-D93H7Xxs.js.map} +1 -1
- package/dist/ui/assets/transcript-item-BadzBIzE.js +3 -0
- package/dist/ui/assets/transcript-item-BadzBIzE.js.map +1 -0
- package/dist/ui/assets/with-selector-CRZWaZzC.js +2 -0
- package/dist/ui/assets/with-selector-CRZWaZzC.js.map +1 -0
- package/dist/ui/index.html +19 -2
- package/package.json +11 -7
- package/dist/ui/assets/ArrowClockwise.es-DMJj7Hv0.js +0 -2
- package/dist/ui/assets/agents-page-CbQpO3Tg.js +0 -2
- package/dist/ui/assets/index-D-CbFx6c.css +0 -1
- package/dist/ui/assets/index-DizotQot.js +0 -119
- package/dist/ui/assets/index-DizotQot.js.map +0 -1
- package/dist/ui/assets/knowledge-page-C8zg8uTL.js +0 -2
- package/dist/ui/assets/mcps-page-gNYeqSPB.js +0 -2
- package/dist/ui/assets/pipelines-page-hTEhFZGg.js +0 -8
- package/dist/ui/assets/sandboxes-page-DtPnuza7.js +0 -2
- package/dist/ui/assets/sandboxes-page-DtPnuza7.js.map +0 -1
- package/dist/ui/assets/sessions-page-DBzoP7MV.js +0 -2
- package/dist/ui/assets/status-page-Bf1lq444.js +0 -2
- package/dist/ui/assets/tool-runner-BbUlfPEO.js +0 -2
- package/dist/ui/assets/tools-page-BgUz-79v.js +0 -2
- package/dist/ui/assets/trace-browser-qfYPcdR2.js +0 -10
- package/dist/ui/assets/transcript-item-HFuuqmKM.js +0 -3
- package/dist/ui/assets/transcript-item-HFuuqmKM.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,14 +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
209
|
output: args.status === "completed" ? traceOutput(args.output) : args.text,
|
|
182
|
-
result
|
|
183
|
-
status: args.status,
|
|
184
|
-
text: args.text,
|
|
185
|
-
...args.status === "completed" ? { output: toJsonValue(args.output) } : { stage: args.stage }
|
|
186
|
-
},
|
|
210
|
+
result,
|
|
187
211
|
usage: args.usage,
|
|
188
212
|
messages: toJsonValue(args.messages)
|
|
189
213
|
});
|
|
@@ -1319,7 +1343,7 @@ function requestModalities(request) {
|
|
|
1319
1343
|
return modalities;
|
|
1320
1344
|
}
|
|
1321
1345
|
function requestMessages(request) {
|
|
1322
|
-
return request.messages;
|
|
1346
|
+
return request.type === "messages" ? request.messages : [];
|
|
1323
1347
|
}
|
|
1324
1348
|
function normalizeProviderId(id) {
|
|
1325
1349
|
const trimmed = id.trim();
|
|
@@ -1611,6 +1635,7 @@ function isEventType(value) {
|
|
|
1611
1635
|
import {
|
|
1612
1636
|
customAgentEventsToClientStream
|
|
1613
1637
|
} from "@anvia/client";
|
|
1638
|
+
import { parseAgentInteractionResponse } from "@anvia/core/agent";
|
|
1614
1639
|
import {
|
|
1615
1640
|
isJsonValue as isJsonValue2,
|
|
1616
1641
|
parseMessages
|
|
@@ -1644,26 +1669,31 @@ function transcriptFromMessages(messages) {
|
|
|
1644
1669
|
}
|
|
1645
1670
|
}
|
|
1646
1671
|
if (!textEntryAdded && attachments.length > 0) {
|
|
1647
|
-
|
|
1672
|
+
const entry = {
|
|
1648
1673
|
entryId: transcript.length,
|
|
1649
1674
|
kind: "message",
|
|
1650
1675
|
role: "user",
|
|
1651
1676
|
text: "",
|
|
1652
1677
|
attachments
|
|
1653
|
-
}
|
|
1678
|
+
};
|
|
1679
|
+
transcript.push(entry);
|
|
1654
1680
|
}
|
|
1655
1681
|
continue;
|
|
1656
1682
|
}
|
|
1657
1683
|
if (message.role === "tool") {
|
|
1658
1684
|
for (const content of message.content) {
|
|
1659
|
-
|
|
1685
|
+
if (content.type !== "tool-result") {
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
const entry = {
|
|
1660
1689
|
entryId: transcript.length,
|
|
1661
1690
|
kind: "tool",
|
|
1662
1691
|
toolName: content.toolName,
|
|
1663
1692
|
callId: content.callId ?? content.toolCallId,
|
|
1664
|
-
result: toolResultText(content.output)
|
|
1665
|
-
|
|
1666
|
-
|
|
1693
|
+
result: toolResultText(content.output)
|
|
1694
|
+
};
|
|
1695
|
+
if (content.output.type === "content") entry.structuredResult = content.output.value;
|
|
1696
|
+
transcript.push(entry);
|
|
1667
1697
|
}
|
|
1668
1698
|
continue;
|
|
1669
1699
|
}
|
|
@@ -1774,46 +1804,6 @@ var AsyncEventQueue = class {
|
|
|
1774
1804
|
return new Promise((resolve2) => this.resolvers.push(resolve2));
|
|
1775
1805
|
}
|
|
1776
1806
|
};
|
|
1777
|
-
async function* mergeRunAndApprovalEvents(runEvents, approvalEvents) {
|
|
1778
|
-
const runIterator = runEvents[Symbol.asyncIterator]();
|
|
1779
|
-
let runDone = false;
|
|
1780
|
-
let runNext = runIterator.next();
|
|
1781
|
-
let approvalNext = approvalEvents.next();
|
|
1782
|
-
try {
|
|
1783
|
-
while (runNext !== void 0 || approvalNext !== void 0) {
|
|
1784
|
-
const pending = [];
|
|
1785
|
-
if (runNext !== void 0) {
|
|
1786
|
-
pending.push(runNext.then((value) => ({ source: "run", value })));
|
|
1787
|
-
}
|
|
1788
|
-
if (approvalNext !== void 0) {
|
|
1789
|
-
pending.push(approvalNext.then((value) => ({ source: "approval", value })));
|
|
1790
|
-
}
|
|
1791
|
-
const result = await Promise.race(pending);
|
|
1792
|
-
if (result.source === "run") {
|
|
1793
|
-
if (result.value.done === true) {
|
|
1794
|
-
runDone = true;
|
|
1795
|
-
runNext = void 0;
|
|
1796
|
-
approvalEvents.close();
|
|
1797
|
-
} else {
|
|
1798
|
-
runNext = runIterator.next();
|
|
1799
|
-
yield result.value.value;
|
|
1800
|
-
}
|
|
1801
|
-
continue;
|
|
1802
|
-
}
|
|
1803
|
-
if (result.value.done === true) {
|
|
1804
|
-
approvalNext = void 0;
|
|
1805
|
-
} else {
|
|
1806
|
-
approvalNext = approvalEvents.next();
|
|
1807
|
-
yield result.value.value;
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
} finally {
|
|
1811
|
-
if (!runDone && runIterator.return !== void 0) {
|
|
1812
|
-
await runIterator.return();
|
|
1813
|
-
}
|
|
1814
|
-
approvalEvents.close();
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
1807
|
function streamAgentRunEvents(_c, events, options) {
|
|
1818
1808
|
return streamStudioClient({
|
|
1819
1809
|
events: customAgentEventsToClientStream({
|
|
@@ -1824,52 +1814,6 @@ function streamAgentRunEvents(_c, events, options) {
|
|
|
1824
1814
|
});
|
|
1825
1815
|
}
|
|
1826
1816
|
function studioEventToClientEvent(event, context) {
|
|
1827
|
-
if (event.type === "tool_approval_request" || event.type === "tool_approval_result") {
|
|
1828
|
-
return {
|
|
1829
|
-
type: "tool_approval",
|
|
1830
|
-
runId: context.runId,
|
|
1831
|
-
...context.turn === void 0 ? {} : { turn: context.turn },
|
|
1832
|
-
...context.scope === void 0 ? {} : { scope: context.scope },
|
|
1833
|
-
approval: {
|
|
1834
|
-
id: event.approval.id,
|
|
1835
|
-
runId: event.approval.runId,
|
|
1836
|
-
agentId: event.approval.agentId,
|
|
1837
|
-
...event.approval.sessionId === void 0 ? {} : { sessionId: event.approval.sessionId },
|
|
1838
|
-
toolName: event.approval.toolName,
|
|
1839
|
-
...event.approval.callId === void 0 ? {} : { callId: event.approval.callId },
|
|
1840
|
-
internalCallId: event.approval.internalCallId,
|
|
1841
|
-
input: parseJsonOrString(event.approval.args),
|
|
1842
|
-
status: event.approval.status,
|
|
1843
|
-
requestedAt: event.approval.requestedAt,
|
|
1844
|
-
...event.approval.resolvedAt === void 0 ? {} : { resolvedAt: event.approval.resolvedAt },
|
|
1845
|
-
...event.approval.reason === void 0 ? {} : { reason: event.approval.reason }
|
|
1846
|
-
}
|
|
1847
|
-
};
|
|
1848
|
-
}
|
|
1849
|
-
if (event.type === "tool_question_request" || event.type === "tool_question_result") {
|
|
1850
|
-
return {
|
|
1851
|
-
type: "tool_question",
|
|
1852
|
-
runId: context.runId,
|
|
1853
|
-
...context.turn === void 0 ? {} : { turn: context.turn },
|
|
1854
|
-
...context.scope === void 0 ? {} : { scope: context.scope },
|
|
1855
|
-
question: {
|
|
1856
|
-
id: event.question.id,
|
|
1857
|
-
runId: event.question.runId,
|
|
1858
|
-
agentId: event.question.agentId,
|
|
1859
|
-
...event.question.sessionId === void 0 ? {} : { sessionId: event.question.sessionId },
|
|
1860
|
-
toolName: event.question.toolName,
|
|
1861
|
-
...event.question.callId === void 0 ? {} : { callId: event.question.callId },
|
|
1862
|
-
internalCallId: event.question.internalCallId,
|
|
1863
|
-
input: parseJsonOrString(event.question.args),
|
|
1864
|
-
questions: event.question.questions,
|
|
1865
|
-
status: event.question.status,
|
|
1866
|
-
requestedAt: event.question.requestedAt,
|
|
1867
|
-
...event.question.answeredAt === void 0 ? {} : { answeredAt: event.question.answeredAt },
|
|
1868
|
-
...event.question.cancelledAt === void 0 ? {} : { cancelledAt: event.question.cancelledAt },
|
|
1869
|
-
...event.question.answers === void 0 ? {} : { answers: event.question.answers }
|
|
1870
|
-
}
|
|
1871
|
-
};
|
|
1872
|
-
}
|
|
1873
1817
|
if (event.type === "session_log" && isJsonValue2(event.log)) {
|
|
1874
1818
|
return {
|
|
1875
1819
|
type: "data",
|
|
@@ -1898,14 +1842,6 @@ function studioEventToClientEvent(event, context) {
|
|
|
1898
1842
|
}
|
|
1899
1843
|
return void 0;
|
|
1900
1844
|
}
|
|
1901
|
-
function parseJsonOrString(value) {
|
|
1902
|
-
try {
|
|
1903
|
-
const parsed = JSON.parse(value);
|
|
1904
|
-
return isJsonValue2(parsed) ? parsed : value;
|
|
1905
|
-
} catch {
|
|
1906
|
-
return value;
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1909
1845
|
function withAgentRunCancellation(events, onCancel) {
|
|
1910
1846
|
const iterator = events[Symbol.asyncIterator]();
|
|
1911
1847
|
let done = false;
|
|
@@ -2014,7 +1950,7 @@ function createPersistedStreamingSessionTranscript(props) {
|
|
|
2014
1950
|
if (event.type === "final" || event.type === "error") {
|
|
2015
1951
|
assignTranscriptRunDuration(transcript, Date.now() - props.startedAt);
|
|
2016
1952
|
}
|
|
2017
|
-
const eventStatus = event.type === "final" ? "success" : event.type === "error" ? "error" : "running";
|
|
1953
|
+
const eventStatus = event.type === "final" ? event.result.status === "suspended" ? "suspended" : "success" : event.type === "error" ? "error" : "running";
|
|
2018
1954
|
const saveInput = {
|
|
2019
1955
|
id: props.session.id,
|
|
2020
1956
|
runId: props.runId,
|
|
@@ -2024,7 +1960,7 @@ function createPersistedStreamingSessionTranscript(props) {
|
|
|
2024
1960
|
};
|
|
2025
1961
|
if (event.type === "error") saveInput.error = serializeError(event.error);
|
|
2026
1962
|
await saveTranscript(saveInput);
|
|
2027
|
-
const generatedMessages = event.type === "final" ? event.result.messages.slice(1) : [];
|
|
1963
|
+
const generatedMessages = event.type === "final" ? event.result.messages.slice(props.generatedMessagesStartIndex ?? 1) : [];
|
|
2028
1964
|
if (props.persistGeneratedMessages === true && generatedMessages.length > 0) {
|
|
2029
1965
|
await props.store.append({
|
|
2030
1966
|
scope: { sessionId: props.session.id },
|
|
@@ -2172,71 +2108,63 @@ function acceptTranscriptStreamEvent(transcript, event) {
|
|
|
2172
2108
|
}
|
|
2173
2109
|
appendChildAgentTranscriptEvent(matched, event);
|
|
2174
2110
|
}
|
|
2175
|
-
if (event.type === "
|
|
2176
|
-
const
|
|
2177
|
-
transcript,
|
|
2178
|
-
event.approval.toolName,
|
|
2179
|
-
approvalCallId(event.approval)
|
|
2180
|
-
);
|
|
2181
|
-
if (matched !== void 0) {
|
|
2182
|
-
matched.approval = {
|
|
2183
|
-
id: event.approval.id,
|
|
2184
|
-
status: event.approval.status,
|
|
2185
|
-
requestedAt: event.approval.requestedAt
|
|
2186
|
-
};
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
if (event.type === "tool_approval_result") {
|
|
2111
|
+
if (event.type === "final" && event.result.status === "suspended") {
|
|
2112
|
+
const interaction = event.result.interaction;
|
|
2190
2113
|
const matched = findTranscriptToolEntry(
|
|
2191
2114
|
transcript,
|
|
2192
|
-
|
|
2193
|
-
|
|
2115
|
+
interaction.toolName,
|
|
2116
|
+
interaction.callId ?? interaction.toolCallId
|
|
2194
2117
|
);
|
|
2195
|
-
if (matched !== void 0) {
|
|
2118
|
+
if (matched !== void 0 && interaction.type === "tool-approval") {
|
|
2196
2119
|
const approval = {
|
|
2197
|
-
id:
|
|
2198
|
-
status:
|
|
2199
|
-
requestedAt:
|
|
2120
|
+
id: interaction.id,
|
|
2121
|
+
status: "pending",
|
|
2122
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2200
2123
|
};
|
|
2201
|
-
if (
|
|
2202
|
-
if (event.approval.reason !== void 0) approval.reason = event.approval.reason;
|
|
2124
|
+
if (interaction.reason !== void 0) approval.reason = interaction.reason;
|
|
2203
2125
|
matched.approval = approval;
|
|
2204
2126
|
}
|
|
2205
|
-
|
|
2206
|
-
if (event.type === "tool_question_request") {
|
|
2207
|
-
const matched = findTranscriptToolEntry(
|
|
2208
|
-
transcript,
|
|
2209
|
-
event.question.toolName,
|
|
2210
|
-
questionCallId(event.question)
|
|
2211
|
-
);
|
|
2212
|
-
if (matched !== void 0) {
|
|
2127
|
+
if (matched !== void 0 && interaction.type === "tool-question") {
|
|
2213
2128
|
matched.question = {
|
|
2214
|
-
id:
|
|
2215
|
-
status:
|
|
2216
|
-
requestedAt:
|
|
2217
|
-
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
|
+
}))
|
|
2218
2138
|
};
|
|
2219
2139
|
}
|
|
2220
2140
|
}
|
|
2221
|
-
if (event.type === "
|
|
2141
|
+
if (event.type === "interaction_response") {
|
|
2142
|
+
const response = event.response;
|
|
2222
2143
|
const matched = findTranscriptToolEntry(
|
|
2223
2144
|
transcript,
|
|
2224
|
-
|
|
2225
|
-
|
|
2145
|
+
response.toolName,
|
|
2146
|
+
response.callId ?? response.toolCallId
|
|
2226
2147
|
);
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
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
|
+
}))
|
|
2233
2167
|
};
|
|
2234
|
-
if (event.question.answeredAt !== void 0) question.answeredAt = event.question.answeredAt;
|
|
2235
|
-
if (event.question.cancelledAt !== void 0) {
|
|
2236
|
-
question.cancelledAt = event.question.cancelledAt;
|
|
2237
|
-
}
|
|
2238
|
-
if (event.question.answers !== void 0) question.answers = event.question.answers;
|
|
2239
|
-
matched.question = question;
|
|
2240
2168
|
}
|
|
2241
2169
|
}
|
|
2242
2170
|
if (event.type === "final" && event.result.trace?.traceId !== void 0) {
|
|
@@ -2246,12 +2174,6 @@ function acceptTranscriptStreamEvent(transcript, event) {
|
|
|
2246
2174
|
appendTranscriptAssistantError(transcript, errorText(event.error));
|
|
2247
2175
|
}
|
|
2248
2176
|
}
|
|
2249
|
-
function approvalCallId(approval) {
|
|
2250
|
-
return approval.callId ?? approval.toolCallId;
|
|
2251
|
-
}
|
|
2252
|
-
function questionCallId(question) {
|
|
2253
|
-
return question.callId ?? question.toolCallId;
|
|
2254
|
-
}
|
|
2255
2177
|
function appendChildAgentTranscriptEvent(entry, event) {
|
|
2256
2178
|
const childEvent = childAgentTranscriptEvent(event);
|
|
2257
2179
|
if (childEvent === void 0) {
|
|
@@ -2524,6 +2446,42 @@ async function parseRunRequest(c) {
|
|
|
2524
2446
|
return parseRunRequestOptions(c, body, request);
|
|
2525
2447
|
}
|
|
2526
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") {
|
|
2476
|
+
return {
|
|
2477
|
+
error: errorResponse(
|
|
2478
|
+
c,
|
|
2479
|
+
400,
|
|
2480
|
+
"bad_request",
|
|
2481
|
+
'type must be "messages" or "interaction_response"'
|
|
2482
|
+
)
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2527
2485
|
let messages;
|
|
2528
2486
|
try {
|
|
2529
2487
|
messages = parseMessages(body.messages);
|
|
@@ -2548,10 +2506,10 @@ function parseRunRequestBody(c, body) {
|
|
|
2548
2506
|
error: errorResponse(c, 400, "bad_request", "The final message must be user-authored")
|
|
2549
2507
|
};
|
|
2550
2508
|
}
|
|
2551
|
-
return { messages };
|
|
2509
|
+
return { type: "messages", messages };
|
|
2552
2510
|
}
|
|
2553
2511
|
function parseRunRequestOptions(c, body, request) {
|
|
2554
|
-
if ("sessionId" in body) {
|
|
2512
|
+
if (request.type === "messages" && "sessionId" in body) {
|
|
2555
2513
|
if (typeof body.sessionId !== "string" || body.sessionId.trim().length === 0) {
|
|
2556
2514
|
return { error: errorResponse(c, 400, "bad_request", "sessionId must be a string") };
|
|
2557
2515
|
}
|
|
@@ -2568,7 +2526,7 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2568
2526
|
}
|
|
2569
2527
|
request.stream = body.stream;
|
|
2570
2528
|
}
|
|
2571
|
-
if ("maxTurns" in body) {
|
|
2529
|
+
if (request.type === "messages" && "maxTurns" in body) {
|
|
2572
2530
|
if (!isNonNegativeInteger(body.maxTurns)) {
|
|
2573
2531
|
return {
|
|
2574
2532
|
error: errorResponse(c, 400, "bad_request", "maxTurns must be a non-negative integer")
|
|
@@ -2576,7 +2534,7 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2576
2534
|
}
|
|
2577
2535
|
request.maxTurns = body.maxTurns;
|
|
2578
2536
|
}
|
|
2579
|
-
if ("toolConcurrency" in body) {
|
|
2537
|
+
if (request.type === "messages" && "toolConcurrency" in body) {
|
|
2580
2538
|
if (!isPositiveInteger(body.toolConcurrency)) {
|
|
2581
2539
|
return {
|
|
2582
2540
|
error: errorResponse(c, 400, "bad_request", "toolConcurrency must be a positive integer")
|
|
@@ -2584,7 +2542,7 @@ function parseRunRequestOptions(c, body, request) {
|
|
|
2584
2542
|
}
|
|
2585
2543
|
request.toolConcurrency = body.toolConcurrency;
|
|
2586
2544
|
}
|
|
2587
|
-
if ("model" in body) {
|
|
2545
|
+
if (request.type === "messages" && "model" in body) {
|
|
2588
2546
|
if (isObject(body.model) && typeof body.model.providerId === "string" && typeof body.model.modelId === "string") {
|
|
2589
2547
|
request.model = {
|
|
2590
2548
|
providerId: body.model.providerId,
|
|
@@ -2877,102 +2835,78 @@ function logsFromStreamEvent(props) {
|
|
|
2877
2835
|
}
|
|
2878
2836
|
if (event.type === "final") {
|
|
2879
2837
|
const result = event.result;
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
runId,
|
|
2884
|
-
durationMs: Date.now() - props.startedAt,
|
|
2885
|
-
usage: result.usage,
|
|
2886
|
-
output: result.text,
|
|
2887
|
-
messageCount: result.messages.length
|
|
2888
|
-
}),
|
|
2889
|
-
memorySavedLog({ sessionId, runId, messageCount: result.messages.length })
|
|
2890
|
-
];
|
|
2891
|
-
}
|
|
2892
|
-
if (event.type === "error") {
|
|
2893
|
-
return [runFailedLog(sessionId, runId, event.error, props.startedAt)];
|
|
2894
|
-
}
|
|
2895
|
-
if (event.type === "tool_approval_request") {
|
|
2896
|
-
const metadata = {
|
|
2897
|
-
approvalId: event.approval.id,
|
|
2898
|
-
toolName: event.approval.toolName,
|
|
2899
|
-
status: event.approval.status,
|
|
2900
|
-
hasReason: event.approval.reason !== void 0,
|
|
2901
|
-
argumentBytes: byteLength2(event.approval.args)
|
|
2902
|
-
};
|
|
2903
|
-
if (event.approval.callId !== void 0) metadata.callId = event.approval.callId;
|
|
2904
|
-
return [
|
|
2905
|
-
{
|
|
2838
|
+
const logs = [];
|
|
2839
|
+
if (result.status === "suspended") {
|
|
2840
|
+
logs.push({
|
|
2906
2841
|
sessionId,
|
|
2907
2842
|
runId,
|
|
2908
2843
|
level: "info",
|
|
2909
|
-
category: "
|
|
2910
|
-
event: "
|
|
2911
|
-
message: `
|
|
2912
|
-
metadata
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
}
|
|
2934
|
-
];
|
|
2935
|
-
}
|
|
2936
|
-
if (event.type === "tool_question_request") {
|
|
2937
|
-
const metadata = {
|
|
2938
|
-
questionId: event.question.id,
|
|
2939
|
-
toolName: event.question.toolName,
|
|
2940
|
-
status: event.question.status,
|
|
2941
|
-
questionCount: event.question.questions.length,
|
|
2942
|
-
argumentBytes: byteLength2(event.question.args)
|
|
2943
|
-
};
|
|
2944
|
-
if (event.question.callId !== void 0) metadata.callId = event.question.callId;
|
|
2945
|
-
return [
|
|
2946
|
-
{
|
|
2844
|
+
category: "run",
|
|
2845
|
+
event: "run.suspended",
|
|
2846
|
+
message: `Run suspended for ${result.interaction.type}`,
|
|
2847
|
+
metadata: {
|
|
2848
|
+
interactionId: result.interaction.id,
|
|
2849
|
+
interactionType: result.interaction.type,
|
|
2850
|
+
toolName: result.interaction.toolName,
|
|
2851
|
+
sourceRunId: result.continuation.sourceRunId
|
|
2852
|
+
}
|
|
2853
|
+
});
|
|
2854
|
+
} else {
|
|
2855
|
+
logs.push(
|
|
2856
|
+
runCompletedLog({
|
|
2857
|
+
sessionId,
|
|
2858
|
+
runId,
|
|
2859
|
+
durationMs: Date.now() - props.startedAt,
|
|
2860
|
+
usage: result.usage,
|
|
2861
|
+
output: result.text,
|
|
2862
|
+
messageCount: result.messages.length
|
|
2863
|
+
})
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
if (result.resumedFrom !== void 0) {
|
|
2867
|
+
logs.push({
|
|
2947
2868
|
sessionId,
|
|
2948
2869
|
runId,
|
|
2949
2870
|
level: "info",
|
|
2950
|
-
category: "
|
|
2951
|
-
event: "
|
|
2952
|
-
message:
|
|
2953
|
-
metadata
|
|
2954
|
-
|
|
2955
|
-
|
|
2871
|
+
category: "run",
|
|
2872
|
+
event: "interaction.resumed",
|
|
2873
|
+
message: "Interaction resumed in a linked run",
|
|
2874
|
+
metadata: {
|
|
2875
|
+
interactionId: result.resumedFrom.interactionId,
|
|
2876
|
+
sourceRunId: result.resumedFrom.runId,
|
|
2877
|
+
resumedRunId: result.runId
|
|
2878
|
+
}
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
logs.push(memorySavedLog({ sessionId, runId, messageCount: result.messages.length }));
|
|
2882
|
+
return logs;
|
|
2883
|
+
}
|
|
2884
|
+
if (event.type === "error") {
|
|
2885
|
+
return [runFailedLog(sessionId, runId, event.error, props.startedAt)];
|
|
2956
2886
|
}
|
|
2957
|
-
if (event.type === "
|
|
2958
|
-
const
|
|
2887
|
+
if (event.type === "interaction_response") {
|
|
2888
|
+
const response = event.response;
|
|
2959
2889
|
const metadata = {
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
argumentBytes: byteLength2(event.question.args),
|
|
2965
|
-
answerCount: event.question.answers?.length ?? 0
|
|
2890
|
+
interactionId: response.interactionId,
|
|
2891
|
+
interactionType: response.type === "tool-approval-response" ? "tool-approval" : "tool-question",
|
|
2892
|
+
toolName: response.toolName,
|
|
2893
|
+
sourceRunId: event.sourceRunId
|
|
2966
2894
|
};
|
|
2967
|
-
if (
|
|
2895
|
+
if (response.callId !== void 0) metadata.callId = response.callId;
|
|
2896
|
+
if (response.type === "tool-approval-response") {
|
|
2897
|
+
metadata.approved = response.approved;
|
|
2898
|
+
metadata.hasReason = response.reason !== void 0;
|
|
2899
|
+
} else {
|
|
2900
|
+
metadata.answerCount = response.answers.length;
|
|
2901
|
+
}
|
|
2968
2902
|
return [
|
|
2969
2903
|
{
|
|
2970
2904
|
sessionId,
|
|
2971
2905
|
runId,
|
|
2972
2906
|
level: "info",
|
|
2973
|
-
category: "
|
|
2974
|
-
event:
|
|
2975
|
-
message:
|
|
2907
|
+
category: "run",
|
|
2908
|
+
event: "interaction.responded",
|
|
2909
|
+
message: `Interaction response received for ${response.toolName}`,
|
|
2976
2910
|
metadata
|
|
2977
2911
|
}
|
|
2978
2912
|
];
|
|
@@ -3167,6 +3101,56 @@ async function prepareAgentRun(c, props) {
|
|
|
3167
3101
|
if ("error" in body) {
|
|
3168
3102
|
return body.error;
|
|
3169
3103
|
}
|
|
3104
|
+
if (body.type === "interaction_response") {
|
|
3105
|
+
const registration = props.continuationRegistry.take(
|
|
3106
|
+
body.interactionId,
|
|
3107
|
+
agentId,
|
|
3108
|
+
body.response
|
|
3109
|
+
);
|
|
3110
|
+
if (registration.status === "missing") {
|
|
3111
|
+
return errorResponse(c, 404, "not_found", "Interaction continuation is unavailable");
|
|
3112
|
+
}
|
|
3113
|
+
if (registration.status === "claimed") {
|
|
3114
|
+
return errorResponse(c, 409, "conflict", "Interaction response was already claimed");
|
|
3115
|
+
}
|
|
3116
|
+
if (registration.status === "invalid") {
|
|
3117
|
+
return errorResponse(c, 400, "bad_request", registration.error.message);
|
|
3118
|
+
}
|
|
3119
|
+
const { registration: claimed } = registration;
|
|
3120
|
+
const source = claimed.context;
|
|
3121
|
+
const runId2 = globalThis.crypto.randomUUID();
|
|
3122
|
+
let resumedBody = {
|
|
3123
|
+
...source.body
|
|
3124
|
+
};
|
|
3125
|
+
if (body.stream !== void 0) resumedBody = { ...resumedBody, stream: body.stream };
|
|
3126
|
+
if (body.metadata !== void 0) resumedBody = { ...resumedBody, metadata: body.metadata };
|
|
3127
|
+
if (body.trace !== void 0) resumedBody = { ...resumedBody, trace: body.trace };
|
|
3128
|
+
const runOptions = createRunOptions(resumedBody, agentId, source.session, c.req.raw.signal);
|
|
3129
|
+
const execution2 = {
|
|
3130
|
+
generate: (options) => source.runAgent.generate({
|
|
3131
|
+
continuation: claimed.continuation,
|
|
3132
|
+
response: body.response,
|
|
3133
|
+
...options
|
|
3134
|
+
}),
|
|
3135
|
+
stream: (options) => source.runAgent.stream({
|
|
3136
|
+
continuation: claimed.continuation,
|
|
3137
|
+
response: body.response,
|
|
3138
|
+
...options
|
|
3139
|
+
})
|
|
3140
|
+
};
|
|
3141
|
+
return {
|
|
3142
|
+
...source,
|
|
3143
|
+
body: resumedBody,
|
|
3144
|
+
execution: execution2,
|
|
3145
|
+
failureMessages: void 0,
|
|
3146
|
+
memoryCompactionLogged: false,
|
|
3147
|
+
options: runOptions,
|
|
3148
|
+
runId: runId2,
|
|
3149
|
+
runStartedAt: Date.now(),
|
|
3150
|
+
generatedMessagesStartIndex: 0,
|
|
3151
|
+
shouldPersistSessionMessages: source.shouldPersistSessionMessages
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3170
3154
|
const session = await resolveRunSession(c, body, agentId, props.stores);
|
|
3171
3155
|
if (session instanceof Response) {
|
|
3172
3156
|
return session;
|
|
@@ -3224,6 +3208,7 @@ async function prepareAgentRun(c, props) {
|
|
|
3224
3208
|
runAgent,
|
|
3225
3209
|
runId,
|
|
3226
3210
|
runStartedAt,
|
|
3211
|
+
generatedMessagesStartIndex: 1,
|
|
3227
3212
|
selectedModel,
|
|
3228
3213
|
session,
|
|
3229
3214
|
sessionStore,
|
|
@@ -3336,8 +3321,10 @@ function createRunExecution(props) {
|
|
|
3336
3321
|
}
|
|
3337
3322
|
function createRunOptions(body, agentId, session, abortSignal) {
|
|
3338
3323
|
const options = { abortSignal };
|
|
3339
|
-
if (body.maxTurns !== void 0) options.maxTurns = body.maxTurns;
|
|
3340
|
-
if (body.toolConcurrency !== void 0)
|
|
3324
|
+
if (body.type === "messages" && body.maxTurns !== void 0) options.maxTurns = body.maxTurns;
|
|
3325
|
+
if (body.type === "messages" && body.toolConcurrency !== void 0) {
|
|
3326
|
+
options.toolConcurrency = body.toolConcurrency;
|
|
3327
|
+
}
|
|
3341
3328
|
if (body.trace !== void 0) {
|
|
3342
3329
|
options.trace = traceForRun(body.trace, agentId, session);
|
|
3343
3330
|
} else if (session !== void 0) {
|
|
@@ -3346,34 +3333,11 @@ function createRunOptions(body, agentId, session, abortSignal) {
|
|
|
3346
3333
|
return options;
|
|
3347
3334
|
}
|
|
3348
3335
|
function handleStreamingAgentRun(c, run, props) {
|
|
3349
|
-
const
|
|
3350
|
-
const
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
};
|
|
3355
|
-
if (run.session !== void 0) approvalContext.sessionId = run.session.id;
|
|
3356
|
-
if (run.body.metadata !== void 0) approvalContext.metadata = run.body.metadata;
|
|
3357
|
-
const questionContext = {
|
|
3358
|
-
runId: run.runId,
|
|
3359
|
-
agentId: run.agentId,
|
|
3360
|
-
emit: (event) => runtimeEvents.push(event)
|
|
3361
|
-
};
|
|
3362
|
-
if (run.session !== void 0) questionContext.sessionId = run.session.id;
|
|
3363
|
-
if (run.body.metadata !== void 0) questionContext.metadata = run.body.metadata;
|
|
3364
|
-
const effectiveHook = props.questionRuntime.createHook(questionContext);
|
|
3365
|
-
const streamOptions = withInternalAgentRunOptions(
|
|
3366
|
-
{ ...run.options },
|
|
3367
|
-
{ hook: effectiveHook, runId: run.runId }
|
|
3368
|
-
);
|
|
3369
|
-
const runStream = mergeRunAndApprovalEvents(
|
|
3370
|
-
resumeStreamingApprovals(
|
|
3371
|
-
run.runAgent,
|
|
3372
|
-
run.execution.stream(streamOptions),
|
|
3373
|
-
props.approvalRuntime,
|
|
3374
|
-
approvalContext
|
|
3375
|
-
),
|
|
3376
|
-
runtimeEvents
|
|
3336
|
+
const streamOptions = withInternalAgentRunOptions({ ...run.options }, { runId: run.runId });
|
|
3337
|
+
const runStream = registerStreamingContinuation(
|
|
3338
|
+
run.execution.stream(streamOptions),
|
|
3339
|
+
props.continuationRegistry,
|
|
3340
|
+
run
|
|
3377
3341
|
);
|
|
3378
3342
|
let stream = runStream;
|
|
3379
3343
|
let persistedRun;
|
|
@@ -3391,15 +3355,14 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3391
3355
|
message: run.body.messages.at(-1),
|
|
3392
3356
|
runId: run.runId,
|
|
3393
3357
|
startedAt: run.runStartedAt,
|
|
3394
|
-
persistGeneratedMessages: run.shouldPersistSessionMessages
|
|
3358
|
+
persistGeneratedMessages: run.shouldPersistSessionMessages,
|
|
3359
|
+
generatedMessagesStartIndex: run.generatedMessagesStartIndex
|
|
3395
3360
|
});
|
|
3396
3361
|
stream = persistedRun.events;
|
|
3397
3362
|
}
|
|
3398
3363
|
return streamAgentRunEvents(c, stream, {
|
|
3399
3364
|
runId: run.runId,
|
|
3400
3365
|
onCancel: async () => {
|
|
3401
|
-
props.approvalRuntime.cancelRun(run.runId);
|
|
3402
|
-
props.questionRuntime.cancelRun(run.runId);
|
|
3403
3366
|
const persistence = [];
|
|
3404
3367
|
if (persistedRun !== void 0) {
|
|
3405
3368
|
persistence.push(persistedRun.cancel());
|
|
@@ -3418,35 +3381,29 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3418
3381
|
}
|
|
3419
3382
|
async function handleBufferedAgentRun(c, run, props) {
|
|
3420
3383
|
try {
|
|
3421
|
-
const runtimeOptions = await startBufferedSessionRun(run
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
const response = result;
|
|
3429
|
-
await completeBufferedSessionRun(run, response);
|
|
3430
|
-
return c.json(response);
|
|
3384
|
+
const runtimeOptions = await startBufferedSessionRun(run);
|
|
3385
|
+
const result = await run.execution.generate(runtimeOptions);
|
|
3386
|
+
if (result.status === "suspended") {
|
|
3387
|
+
props.continuationRegistry.register({ continuation: result.continuation, context: run });
|
|
3388
|
+
}
|
|
3389
|
+
await completeBufferedSessionRun(run, result);
|
|
3390
|
+
return c.json(agentRunResponse(result));
|
|
3431
3391
|
} catch (error) {
|
|
3432
3392
|
await failBufferedSessionRun(run, error);
|
|
3433
3393
|
return errorResponse(c, 500, "internal_error", "Agent run failed", serializeError(error));
|
|
3434
3394
|
}
|
|
3435
3395
|
}
|
|
3436
|
-
|
|
3396
|
+
function agentRunResponse(result) {
|
|
3397
|
+
if (result.status !== "suspended") return result;
|
|
3398
|
+
const { continuation: _continuation, messages: _messages, ...response } = result;
|
|
3399
|
+
return response;
|
|
3400
|
+
}
|
|
3401
|
+
async function startBufferedSessionRun(run) {
|
|
3437
3402
|
if (run.session !== void 0) {
|
|
3438
3403
|
await appendSessionLog(run.sessionStore, runStartedLog(run.session, run.runId));
|
|
3439
3404
|
await appendSessionLog(run.sessionStore, memoryLoadedLog(run.session, run.runId));
|
|
3440
3405
|
}
|
|
3441
|
-
const questionContext = {
|
|
3442
|
-
runId: run.runId,
|
|
3443
|
-
agentId: run.agentId
|
|
3444
|
-
};
|
|
3445
|
-
if (run.session !== void 0) questionContext.sessionId = run.session.id;
|
|
3446
|
-
if (run.body.metadata !== void 0) questionContext.metadata = run.body.metadata;
|
|
3447
|
-
const effectiveHook = props.questionRuntime.createHook(questionContext);
|
|
3448
3406
|
const internalOptions = {
|
|
3449
|
-
hook: effectiveHook,
|
|
3450
3407
|
onFailure: ({ messages }) => {
|
|
3451
3408
|
run.failureMessages = structuredClone(messages);
|
|
3452
3409
|
},
|
|
@@ -3469,30 +3426,12 @@ async function startBufferedSessionRun(run, props) {
|
|
|
3469
3426
|
}
|
|
3470
3427
|
return withInternalAgentRunOptions({ ...run.options }, internalOptions);
|
|
3471
3428
|
}
|
|
3472
|
-
function
|
|
3473
|
-
const
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
};
|
|
3477
|
-
if (run.session !== void 0) context.sessionId = run.session.id;
|
|
3478
|
-
if (run.body.metadata !== void 0) context.metadata = run.body.metadata;
|
|
3479
|
-
if (emit !== void 0) context.emit = emit;
|
|
3480
|
-
return context;
|
|
3481
|
-
}
|
|
3482
|
-
async function* resumeStreamingApprovals(agent, initialStream, approvalRuntime, context) {
|
|
3483
|
-
let stream = initialStream;
|
|
3484
|
-
while (true) {
|
|
3485
|
-
let pending;
|
|
3486
|
-
for await (const event of stream) {
|
|
3487
|
-
if (event.type === "approval_required") {
|
|
3488
|
-
pending = event;
|
|
3489
|
-
break;
|
|
3490
|
-
}
|
|
3491
|
-
yield event;
|
|
3429
|
+
async function* registerStreamingContinuation(stream, registry, run) {
|
|
3430
|
+
for await (const event of stream) {
|
|
3431
|
+
if (event.type === "final" && event.result.status === "suspended") {
|
|
3432
|
+
registry.register({ continuation: event.result.continuation, context: run });
|
|
3492
3433
|
}
|
|
3493
|
-
|
|
3494
|
-
const decision = await approvalRuntime.request(context, pending.approval);
|
|
3495
|
-
stream = agent.resume(pending, decision);
|
|
3434
|
+
yield event;
|
|
3496
3435
|
}
|
|
3497
3436
|
}
|
|
3498
3437
|
async function completeBufferedSessionRun(run, response) {
|
|
@@ -3500,7 +3439,7 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3500
3439
|
return;
|
|
3501
3440
|
}
|
|
3502
3441
|
if (run.shouldPersistSessionMessages) {
|
|
3503
|
-
const generatedMessages = response.messages.slice(
|
|
3442
|
+
const generatedMessages = response.messages.slice(run.generatedMessagesStartIndex);
|
|
3504
3443
|
if (generatedMessages.length > 0) {
|
|
3505
3444
|
await run.sessionStore.append({
|
|
3506
3445
|
scope: { sessionId: run.session.id, metadata: run.memoryMetadata },
|
|
@@ -3518,7 +3457,7 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3518
3457
|
runId: run.runId,
|
|
3519
3458
|
...optionalTitle(run.body.messages.at(-1)),
|
|
3520
3459
|
transcript,
|
|
3521
|
-
status: "success"
|
|
3460
|
+
status: response.status === "suspended" ? "suspended" : "success"
|
|
3522
3461
|
});
|
|
3523
3462
|
if (response.memoryCompaction !== void 0 && !run.memoryCompactionLogged) {
|
|
3524
3463
|
await appendSessionLog(
|
|
@@ -3533,7 +3472,20 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3533
3472
|
}
|
|
3534
3473
|
await appendSessionLog(
|
|
3535
3474
|
run.sessionStore,
|
|
3536
|
-
|
|
3475
|
+
response.status === "suspended" ? {
|
|
3476
|
+
sessionId: run.session.id,
|
|
3477
|
+
runId: run.runId,
|
|
3478
|
+
level: "info",
|
|
3479
|
+
category: "run",
|
|
3480
|
+
event: "run.suspended",
|
|
3481
|
+
message: `Run suspended for ${response.interaction.type}`,
|
|
3482
|
+
metadata: {
|
|
3483
|
+
interactionId: response.interaction.id,
|
|
3484
|
+
interactionType: response.interaction.type,
|
|
3485
|
+
toolName: response.interaction.toolName,
|
|
3486
|
+
sourceRunId: response.continuation.sourceRunId
|
|
3487
|
+
}
|
|
3488
|
+
} : runCompletedLog({
|
|
3537
3489
|
sessionId: run.session.id,
|
|
3538
3490
|
runId: run.runId,
|
|
3539
3491
|
durationMs: durationMs2,
|
|
@@ -3542,6 +3494,46 @@ async function completeBufferedSessionRun(run, response) {
|
|
|
3542
3494
|
messageCount: response.messages.length
|
|
3543
3495
|
})
|
|
3544
3496
|
);
|
|
3497
|
+
if (response.resumedFrom !== void 0) {
|
|
3498
|
+
const responseMessage = response.messages[0];
|
|
3499
|
+
const responsePart = responseMessage?.role === "tool" ? responseMessage.content[0] : void 0;
|
|
3500
|
+
if (responsePart !== void 0 && responsePart.type !== "tool-result") {
|
|
3501
|
+
const metadata = {
|
|
3502
|
+
interactionId: responsePart.interactionId,
|
|
3503
|
+
interactionType: responsePart.type === "tool-approval-response" ? "tool-approval" : "tool-question",
|
|
3504
|
+
toolName: responsePart.toolName,
|
|
3505
|
+
sourceRunId: response.resumedFrom.runId
|
|
3506
|
+
};
|
|
3507
|
+
if (responsePart.type === "tool-approval-response") {
|
|
3508
|
+
metadata.approved = responsePart.approved;
|
|
3509
|
+
metadata.hasReason = responsePart.reason !== void 0;
|
|
3510
|
+
} else {
|
|
3511
|
+
metadata.answerCount = responsePart.answers.length;
|
|
3512
|
+
}
|
|
3513
|
+
await appendSessionLog(run.sessionStore, {
|
|
3514
|
+
sessionId: run.session.id,
|
|
3515
|
+
runId: run.runId,
|
|
3516
|
+
level: "info",
|
|
3517
|
+
category: "run",
|
|
3518
|
+
event: "interaction.responded",
|
|
3519
|
+
message: `Interaction response received for ${responsePart.toolName}`,
|
|
3520
|
+
metadata
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
await appendSessionLog(run.sessionStore, {
|
|
3524
|
+
sessionId: run.session.id,
|
|
3525
|
+
runId: run.runId,
|
|
3526
|
+
level: "info",
|
|
3527
|
+
category: "run",
|
|
3528
|
+
event: "interaction.resumed",
|
|
3529
|
+
message: "Interaction resumed in a linked run",
|
|
3530
|
+
metadata: {
|
|
3531
|
+
interactionId: response.resumedFrom.interactionId,
|
|
3532
|
+
sourceRunId: response.resumedFrom.runId,
|
|
3533
|
+
resumedRunId: response.runId
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3545
3537
|
await appendSessionLog(
|
|
3546
3538
|
run.sessionStore,
|
|
3547
3539
|
memorySavedLog({
|
|
@@ -3577,286 +3569,48 @@ function usesStoreAsAgentMemory(agent, store) {
|
|
|
3577
3569
|
return memoryStore !== void 0 && rawObservedStore(memoryStore) === rawObservedStore(store);
|
|
3578
3570
|
}
|
|
3579
3571
|
|
|
3580
|
-
// src/runtime/
|
|
3581
|
-
import {
|
|
3572
|
+
// src/runtime/config.ts
|
|
3573
|
+
import { isQuestionTool } from "@anvia/core/tool";
|
|
3582
3574
|
|
|
3583
|
-
// src/runtime/
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3575
|
+
// src/runtime/eval-config.ts
|
|
3576
|
+
var CASE_PREVIEW_LIMIT = 5;
|
|
3577
|
+
var PREVIEW_VALUE_MAX_LENGTH = 500;
|
|
3578
|
+
function evalConfig(suite) {
|
|
3579
|
+
const casePreviews = suite.cases.slice(0, CASE_PREVIEW_LIMIT).map(casePreview);
|
|
3580
|
+
const metricSummaries = suite.metrics.map(metricSummary);
|
|
3581
|
+
const config = {
|
|
3582
|
+
id: suite.id ?? suite.name,
|
|
3583
|
+
name: suite.name,
|
|
3584
|
+
caseCount: suite.cases.length,
|
|
3585
|
+
metricNames: suite.metrics.map((metric) => metric.name),
|
|
3586
|
+
casePreviewCount: casePreviews.length,
|
|
3587
|
+
casePreviews,
|
|
3588
|
+
metricSummaries
|
|
3589
|
+
};
|
|
3590
|
+
if (suite.description !== void 0) config.description = suite.description;
|
|
3591
|
+
if (suite.concurrency !== void 0) config.concurrency = suite.concurrency;
|
|
3592
|
+
if (suite.metadata !== void 0) config.metadata = suite.metadata;
|
|
3593
|
+
return config;
|
|
3587
3594
|
}
|
|
3588
|
-
function
|
|
3589
|
-
if (
|
|
3590
|
-
return
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
return void 0;
|
|
3595
|
+
function casePreview(testCase, index) {
|
|
3596
|
+
if (!isRecord2(testCase)) {
|
|
3597
|
+
return {
|
|
3598
|
+
id: `case-${index + 1}`,
|
|
3599
|
+
input: previewValue(testCase)
|
|
3600
|
+
};
|
|
3595
3601
|
}
|
|
3596
|
-
|
|
3602
|
+
const preview = {
|
|
3603
|
+
id: typeof testCase.id === "string" && testCase.id.length > 0 ? testCase.id : `case-${index + 1}`
|
|
3604
|
+
};
|
|
3605
|
+
if ("input" in testCase) preview.input = previewValue(testCase.input);
|
|
3606
|
+
if ("expected" in testCase) preview.expected = previewValue(testCase.expected);
|
|
3607
|
+
const keys = metadataKeys(testCase.metadata);
|
|
3608
|
+
if (keys !== void 0) preview.metadataKeys = keys;
|
|
3609
|
+
return preview;
|
|
3597
3610
|
}
|
|
3598
|
-
function
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
return void 0;
|
|
3602
|
-
}
|
|
3603
|
-
return status === "running" || status === "success" || status === "error" ? status : false;
|
|
3604
|
-
}
|
|
3605
|
-
function parseAfter(value) {
|
|
3606
|
-
if (value === void 0 || value.trim().length === 0) {
|
|
3607
|
-
return void 0;
|
|
3608
|
-
}
|
|
3609
|
-
const after = Number(value);
|
|
3610
|
-
if (!Number.isInteger(after) || after < 0) {
|
|
3611
|
-
return false;
|
|
3612
|
-
}
|
|
3613
|
-
return after;
|
|
3614
|
-
}
|
|
3615
|
-
|
|
3616
|
-
// src/runtime/approvals.ts
|
|
3617
|
-
function registerApprovalRoutes(app, approvals) {
|
|
3618
|
-
app.get("/approvals", (c) => {
|
|
3619
|
-
const status = parseApprovalStatus(c.req.query("status"));
|
|
3620
|
-
if (status === false) {
|
|
3621
|
-
return errorResponse(c, 400, "bad_request", "status must be pending or resolved");
|
|
3622
|
-
}
|
|
3623
|
-
const options = {};
|
|
3624
|
-
const runId = optionalQueryString(c.req.query("runId"));
|
|
3625
|
-
const agentId = optionalQueryString(c.req.query("agentId"));
|
|
3626
|
-
const sessionId = optionalQueryString(c.req.query("sessionId"));
|
|
3627
|
-
if (status !== void 0) {
|
|
3628
|
-
options.status = status;
|
|
3629
|
-
}
|
|
3630
|
-
if (runId !== void 0) {
|
|
3631
|
-
options.runId = runId;
|
|
3632
|
-
}
|
|
3633
|
-
if (agentId !== void 0) {
|
|
3634
|
-
options.agentId = agentId;
|
|
3635
|
-
}
|
|
3636
|
-
if (sessionId !== void 0) {
|
|
3637
|
-
options.sessionId = sessionId;
|
|
3638
|
-
}
|
|
3639
|
-
return c.json({
|
|
3640
|
-
approvals: approvals.list(options)
|
|
3641
|
-
});
|
|
3642
|
-
});
|
|
3643
|
-
app.post("/approvals/:approvalId/decision", async (c) => {
|
|
3644
|
-
const body = await parseApprovalDecisionRequest(c);
|
|
3645
|
-
if ("error" in body) {
|
|
3646
|
-
return body.error;
|
|
3647
|
-
}
|
|
3648
|
-
const result = approvals.decide(c.req.param("approvalId"), body);
|
|
3649
|
-
if (result === "missing") {
|
|
3650
|
-
return errorResponse(c, 404, "not_found", "Approval not found");
|
|
3651
|
-
}
|
|
3652
|
-
if (result === "resolved") {
|
|
3653
|
-
return errorResponse(c, 409, "conflict", "Approval is already resolved");
|
|
3654
|
-
}
|
|
3655
|
-
return c.json(result);
|
|
3656
|
-
});
|
|
3657
|
-
}
|
|
3658
|
-
function parseApprovalStatus(value) {
|
|
3659
|
-
const status = optionalQueryString(value);
|
|
3660
|
-
if (status === void 0) {
|
|
3661
|
-
return void 0;
|
|
3662
|
-
}
|
|
3663
|
-
return status === "pending" || status === "resolved" ? status : false;
|
|
3664
|
-
}
|
|
3665
|
-
async function parseApprovalDecisionRequest(c) {
|
|
3666
|
-
let body;
|
|
3667
|
-
try {
|
|
3668
|
-
body = await c.req.json();
|
|
3669
|
-
} catch {
|
|
3670
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
|
|
3671
|
-
}
|
|
3672
|
-
if (!isObject(body)) {
|
|
3673
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
3674
|
-
}
|
|
3675
|
-
if (typeof body.approved !== "boolean") {
|
|
3676
|
-
return { error: errorResponse(c, 400, "bad_request", "approved must be a boolean") };
|
|
3677
|
-
}
|
|
3678
|
-
if ("reason" in body && typeof body.reason !== "string") {
|
|
3679
|
-
return { error: errorResponse(c, 400, "bad_request", "reason must be a string") };
|
|
3680
|
-
}
|
|
3681
|
-
const reason = typeof body.reason === "string" && body.reason.trim().length > 0 ? body.reason.trim() : void 0;
|
|
3682
|
-
return approvalDecision(body.approved, reason);
|
|
3683
|
-
}
|
|
3684
|
-
function createApprovalRuntime() {
|
|
3685
|
-
const approvals = /* @__PURE__ */ new Map();
|
|
3686
|
-
return {
|
|
3687
|
-
approvals,
|
|
3688
|
-
async request(context, approval) {
|
|
3689
|
-
const decision = await requestApproval(approvals, context, approval);
|
|
3690
|
-
return decision.approved ? { approved: true, ...decision.reason === void 0 ? {} : { reason: decision.reason } } : {
|
|
3691
|
-
approved: false,
|
|
3692
|
-
...decision.reason === void 0 ? {} : { reason: decision.reason }
|
|
3693
|
-
};
|
|
3694
|
-
},
|
|
3695
|
-
list(options) {
|
|
3696
|
-
return [...approvals.values()].filter((approval) => {
|
|
3697
|
-
if (options.status === "pending" && approval.status !== "pending") {
|
|
3698
|
-
return false;
|
|
3699
|
-
}
|
|
3700
|
-
if (options.status === "resolved" && approval.status === "pending") {
|
|
3701
|
-
return false;
|
|
3702
|
-
}
|
|
3703
|
-
if (options.runId !== void 0 && approval.runId !== options.runId) {
|
|
3704
|
-
return false;
|
|
3705
|
-
}
|
|
3706
|
-
if (options.agentId !== void 0 && approval.agentId !== options.agentId) {
|
|
3707
|
-
return false;
|
|
3708
|
-
}
|
|
3709
|
-
if (options.sessionId !== void 0 && approval.sessionId !== options.sessionId) {
|
|
3710
|
-
return false;
|
|
3711
|
-
}
|
|
3712
|
-
return true;
|
|
3713
|
-
}).map(publicApproval);
|
|
3714
|
-
},
|
|
3715
|
-
cancelRun(runId) {
|
|
3716
|
-
const cancelled = [];
|
|
3717
|
-
for (const approval of approvals.values()) {
|
|
3718
|
-
if (!isPendingApproval(approval) || approval.runId !== runId) {
|
|
3719
|
-
continue;
|
|
3720
|
-
}
|
|
3721
|
-
const resolved = resolveApproval(approval, "cancelled", {
|
|
3722
|
-
reason: "Run cancelled in Anvia Studio."
|
|
3723
|
-
});
|
|
3724
|
-
approvals.set(approval.id, resolved);
|
|
3725
|
-
approval.emit?.({ type: "tool_approval_result", approval: resolved });
|
|
3726
|
-
approval.resolve(approvalDecision(false, resolved.reason));
|
|
3727
|
-
cancelled.push(resolved);
|
|
3728
|
-
}
|
|
3729
|
-
return cancelled;
|
|
3730
|
-
},
|
|
3731
|
-
decide(id, decision) {
|
|
3732
|
-
const approval = approvals.get(id);
|
|
3733
|
-
if (approval === void 0) {
|
|
3734
|
-
return "missing";
|
|
3735
|
-
}
|
|
3736
|
-
if (!isPendingApproval(approval)) {
|
|
3737
|
-
return "resolved";
|
|
3738
|
-
}
|
|
3739
|
-
const reason = decision.approved ? decision.reason : decision.reason ?? approval.rejectMessage ?? "Rejected in Anvia Studio.";
|
|
3740
|
-
const resolved = resolveApproval(
|
|
3741
|
-
approval,
|
|
3742
|
-
decision.approved ? "approved" : "rejected",
|
|
3743
|
-
reason === void 0 ? {} : { reason }
|
|
3744
|
-
);
|
|
3745
|
-
approvals.set(id, resolved);
|
|
3746
|
-
approval.emit?.({ type: "tool_approval_result", approval: resolved });
|
|
3747
|
-
approval.resolve(approvalDecision(decision.approved, reason));
|
|
3748
|
-
return publicApproval(resolved);
|
|
3749
|
-
}
|
|
3750
|
-
};
|
|
3751
|
-
}
|
|
3752
|
-
async function requestApproval(approvals, context, request) {
|
|
3753
|
-
const id = request.id;
|
|
3754
|
-
const details = getAgentApprovalRequestDetails(request);
|
|
3755
|
-
const approval = {
|
|
3756
|
-
id,
|
|
3757
|
-
runId: context.runId,
|
|
3758
|
-
agentId: context.agentId,
|
|
3759
|
-
toolName: request.toolName,
|
|
3760
|
-
internalCallId: details?.internalCallId ?? request.id,
|
|
3761
|
-
args: details?.rawArgs ?? JSON.stringify(request.input) ?? "null",
|
|
3762
|
-
status: "pending",
|
|
3763
|
-
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3764
|
-
resolve: () => {
|
|
3765
|
-
}
|
|
3766
|
-
};
|
|
3767
|
-
if (context.sessionId !== void 0) approval.sessionId = context.sessionId;
|
|
3768
|
-
if (request.toolCallId !== void 0) approval.callId = request.toolCallId;
|
|
3769
|
-
if (request.reason !== void 0) approval.reason = request.reason;
|
|
3770
|
-
if (details?.rejectMessage !== void 0) approval.rejectMessage = details.rejectMessage;
|
|
3771
|
-
if (context.emit !== void 0) approval.emit = context.emit;
|
|
3772
|
-
const decision = new Promise((resolve2) => {
|
|
3773
|
-
approval.resolve = (decision2) => {
|
|
3774
|
-
const current = approvals.get(id);
|
|
3775
|
-
if (!isPendingApproval(current)) {
|
|
3776
|
-
if (current !== void 0) {
|
|
3777
|
-
resolve2(approvalDecision(current.status === "approved", current.reason));
|
|
3778
|
-
}
|
|
3779
|
-
return;
|
|
3780
|
-
}
|
|
3781
|
-
const reason = decision2.approved ? decision2.reason : decision2.reason ?? details?.rejectMessage ?? "Rejected in Anvia Studio.";
|
|
3782
|
-
const resolved = resolveApproval(
|
|
3783
|
-
current,
|
|
3784
|
-
decision2.approved ? "approved" : "rejected",
|
|
3785
|
-
reason === void 0 ? {} : { reason }
|
|
3786
|
-
);
|
|
3787
|
-
approvals.set(id, resolved);
|
|
3788
|
-
context.emit?.({ type: "tool_approval_result", approval: resolved });
|
|
3789
|
-
resolve2(approvalDecision(decision2.approved, reason));
|
|
3790
|
-
};
|
|
3791
|
-
});
|
|
3792
|
-
approvals.set(id, approval);
|
|
3793
|
-
context.emit?.({ type: "tool_approval_request", approval: publicApproval(approval) });
|
|
3794
|
-
return decision;
|
|
3795
|
-
}
|
|
3796
|
-
function isPendingApproval(approval) {
|
|
3797
|
-
return approval !== void 0 && approval.status === "pending" && "resolve" in approval;
|
|
3798
|
-
}
|
|
3799
|
-
function resolveApproval(approval, status, options = {}) {
|
|
3800
|
-
const resolved = {
|
|
3801
|
-
...approval,
|
|
3802
|
-
status,
|
|
3803
|
-
resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3804
|
-
};
|
|
3805
|
-
if (options.reason !== void 0) resolved.reason = options.reason;
|
|
3806
|
-
return publicApproval(resolved);
|
|
3807
|
-
}
|
|
3808
|
-
function approvalDecision(approved, reason) {
|
|
3809
|
-
const decision = { approved };
|
|
3810
|
-
if (reason !== void 0) decision.reason = reason;
|
|
3811
|
-
return decision;
|
|
3812
|
-
}
|
|
3813
|
-
function publicApproval(approval) {
|
|
3814
|
-
const { emit, rejectMessage, resolve: resolve2, ...rest } = approval;
|
|
3815
|
-
void emit;
|
|
3816
|
-
void rejectMessage;
|
|
3817
|
-
void resolve2;
|
|
3818
|
-
return rest;
|
|
3819
|
-
}
|
|
3820
|
-
|
|
3821
|
-
// src/runtime/eval-config.ts
|
|
3822
|
-
var CASE_PREVIEW_LIMIT = 5;
|
|
3823
|
-
var PREVIEW_VALUE_MAX_LENGTH = 500;
|
|
3824
|
-
function evalConfig(suite) {
|
|
3825
|
-
const casePreviews = suite.cases.slice(0, CASE_PREVIEW_LIMIT).map(casePreview);
|
|
3826
|
-
const metricSummaries = suite.metrics.map(metricSummary);
|
|
3827
|
-
const config = {
|
|
3828
|
-
id: suite.id ?? suite.name,
|
|
3829
|
-
name: suite.name,
|
|
3830
|
-
caseCount: suite.cases.length,
|
|
3831
|
-
metricNames: suite.metrics.map((metric) => metric.name),
|
|
3832
|
-
casePreviewCount: casePreviews.length,
|
|
3833
|
-
casePreviews,
|
|
3834
|
-
metricSummaries
|
|
3835
|
-
};
|
|
3836
|
-
if (suite.description !== void 0) config.description = suite.description;
|
|
3837
|
-
if (suite.concurrency !== void 0) config.concurrency = suite.concurrency;
|
|
3838
|
-
if (suite.metadata !== void 0) config.metadata = suite.metadata;
|
|
3839
|
-
return config;
|
|
3840
|
-
}
|
|
3841
|
-
function casePreview(testCase, index) {
|
|
3842
|
-
if (!isRecord2(testCase)) {
|
|
3843
|
-
return {
|
|
3844
|
-
id: `case-${index + 1}`,
|
|
3845
|
-
input: previewValue(testCase)
|
|
3846
|
-
};
|
|
3847
|
-
}
|
|
3848
|
-
const preview = {
|
|
3849
|
-
id: typeof testCase.id === "string" && testCase.id.length > 0 ? testCase.id : `case-${index + 1}`
|
|
3850
|
-
};
|
|
3851
|
-
if ("input" in testCase) preview.input = previewValue(testCase.input);
|
|
3852
|
-
if ("expected" in testCase) preview.expected = previewValue(testCase.expected);
|
|
3853
|
-
const keys = metadataKeys(testCase.metadata);
|
|
3854
|
-
if (keys !== void 0) preview.metadataKeys = keys;
|
|
3855
|
-
return preview;
|
|
3856
|
-
}
|
|
3857
|
-
function metricSummary(metric, index) {
|
|
3858
|
-
if (!isRecord2(metric)) {
|
|
3859
|
-
return { name: `metric-${index + 1}` };
|
|
3611
|
+
function metricSummary(metric, index) {
|
|
3612
|
+
if (!isRecord2(metric)) {
|
|
3613
|
+
return { name: `metric-${index + 1}` };
|
|
3860
3614
|
}
|
|
3861
3615
|
const summary = {
|
|
3862
3616
|
name: typeof metric.name === "string" && metric.name.length > 0 ? metric.name : `metric-${index + 1}`
|
|
@@ -3898,6 +3652,41 @@ function isRecord2(value) {
|
|
|
3898
3652
|
|
|
3899
3653
|
// src/runtime/knowledge.ts
|
|
3900
3654
|
import { getAgentToolState } from "@anvia/core/internal/agent";
|
|
3655
|
+
|
|
3656
|
+
// src/runtime/query.ts
|
|
3657
|
+
function optionalQueryString(value) {
|
|
3658
|
+
const trimmed = value?.trim();
|
|
3659
|
+
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
3660
|
+
}
|
|
3661
|
+
function parseLimit(value, defaultMax = 50, max = 100) {
|
|
3662
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3663
|
+
return defaultMax;
|
|
3664
|
+
}
|
|
3665
|
+
const limit = Number(value);
|
|
3666
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
3667
|
+
return void 0;
|
|
3668
|
+
}
|
|
3669
|
+
return Math.min(limit, max);
|
|
3670
|
+
}
|
|
3671
|
+
function parseTraceStatus(value) {
|
|
3672
|
+
const status = optionalQueryString(value);
|
|
3673
|
+
if (status === void 0) {
|
|
3674
|
+
return void 0;
|
|
3675
|
+
}
|
|
3676
|
+
return status === "running" || status === "success" || status === "suspended" || status === "error" ? status : false;
|
|
3677
|
+
}
|
|
3678
|
+
function parseAfter(value) {
|
|
3679
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3680
|
+
return void 0;
|
|
3681
|
+
}
|
|
3682
|
+
const after = Number(value);
|
|
3683
|
+
if (!Number.isInteger(after) || after < 0) {
|
|
3684
|
+
return false;
|
|
3685
|
+
}
|
|
3686
|
+
return after;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
// src/runtime/knowledge.ts
|
|
3901
3690
|
function registerKnowledgeRoutes(app, props) {
|
|
3902
3691
|
app.get("/knowledge", async (c) => {
|
|
3903
3692
|
const limit = parseLimit(c.req.query("limit"));
|
|
@@ -4446,8 +4235,10 @@ function capabilityConfig(_options, agents, pipelines, stores, sandboxCount = 0)
|
|
|
4446
4235
|
if (sandboxCount > 0) {
|
|
4447
4236
|
capabilities.sandboxes = { enabled: true };
|
|
4448
4237
|
}
|
|
4449
|
-
if (agents.some(
|
|
4450
|
-
|
|
4238
|
+
if (agents.some(
|
|
4239
|
+
(agent) => agent.agent.tools.some((tool) => toolRequiresApproval(tool) || isQuestionTool(tool))
|
|
4240
|
+
)) {
|
|
4241
|
+
capabilities.interactions = { enabled: true };
|
|
4451
4242
|
}
|
|
4452
4243
|
if (agents.some(agentHasKnowledge)) {
|
|
4453
4244
|
capabilities.knowledge = { enabled: true };
|
|
@@ -4529,6 +4320,47 @@ async function parseEvalRunRequest(c) {
|
|
|
4529
4320
|
return request;
|
|
4530
4321
|
}
|
|
4531
4322
|
|
|
4323
|
+
// src/runtime/interactions.ts
|
|
4324
|
+
import {
|
|
4325
|
+
assertAgentInteractionResponse
|
|
4326
|
+
} from "@anvia/core/agent";
|
|
4327
|
+
function createStudioContinuationRegistry() {
|
|
4328
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4329
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
4330
|
+
return {
|
|
4331
|
+
register(registration) {
|
|
4332
|
+
const id = registration.continuation.interaction.id;
|
|
4333
|
+
if (pending.has(id) || claimed.has(id)) {
|
|
4334
|
+
throw new TypeError(`Duplicate Studio interaction ID "${id}".`);
|
|
4335
|
+
}
|
|
4336
|
+
pending.set(id, registration);
|
|
4337
|
+
},
|
|
4338
|
+
take(interactionId, agentId, response) {
|
|
4339
|
+
if (claimed.has(interactionId)) return { status: "claimed" };
|
|
4340
|
+
const registration = pending.get(interactionId);
|
|
4341
|
+
if (registration === void 0 || registration.continuation.agentId !== agentId) {
|
|
4342
|
+
return { status: "missing" };
|
|
4343
|
+
}
|
|
4344
|
+
const request = registration.continuation.interaction;
|
|
4345
|
+
try {
|
|
4346
|
+
assertAgentInteractionResponse(request, response);
|
|
4347
|
+
} catch (error) {
|
|
4348
|
+
return {
|
|
4349
|
+
status: "invalid",
|
|
4350
|
+
error: error instanceof TypeError ? error : new TypeError(String(error))
|
|
4351
|
+
};
|
|
4352
|
+
}
|
|
4353
|
+
pending.delete(interactionId);
|
|
4354
|
+
claimed.add(interactionId);
|
|
4355
|
+
return { status: "ready", registration };
|
|
4356
|
+
},
|
|
4357
|
+
clear() {
|
|
4358
|
+
pending.clear();
|
|
4359
|
+
claimed.clear();
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4532
4364
|
// src/runtime/mcps.ts
|
|
4533
4365
|
function registerMcpRoutes(app, props) {
|
|
4534
4366
|
app.get("/agents/:agentId/mcps", async (c) => {
|
|
@@ -5481,272 +5313,284 @@ function parsePipelineLogAfter(value) {
|
|
|
5481
5313
|
return after;
|
|
5482
5314
|
}
|
|
5483
5315
|
|
|
5484
|
-
// src/runtime/
|
|
5485
|
-
import {
|
|
5486
|
-
import {
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
const
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
}
|
|
5500
|
-
if (runId !== void 0) {
|
|
5501
|
-
options.runId = runId;
|
|
5502
|
-
}
|
|
5503
|
-
if (agentId !== void 0) {
|
|
5504
|
-
options.agentId = agentId;
|
|
5505
|
-
}
|
|
5506
|
-
if (sessionId !== void 0) {
|
|
5507
|
-
options.sessionId = sessionId;
|
|
5508
|
-
}
|
|
5509
|
-
return c.json({ questions: questions.list(options) });
|
|
5316
|
+
// src/runtime/sandbox-views.ts
|
|
5317
|
+
import { upgradeWebSocket } from "@hono/node-server";
|
|
5318
|
+
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
5319
|
+
import { WebSocket } from "ws";
|
|
5320
|
+
var minLeaseTimeoutMs = 5e3;
|
|
5321
|
+
var maxLeaseTimeoutMs = 12e4;
|
|
5322
|
+
var maxPendingWebSocketBytes = 1024 * 1024;
|
|
5323
|
+
function registerSandboxViewRoutes(app, registry) {
|
|
5324
|
+
const leases = /* @__PURE__ */ new Map();
|
|
5325
|
+
const upstreamSockets = /* @__PURE__ */ new Set();
|
|
5326
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/control", async (c) => {
|
|
5327
|
+
const resolved = await authorizedView(c, registry);
|
|
5328
|
+
if (resolved instanceof Response) return resolved;
|
|
5329
|
+
pruneExpiredLease(leases, viewKey(resolved));
|
|
5330
|
+
return c.json(resolved.view.source.control.snapshot());
|
|
5510
5331
|
});
|
|
5511
|
-
app.
|
|
5512
|
-
const
|
|
5513
|
-
if (
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
return c.json(result);
|
|
5332
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/connection", async (c) => {
|
|
5333
|
+
const resolved = await authorizedView(c, registry);
|
|
5334
|
+
if (resolved instanceof Response) return resolved;
|
|
5335
|
+
const response = {
|
|
5336
|
+
sandboxRef: resolved.entry.summary.ref,
|
|
5337
|
+
viewId: resolved.view.id,
|
|
5338
|
+
protocol: "novnc",
|
|
5339
|
+
websocketPath: `/sandboxes/${encodeURIComponent(resolved.entry.summary.ref)}/views/${encodeURIComponent(resolved.view.id)}/ws`,
|
|
5340
|
+
authentication: resolved.view.authentication.type === "password" ? { type: "password", password: resolved.view.authentication.password } : { type: "none" }
|
|
5341
|
+
};
|
|
5342
|
+
c.header("cache-control", "no-store");
|
|
5343
|
+
return c.json(response);
|
|
5524
5344
|
});
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
return
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
let body;
|
|
5535
|
-
try {
|
|
5536
|
-
body = await c.req.json();
|
|
5537
|
-
} catch {
|
|
5538
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
|
|
5539
|
-
}
|
|
5540
|
-
if (!isObject(body)) {
|
|
5541
|
-
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
5542
|
-
}
|
|
5543
|
-
if (!Array.isArray(body.answers)) {
|
|
5544
|
-
return { error: errorResponse(c, 400, "bad_request", "answers must be an array") };
|
|
5545
|
-
}
|
|
5546
|
-
const answers = [];
|
|
5547
|
-
for (const answer of body.answers) {
|
|
5548
|
-
if (!isObject(answer)) {
|
|
5549
|
-
return { error: errorResponse(c, 400, "bad_request", "answers must contain objects") };
|
|
5550
|
-
}
|
|
5551
|
-
if (typeof answer.questionId !== "string" || answer.questionId.trim().length === 0) {
|
|
5552
|
-
return { error: errorResponse(c, 400, "bad_request", "questionId must be a string") };
|
|
5345
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/acquire", async (c) => {
|
|
5346
|
+
const resolved = await authorizedView(c, registry);
|
|
5347
|
+
if (resolved instanceof Response) return resolved;
|
|
5348
|
+
const body = await readControlBody(c);
|
|
5349
|
+
if (body instanceof Response) return body;
|
|
5350
|
+
const key = viewKey(resolved);
|
|
5351
|
+
pruneExpiredLease(leases, key);
|
|
5352
|
+
if (leases.has(key)) {
|
|
5353
|
+
return errorResponse(c, 409, "conflict", "Browser view is already controlled");
|
|
5553
5354
|
}
|
|
5554
|
-
|
|
5555
|
-
|
|
5355
|
+
try {
|
|
5356
|
+
const lease = await resolved.view.source.control.acquireHumanControl({
|
|
5357
|
+
ownerId: body.ownerId,
|
|
5358
|
+
leaseTimeoutMs: body.leaseTimeoutMs,
|
|
5359
|
+
abortSignal: c.req.raw.signal
|
|
5360
|
+
});
|
|
5361
|
+
leases.set(key, lease);
|
|
5362
|
+
return c.json(leaseResponse(lease), 201);
|
|
5363
|
+
} catch (error) {
|
|
5364
|
+
return controlErrorResponse(c, error);
|
|
5556
5365
|
}
|
|
5557
|
-
|
|
5558
|
-
|
|
5366
|
+
});
|
|
5367
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/renew", async (c) => {
|
|
5368
|
+
const resolved = await authorizedView(c, registry);
|
|
5369
|
+
if (resolved instanceof Response) return resolved;
|
|
5370
|
+
const body = await readLeaseBody(c);
|
|
5371
|
+
if (body instanceof Response) return body;
|
|
5372
|
+
const key = viewKey(resolved);
|
|
5373
|
+
pruneExpiredLease(leases, key);
|
|
5374
|
+
const lease = leases.get(key);
|
|
5375
|
+
if (lease === void 0 || lease.id !== body.leaseId) {
|
|
5376
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is not active");
|
|
5559
5377
|
}
|
|
5560
|
-
|
|
5561
|
-
|
|
5378
|
+
try {
|
|
5379
|
+
lease.renew({ leaseTimeoutMs: body.leaseTimeoutMs });
|
|
5380
|
+
return c.json(leaseResponse(lease));
|
|
5381
|
+
} catch (error) {
|
|
5382
|
+
leases.delete(key);
|
|
5383
|
+
return controlErrorResponse(c, error);
|
|
5562
5384
|
}
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
if (
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5385
|
+
});
|
|
5386
|
+
app.post("/sandboxes/:sandboxRef/views/:viewId/control/release", async (c) => {
|
|
5387
|
+
const resolved = await authorizedView(c, registry);
|
|
5388
|
+
if (resolved instanceof Response) return resolved;
|
|
5389
|
+
const body = await readReleaseBody(c);
|
|
5390
|
+
if (body instanceof Response) return body;
|
|
5391
|
+
const key = viewKey(resolved);
|
|
5392
|
+
const lease = leases.get(key);
|
|
5393
|
+
if (lease === void 0 || lease.id !== body.leaseId) {
|
|
5394
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is not active");
|
|
5395
|
+
}
|
|
5396
|
+
lease.release();
|
|
5397
|
+
leases.delete(key);
|
|
5398
|
+
return c.json({ released: true });
|
|
5399
|
+
});
|
|
5400
|
+
app.get("/sandboxes/:sandboxRef/views/:viewId/ws", async (c) => {
|
|
5401
|
+
const resolved = await authorizedView(c, registry);
|
|
5402
|
+
if (resolved instanceof Response) return resolved;
|
|
5403
|
+
const upstreamUrl = `ws://${hostForUrl(resolved.host)}:${resolved.port}/websockify`;
|
|
5404
|
+
return upgradeWebSocket(c, createProxyEvents(upstreamUrl, upstreamSockets), {
|
|
5405
|
+
onError: () => void 0
|
|
5406
|
+
});
|
|
5407
|
+
});
|
|
5408
|
+
return () => {
|
|
5409
|
+
for (const lease of leases.values()) lease.release();
|
|
5410
|
+
leases.clear();
|
|
5411
|
+
for (const socket of upstreamSockets) socket.close(1001, "Studio closed");
|
|
5412
|
+
upstreamSockets.clear();
|
|
5413
|
+
};
|
|
5572
5414
|
}
|
|
5573
|
-
function
|
|
5574
|
-
|
|
5415
|
+
function createProxyEvents(upstreamUrl, sockets) {
|
|
5416
|
+
let upstream;
|
|
5417
|
+
let pendingBytes = 0;
|
|
5418
|
+
const pending = [];
|
|
5575
5419
|
return {
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
const request = {
|
|
5588
|
-
toolName,
|
|
5589
|
-
internalCallId,
|
|
5590
|
-
args,
|
|
5591
|
-
questions: prompts.questions
|
|
5592
|
-
};
|
|
5593
|
-
if (toolCallId !== void 0) request.toolCallId = toolCallId;
|
|
5594
|
-
const answers = await requestQuestion(questions, context, request);
|
|
5595
|
-
return control.skip(JSON.stringify({ answers }));
|
|
5596
|
-
}
|
|
5420
|
+
onOpen(_event, downstream) {
|
|
5421
|
+
const protocols = downstream.protocol === null || downstream.protocol.length === 0 ? void 0 : [downstream.protocol];
|
|
5422
|
+
upstream = new WebSocket(upstreamUrl, protocols, {
|
|
5423
|
+
headers: { origin: "http://127.0.0.1" }
|
|
5424
|
+
});
|
|
5425
|
+
sockets.add(upstream);
|
|
5426
|
+
upstream.binaryType = "arraybuffer";
|
|
5427
|
+
upstream.once("open", () => {
|
|
5428
|
+
for (const message of pending) upstream?.send(message);
|
|
5429
|
+
pending.length = 0;
|
|
5430
|
+
pendingBytes = 0;
|
|
5597
5431
|
});
|
|
5432
|
+
upstream.on("message", (data, isBinary) => {
|
|
5433
|
+
const value = isBinary ? Uint8Array.from(Buffer.from(data)) : data.toString();
|
|
5434
|
+
downstream.send(value);
|
|
5435
|
+
});
|
|
5436
|
+
upstream.once("close", (code, reason) => {
|
|
5437
|
+
sockets.delete(upstream);
|
|
5438
|
+
downstream.close(normalizeCloseCode(code), reason.toString("utf8").slice(0, 123));
|
|
5439
|
+
});
|
|
5440
|
+
upstream.once("error", () => downstream.close(1011, "noVNC upstream failed"));
|
|
5598
5441
|
},
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
if (
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
if (options.status === "resolved" && question.status === "pending") {
|
|
5605
|
-
return false;
|
|
5606
|
-
}
|
|
5607
|
-
if (options.runId !== void 0 && question.runId !== options.runId) {
|
|
5608
|
-
return false;
|
|
5609
|
-
}
|
|
5610
|
-
if (options.agentId !== void 0 && question.agentId !== options.agentId) {
|
|
5611
|
-
return false;
|
|
5442
|
+
onMessage(event, downstream) {
|
|
5443
|
+
void normalizeWebSocketMessage(event.data).then((message) => {
|
|
5444
|
+
if (upstream?.readyState === WebSocket.OPEN) {
|
|
5445
|
+
upstream.send(message);
|
|
5446
|
+
return;
|
|
5612
5447
|
}
|
|
5613
|
-
|
|
5614
|
-
|
|
5448
|
+
pendingBytes += typeof message === "string" ? Buffer.byteLength(message) : message.byteLength;
|
|
5449
|
+
if (pendingBytes > maxPendingWebSocketBytes) {
|
|
5450
|
+
downstream.close(1009, "Pending noVNC messages exceeded the limit");
|
|
5451
|
+
return;
|
|
5615
5452
|
}
|
|
5616
|
-
|
|
5617
|
-
}).
|
|
5453
|
+
pending.push(message);
|
|
5454
|
+
}).catch(() => downstream.close(1003, "Unsupported noVNC message"));
|
|
5618
5455
|
},
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
if (!isPendingQuestion(question) || question.runId !== runId) {
|
|
5623
|
-
continue;
|
|
5624
|
-
}
|
|
5625
|
-
const resolved = cancelQuestion(question);
|
|
5626
|
-
questions.set(question.id, resolved);
|
|
5627
|
-
question.emit?.({ type: "tool_question_result", question: resolved });
|
|
5628
|
-
question.resolve([]);
|
|
5629
|
-
cancelled.push(resolved);
|
|
5630
|
-
}
|
|
5631
|
-
return cancelled;
|
|
5456
|
+
onClose() {
|
|
5457
|
+
upstream?.close(1e3, "Viewer disconnected");
|
|
5458
|
+
if (upstream !== void 0) sockets.delete(upstream);
|
|
5632
5459
|
},
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
if (question === void 0) {
|
|
5636
|
-
return "missing";
|
|
5637
|
-
}
|
|
5638
|
-
if (!isPendingQuestion(question)) {
|
|
5639
|
-
return "resolved";
|
|
5640
|
-
}
|
|
5641
|
-
const resolved = resolveQuestion(question, answers);
|
|
5642
|
-
questions.set(id, resolved);
|
|
5643
|
-
question.emit?.({ type: "tool_question_result", question: resolved });
|
|
5644
|
-
question.resolve(answers);
|
|
5645
|
-
return publicQuestion(resolved);
|
|
5460
|
+
onError() {
|
|
5461
|
+
upstream?.close(1011, "Viewer connection failed");
|
|
5646
5462
|
}
|
|
5647
5463
|
};
|
|
5648
5464
|
}
|
|
5649
|
-
async function
|
|
5650
|
-
const
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
};
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
if (
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
}
|
|
5676
|
-
function normalizeQuestionPrompts(args) {
|
|
5677
|
-
if (!isObject(args)) {
|
|
5678
|
-
return { error: "ask_question requires a JSON object with questions." };
|
|
5679
|
-
}
|
|
5680
|
-
const rawQuestions = Array.isArray(args.questions) ? args.questions : [args];
|
|
5681
|
-
if (rawQuestions.length === 0) {
|
|
5682
|
-
return { error: "ask_question requires at least one question." };
|
|
5683
|
-
}
|
|
5684
|
-
const questions = [];
|
|
5685
|
-
for (const [index, question] of rawQuestions.entries()) {
|
|
5686
|
-
const normalized = normalizeQuestionPrompt(question, index);
|
|
5687
|
-
if (normalized === void 0) {
|
|
5688
|
-
return {
|
|
5689
|
-
error: "ask_question requires every question to include text and at least one choice."
|
|
5690
|
-
};
|
|
5465
|
+
async function authorizedView(c, registry) {
|
|
5466
|
+
const entry = registry.get(c.req.param("sandboxRef") ?? "");
|
|
5467
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5468
|
+
const view = entry.views.get(c.req.param("viewId") ?? "");
|
|
5469
|
+
if (view === void 0) return errorResponse(c, 404, "not_found", "Sandbox view not found");
|
|
5470
|
+
if (!await authorize(c, entry, view)) {
|
|
5471
|
+
return errorResponse(c, 403, "forbidden", "Sandbox view access denied");
|
|
5472
|
+
}
|
|
5473
|
+
const published = entry.inspector.publishedPorts?.find(
|
|
5474
|
+
(port) => port.containerPort === view.source.containerPort && port.protocol === "tcp" && isLoopbackAddress(port.host)
|
|
5475
|
+
);
|
|
5476
|
+
if (published === void 0) {
|
|
5477
|
+
return errorResponse(c, 503, "internal_error", "Sandbox view port is unavailable");
|
|
5478
|
+
}
|
|
5479
|
+
return { entry, view, host: published.host, port: published.hostPort };
|
|
5480
|
+
}
|
|
5481
|
+
async function authorize(c, entry, view) {
|
|
5482
|
+
if (view.access.mode === "authorize") {
|
|
5483
|
+
try {
|
|
5484
|
+
return await view.access.authorize({
|
|
5485
|
+
request: c.req.raw,
|
|
5486
|
+
sandboxRef: entry.summary.ref,
|
|
5487
|
+
viewId: view.id
|
|
5488
|
+
}) === true;
|
|
5489
|
+
} catch {
|
|
5490
|
+
return false;
|
|
5691
5491
|
}
|
|
5692
|
-
questions.push(normalized);
|
|
5693
5492
|
}
|
|
5694
|
-
|
|
5493
|
+
let remoteAddress;
|
|
5494
|
+
try {
|
|
5495
|
+
const address = getConnInfo(c).remote.address;
|
|
5496
|
+
if (address === void 0) return false;
|
|
5497
|
+
remoteAddress = address;
|
|
5498
|
+
} catch {
|
|
5499
|
+
return false;
|
|
5500
|
+
}
|
|
5501
|
+
if (!isLoopbackAddress(remoteAddress)) return false;
|
|
5502
|
+
const expectedOrigin = new URL(c.req.url).origin;
|
|
5503
|
+
const origin = c.req.header("origin");
|
|
5504
|
+
return origin === expectedOrigin || origin === void 0 && c.req.header("sec-fetch-site") === "same-origin";
|
|
5695
5505
|
}
|
|
5696
|
-
function
|
|
5697
|
-
|
|
5698
|
-
|
|
5506
|
+
async function readControlBody(c) {
|
|
5507
|
+
const body = await readJsonObject(c);
|
|
5508
|
+
if (body instanceof Response) return body;
|
|
5509
|
+
if (typeof body.ownerId !== "string" || body.ownerId.length === 0 || body.ownerId.length > 200) {
|
|
5510
|
+
return errorResponse(c, 400, "bad_request", "ownerId must be a non-empty string");
|
|
5699
5511
|
}
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
return void 0;
|
|
5512
|
+
if (!isLeaseTimeout(body.leaseTimeoutMs)) {
|
|
5513
|
+
return errorResponse(c, 400, "bad_request", "leaseTimeoutMs is outside the allowed range");
|
|
5703
5514
|
}
|
|
5704
|
-
return {
|
|
5705
|
-
id: typeof value.id === "string" && value.id.trim().length > 0 ? value.id.trim() : `question_${index + 1}`,
|
|
5706
|
-
question: value.question.trim(),
|
|
5707
|
-
choices
|
|
5708
|
-
};
|
|
5515
|
+
return { ownerId: body.ownerId, leaseTimeoutMs: body.leaseTimeoutMs };
|
|
5709
5516
|
}
|
|
5710
|
-
function
|
|
5711
|
-
|
|
5712
|
-
|
|
5517
|
+
async function readLeaseBody(c) {
|
|
5518
|
+
const body = await readJsonObject(c);
|
|
5519
|
+
if (body instanceof Response) return body;
|
|
5520
|
+
if (typeof body.leaseId !== "string" || body.leaseId.length === 0) {
|
|
5521
|
+
return errorResponse(c, 400, "bad_request", "leaseId must be a non-empty string");
|
|
5713
5522
|
}
|
|
5714
|
-
if (!
|
|
5715
|
-
return
|
|
5523
|
+
if (!isLeaseTimeout(body.leaseTimeoutMs)) {
|
|
5524
|
+
return errorResponse(c, 400, "bad_request", "leaseTimeoutMs is outside the allowed range");
|
|
5716
5525
|
}
|
|
5717
|
-
return {
|
|
5718
|
-
label: value.label.trim(),
|
|
5719
|
-
value: typeof value.value === "string" && value.value.trim().length > 0 ? value.value.trim() : value.label.trim()
|
|
5720
|
-
};
|
|
5526
|
+
return { leaseId: body.leaseId, leaseTimeoutMs: body.leaseTimeoutMs };
|
|
5721
5527
|
}
|
|
5722
|
-
function
|
|
5723
|
-
|
|
5528
|
+
async function readReleaseBody(c) {
|
|
5529
|
+
const body = await readJsonObject(c);
|
|
5530
|
+
if (body instanceof Response) return body;
|
|
5531
|
+
if (typeof body.leaseId !== "string" || body.leaseId.length === 0) {
|
|
5532
|
+
return errorResponse(c, 400, "bad_request", "leaseId must be a non-empty string");
|
|
5533
|
+
}
|
|
5534
|
+
return { leaseId: body.leaseId };
|
|
5724
5535
|
}
|
|
5725
|
-
function
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
}
|
|
5536
|
+
async function readJsonObject(c) {
|
|
5537
|
+
let body;
|
|
5538
|
+
try {
|
|
5539
|
+
body = await c.req.json();
|
|
5540
|
+
} catch {
|
|
5541
|
+
return errorResponse(c, 400, "bad_request", "Request body must be valid JSON");
|
|
5542
|
+
}
|
|
5543
|
+
return isRecord4(body) ? body : errorResponse(c, 400, "bad_request", "Request body must be an object");
|
|
5732
5544
|
}
|
|
5733
|
-
function
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5545
|
+
function controlErrorResponse(c, error) {
|
|
5546
|
+
if (isRecord4(error) && error.code === "human_controlled") {
|
|
5547
|
+
return errorResponse(c, 409, "conflict", "Browser view is already controlled");
|
|
5548
|
+
}
|
|
5549
|
+
if (isRecord4(error) && error.code === "invalid_state") {
|
|
5550
|
+
return errorResponse(c, 409, "conflict", "Browser control lease is no longer active");
|
|
5551
|
+
}
|
|
5552
|
+
return errorResponse(c, 500, "internal_error", "Browser control operation failed");
|
|
5553
|
+
}
|
|
5554
|
+
function leaseResponse(lease) {
|
|
5555
|
+
return { id: lease.id, ownerId: lease.ownerId, expiresAt: lease.expiresAt };
|
|
5739
5556
|
}
|
|
5740
|
-
function
|
|
5741
|
-
const
|
|
5742
|
-
void
|
|
5743
|
-
|
|
5744
|
-
|
|
5557
|
+
function pruneExpiredLease(leases, key) {
|
|
5558
|
+
const lease = leases.get(key);
|
|
5559
|
+
if (lease !== void 0 && Date.parse(lease.expiresAt) <= Date.now()) {
|
|
5560
|
+
lease.release();
|
|
5561
|
+
leases.delete(key);
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
function viewKey(resolved) {
|
|
5565
|
+
return `${resolved.entry.summary.ref}\0${resolved.view.id}`;
|
|
5566
|
+
}
|
|
5567
|
+
function isLeaseTimeout(value) {
|
|
5568
|
+
return Number.isSafeInteger(value) && Number(value) >= minLeaseTimeoutMs && Number(value) <= maxLeaseTimeoutMs;
|
|
5569
|
+
}
|
|
5570
|
+
function isLoopbackAddress(value) {
|
|
5571
|
+
return value === "localhost" || value === "127.0.0.1" || value === "::1" || value.startsWith("127.") || value.startsWith("::ffff:127.");
|
|
5572
|
+
}
|
|
5573
|
+
function hostForUrl(host) {
|
|
5574
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
5575
|
+
}
|
|
5576
|
+
async function normalizeWebSocketMessage(value) {
|
|
5577
|
+
if (typeof value === "string") return value;
|
|
5578
|
+
if (value instanceof ArrayBuffer) return Uint8Array.from(new Uint8Array(value));
|
|
5579
|
+
if (ArrayBuffer.isView(value)) {
|
|
5580
|
+
return Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
5581
|
+
}
|
|
5582
|
+
if (value instanceof Blob) return Uint8Array.from(new Uint8Array(await value.arrayBuffer()));
|
|
5583
|
+
throw new TypeError("Unsupported noVNC WebSocket message type.");
|
|
5584
|
+
}
|
|
5585
|
+
function normalizeCloseCode(code) {
|
|
5586
|
+
return code >= 1e3 && code <= 4999 && code !== 1004 && code !== 1005 && code !== 1006 ? code : 1001;
|
|
5587
|
+
}
|
|
5588
|
+
function isRecord4(value) {
|
|
5589
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5745
5590
|
}
|
|
5746
5591
|
|
|
5747
5592
|
// src/runtime/sandboxes.ts
|
|
5748
5593
|
import path from "path";
|
|
5749
|
-
var sandboxToolMetadataKey = /* @__PURE__ */ Symbol.for("anvia.sandbox.tool.metadata");
|
|
5750
5594
|
var maxFileResponseBytes = 10 * 1024 * 1024;
|
|
5751
5595
|
var defaultProcessLogBytes = 64 * 1024;
|
|
5752
5596
|
var maxProcessLogBytes = 1024 * 1024;
|
|
@@ -5765,51 +5609,41 @@ var StudioSandboxRegistry = class {
|
|
|
5765
5609
|
return this.entries.get(ref);
|
|
5766
5610
|
}
|
|
5767
5611
|
};
|
|
5768
|
-
function createStudioSandboxRegistry(agents) {
|
|
5769
|
-
const
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
draft.toolNames.add(tool.name);
|
|
5783
|
-
drafts.set(session, draft);
|
|
5784
|
-
}
|
|
5785
|
-
}
|
|
5786
|
-
const refCounts = /* @__PURE__ */ new Map();
|
|
5787
|
-
const entries = [...drafts.values()].sort(
|
|
5788
|
-
(left, right) => `${left.session.provider}\0${left.session.id}`.localeCompare(
|
|
5789
|
-
`${right.session.provider}\0${right.session.id}`
|
|
5790
|
-
)
|
|
5791
|
-
).map((draft) => {
|
|
5792
|
-
const baseRef = sandboxRef(draft.session.provider, draft.session.id);
|
|
5793
|
-
const occurrence = (refCounts.get(baseRef) ?? 0) + 1;
|
|
5794
|
-
refCounts.set(baseRef, occurrence);
|
|
5795
|
-
const ref = occurrence === 1 ? baseRef : `${baseRef}.${occurrence}`;
|
|
5612
|
+
function createStudioSandboxRegistry(agents, registrations) {
|
|
5613
|
+
const agentIds = new Set(agents.map((agent) => agent.id));
|
|
5614
|
+
const refs = /* @__PURE__ */ new Set();
|
|
5615
|
+
const entries = registrations.map((registration, index) => {
|
|
5616
|
+
validateRegistration(registration, index, agentIds);
|
|
5617
|
+
const inspector = registration.inspector;
|
|
5618
|
+
const ref = sandboxRef(inspector.provider, inspector.id);
|
|
5619
|
+
if (refs.has(ref)) {
|
|
5620
|
+
throw new TypeError(
|
|
5621
|
+
`sandboxes contains a duplicate provider/id registration: ${inspector.provider}/${inspector.id}`
|
|
5622
|
+
);
|
|
5623
|
+
}
|
|
5624
|
+
refs.add(ref);
|
|
5625
|
+
const views = new Map((registration.views ?? []).map((view) => [view.id, view]));
|
|
5796
5626
|
return {
|
|
5797
|
-
|
|
5627
|
+
inspector,
|
|
5628
|
+
views,
|
|
5798
5629
|
summary: {
|
|
5799
5630
|
ref,
|
|
5800
|
-
id:
|
|
5801
|
-
provider:
|
|
5802
|
-
workdir:
|
|
5803
|
-
agentIds:
|
|
5804
|
-
toolNames:
|
|
5631
|
+
id: inspector.id,
|
|
5632
|
+
provider: inspector.provider,
|
|
5633
|
+
workdir: inspector.workdir,
|
|
5634
|
+
agentIds: sortedUnique(registration.agentIds ?? []),
|
|
5635
|
+
toolNames: sortedUnique(registration.toolNames ?? []),
|
|
5636
|
+
views: [...views.values()].map((view) => ({ id: view.id, label: view.label, protocol: view.source.protocol })).sort((left, right) => left.id.localeCompare(right.id)),
|
|
5805
5637
|
capabilities: {
|
|
5806
|
-
files:
|
|
5807
|
-
ports: supportsPorts(
|
|
5808
|
-
processes: supportsProcesses(
|
|
5638
|
+
files: supportsFiles(inspector),
|
|
5639
|
+
ports: supportsPorts(inspector),
|
|
5640
|
+
processes: supportsProcesses(inspector),
|
|
5641
|
+
views: views.size > 0
|
|
5809
5642
|
}
|
|
5810
5643
|
}
|
|
5811
5644
|
};
|
|
5812
5645
|
});
|
|
5646
|
+
entries.sort((left, right) => left.summary.ref.localeCompare(right.summary.ref));
|
|
5813
5647
|
return new StudioSandboxRegistry(entries);
|
|
5814
5648
|
}
|
|
5815
5649
|
function registerSandboxRoutes(app, registry) {
|
|
@@ -5818,24 +5652,19 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5818
5652
|
app.get("/sandboxes", (c) => c.json({ sandboxes: registry.list() }));
|
|
5819
5653
|
app.get("/sandboxes/:sandboxRef", (c) => {
|
|
5820
5654
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5821
|
-
|
|
5822
|
-
return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5823
|
-
}
|
|
5824
|
-
return c.json(copySummary(entry.summary));
|
|
5655
|
+
return entry === void 0 ? errorResponse(c, 404, "not_found", "Sandbox not found") : c.json(copySummary(entry.summary));
|
|
5825
5656
|
});
|
|
5826
5657
|
app.get("/sandboxes/:sandboxRef/files", async (c) => {
|
|
5827
5658
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5828
|
-
if (entry === void 0)
|
|
5829
|
-
|
|
5830
|
-
}
|
|
5659
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5660
|
+
if (!supportsFiles(entry.inspector)) return unsupportedSandboxOperation(c, "files");
|
|
5831
5661
|
try {
|
|
5832
5662
|
const requestedPath = c.req.query("path") ?? ".";
|
|
5833
5663
|
const normalizedPath = normalizeSandboxPath(requestedPath, true);
|
|
5834
|
-
const entries = await listSandboxFiles(entry.session, normalizedPath);
|
|
5835
5664
|
return c.json({
|
|
5836
5665
|
sandboxRef: entry.summary.ref,
|
|
5837
5666
|
path: normalizedPath,
|
|
5838
|
-
entries
|
|
5667
|
+
entries: await listSandboxFiles(entry.inspector, normalizedPath, c.req.raw.signal)
|
|
5839
5668
|
});
|
|
5840
5669
|
} catch (error) {
|
|
5841
5670
|
return sandboxErrorResponse(c, error);
|
|
@@ -5843,9 +5672,8 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5843
5672
|
});
|
|
5844
5673
|
app.get("/sandboxes/:sandboxRef/files/content", async (c) => {
|
|
5845
5674
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5846
|
-
if (entry === void 0)
|
|
5847
|
-
|
|
5848
|
-
}
|
|
5675
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5676
|
+
if (!supportsFiles(entry.inspector)) return unsupportedSandboxOperation(c, "files.content");
|
|
5849
5677
|
try {
|
|
5850
5678
|
const requestedPath = c.req.query("path");
|
|
5851
5679
|
if (requestedPath === void 0) {
|
|
@@ -5856,8 +5684,11 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5856
5684
|
throw new SandboxRouteError(400, "bad_request", "download must be 1 when provided");
|
|
5857
5685
|
}
|
|
5858
5686
|
const normalizedPath = normalizeSandboxPath(requestedPath, false);
|
|
5859
|
-
const
|
|
5860
|
-
|
|
5687
|
+
const siblings = await listSandboxFiles(
|
|
5688
|
+
entry.inspector,
|
|
5689
|
+
path.posix.dirname(normalizedPath),
|
|
5690
|
+
c.req.raw.signal
|
|
5691
|
+
);
|
|
5861
5692
|
const file = siblings.find((candidate) => candidate.path === normalizedPath);
|
|
5862
5693
|
if (file === void 0) {
|
|
5863
5694
|
throw new SandboxRouteError(404, "not_found", "Sandbox file not found");
|
|
@@ -5869,25 +5700,24 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5869
5700
|
"Sandbox content path must refer to a regular file"
|
|
5870
5701
|
);
|
|
5871
5702
|
}
|
|
5872
|
-
if (file.size !== void 0 && file.size > maxFileResponseBytes)
|
|
5703
|
+
if (file.size !== void 0 && file.size > maxFileResponseBytes)
|
|
5873
5704
|
throw fileTooLarge(file.size);
|
|
5874
|
-
|
|
5875
|
-
|
|
5705
|
+
const rawBytes = await entry.inspector.readFile({
|
|
5706
|
+
path: normalizedPath,
|
|
5707
|
+
abortSignal: c.req.raw.signal
|
|
5708
|
+
});
|
|
5876
5709
|
if (!(rawBytes instanceof Uint8Array)) {
|
|
5877
5710
|
throw new TypeError("Sandbox readFile returned an invalid byte payload");
|
|
5878
5711
|
}
|
|
5879
|
-
if (rawBytes.byteLength > maxFileResponseBytes)
|
|
5880
|
-
|
|
5881
|
-
}
|
|
5882
|
-
const bytes = new Uint8Array(rawBytes.byteLength);
|
|
5883
|
-
bytes.set(rawBytes);
|
|
5712
|
+
if (rawBytes.byteLength > maxFileResponseBytes) throw fileTooLarge(rawBytes.byteLength);
|
|
5713
|
+
const bytes = rawBytes.slice();
|
|
5884
5714
|
const disposition = download === "1" ? "attachment" : "inline";
|
|
5885
5715
|
const filename = encodeURIComponent(path.posix.basename(normalizedPath));
|
|
5886
5716
|
return new Response(bytes, {
|
|
5887
5717
|
headers: {
|
|
5888
5718
|
"cache-control": "no-store",
|
|
5889
5719
|
"content-disposition": `${disposition}; filename*=UTF-8''${filename}`,
|
|
5890
|
-
"content-length":
|
|
5720
|
+
"content-length": `${bytes.byteLength}`,
|
|
5891
5721
|
"content-type": "application/octet-stream",
|
|
5892
5722
|
"x-content-type-options": "nosniff"
|
|
5893
5723
|
}
|
|
@@ -5898,16 +5728,12 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5898
5728
|
});
|
|
5899
5729
|
app.get("/sandboxes/:sandboxRef/ports", (c) => {
|
|
5900
5730
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5901
|
-
if (entry === void 0)
|
|
5902
|
-
|
|
5903
|
-
}
|
|
5904
|
-
if (!supportsPorts(entry.session)) {
|
|
5905
|
-
return unsupportedSandboxOperation(c, "ports");
|
|
5906
|
-
}
|
|
5731
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5732
|
+
if (!supportsPorts(entry.inspector)) return unsupportedSandboxOperation(c, "ports");
|
|
5907
5733
|
try {
|
|
5908
5734
|
return c.json({
|
|
5909
5735
|
sandboxRef: entry.summary.ref,
|
|
5910
|
-
ports: normalizePorts(entry.
|
|
5736
|
+
ports: normalizePorts(entry.inspector.publishedPorts)
|
|
5911
5737
|
});
|
|
5912
5738
|
} catch (error) {
|
|
5913
5739
|
return sandboxErrorResponse(c, error);
|
|
@@ -5915,16 +5741,16 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5915
5741
|
});
|
|
5916
5742
|
app.get("/sandboxes/:sandboxRef/processes", async (c) => {
|
|
5917
5743
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5918
|
-
if (entry === void 0)
|
|
5919
|
-
|
|
5920
|
-
}
|
|
5921
|
-
if (!supportsProcesses(entry.session)) {
|
|
5744
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5745
|
+
if (!supportsProcesses(entry.inspector)) {
|
|
5922
5746
|
return unsupportedSandboxOperation(c, "processes");
|
|
5923
5747
|
}
|
|
5924
5748
|
try {
|
|
5925
5749
|
return c.json({
|
|
5926
5750
|
sandboxRef: entry.summary.ref,
|
|
5927
|
-
processes: normalizeProcesses(
|
|
5751
|
+
processes: normalizeProcesses(
|
|
5752
|
+
await entry.inspector.listProcesses({ abortSignal: c.req.raw.signal })
|
|
5753
|
+
)
|
|
5928
5754
|
});
|
|
5929
5755
|
} catch (error) {
|
|
5930
5756
|
return sandboxErrorResponse(c, error);
|
|
@@ -5932,21 +5758,25 @@ function registerSandboxRoutes(app, registry) {
|
|
|
5932
5758
|
});
|
|
5933
5759
|
app.get("/sandboxes/:sandboxRef/processes/:processId/logs", async (c) => {
|
|
5934
5760
|
const entry = registry.get(c.req.param("sandboxRef"));
|
|
5935
|
-
if (entry === void 0)
|
|
5936
|
-
|
|
5937
|
-
}
|
|
5938
|
-
if (!supportsProcesses(entry.session)) {
|
|
5761
|
+
if (entry === void 0) return errorResponse(c, 404, "not_found", "Sandbox not found");
|
|
5762
|
+
if (!supportsProcesses(entry.inspector)) {
|
|
5939
5763
|
return unsupportedSandboxOperation(c, "processes.logs");
|
|
5940
5764
|
}
|
|
5941
5765
|
try {
|
|
5942
5766
|
const tailBytes = parseTailBytes(c.req.query("tailBytes"));
|
|
5943
5767
|
const processId = c.req.param("processId");
|
|
5944
|
-
const processes = normalizeProcesses(
|
|
5768
|
+
const processes = normalizeProcesses(
|
|
5769
|
+
await entry.inspector.listProcesses({ abortSignal: c.req.raw.signal })
|
|
5770
|
+
);
|
|
5945
5771
|
if (!processes.some((process2) => process2.id === processId)) {
|
|
5946
5772
|
throw new SandboxRouteError(404, "not_found", "Sandbox process not found");
|
|
5947
5773
|
}
|
|
5948
5774
|
const logs = normalizeProcessLogs(
|
|
5949
|
-
await entry.
|
|
5775
|
+
await entry.inspector.readProcessLogs({
|
|
5776
|
+
processId,
|
|
5777
|
+
tailBytes,
|
|
5778
|
+
abortSignal: c.req.raw.signal
|
|
5779
|
+
}),
|
|
5950
5780
|
tailBytes
|
|
5951
5781
|
);
|
|
5952
5782
|
return c.json({
|
|
@@ -5963,21 +5793,86 @@ async function sandboxNoStore(c, next) {
|
|
|
5963
5793
|
c.header("cache-control", "no-store");
|
|
5964
5794
|
await next();
|
|
5965
5795
|
}
|
|
5966
|
-
function
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5796
|
+
function validateRegistration(registration, index, knownAgentIds) {
|
|
5797
|
+
if (!isRecord5(registration) || !isInspector(registration.inspector)) {
|
|
5798
|
+
throw new TypeError(`sandboxes[${index}] must contain a valid inspector.`);
|
|
5799
|
+
}
|
|
5800
|
+
const inspector = registration.inspector;
|
|
5801
|
+
const files = supportsFiles(inspector);
|
|
5802
|
+
if (inspector.listFiles === void 0 !== (inspector.readFile === void 0)) {
|
|
5803
|
+
throw new TypeError(`sandboxes[${index}] must register both file inspector methods.`);
|
|
5970
5804
|
}
|
|
5971
|
-
|
|
5805
|
+
const processes = supportsProcesses(inspector);
|
|
5806
|
+
if (inspector.listProcesses === void 0 !== (inspector.readProcessLogs === void 0)) {
|
|
5807
|
+
throw new TypeError(`sandboxes[${index}] must register both process inspector methods.`);
|
|
5808
|
+
}
|
|
5809
|
+
if (!files && !supportsPorts(inspector) && !processes) {
|
|
5810
|
+
throw new TypeError(`sandboxes[${index}] inspector exposes no capabilities.`);
|
|
5811
|
+
}
|
|
5812
|
+
for (const agentId of sortedUnique(registration.agentIds ?? [])) {
|
|
5813
|
+
if (!knownAgentIds.has(agentId)) {
|
|
5814
|
+
throw new TypeError(`sandboxes[${index}] references an unknown agent: ${agentId}`);
|
|
5815
|
+
}
|
|
5816
|
+
}
|
|
5817
|
+
sortedUnique(registration.toolNames ?? []);
|
|
5818
|
+
validateViews(registration.views, inspector, index);
|
|
5972
5819
|
}
|
|
5973
|
-
function
|
|
5974
|
-
|
|
5820
|
+
function validateViews(views, inspector, registrationIndex) {
|
|
5821
|
+
if (views === void 0) return;
|
|
5822
|
+
if (!Array.isArray(views))
|
|
5823
|
+
throw new TypeError(`sandboxes[${registrationIndex}].views must be an array.`);
|
|
5824
|
+
if (!supportsPorts(inspector)) {
|
|
5825
|
+
throw new TypeError(`sandboxes[${registrationIndex}] views require published port inspection.`);
|
|
5826
|
+
}
|
|
5827
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5828
|
+
for (const [viewIndex, view] of views.entries()) {
|
|
5829
|
+
const prefix = `sandboxes[${registrationIndex}].views[${viewIndex}]`;
|
|
5830
|
+
const candidate = view;
|
|
5831
|
+
if (!isRecord5(candidate) || typeof candidate.id !== "string" || !/^[a-z0-9](?:[a-z0-9_-]{0,62})$/.test(candidate.id)) {
|
|
5832
|
+
throw new TypeError(`${prefix}.id must be a stable lowercase identifier.`);
|
|
5833
|
+
}
|
|
5834
|
+
if (ids.has(candidate.id)) throw new TypeError(`${prefix}.id is duplicated: ${candidate.id}`);
|
|
5835
|
+
ids.add(candidate.id);
|
|
5836
|
+
if (typeof candidate.label !== "string" || candidate.label.length === 0) {
|
|
5837
|
+
throw new TypeError(`${prefix}.label must be a non-empty string.`);
|
|
5838
|
+
}
|
|
5839
|
+
const source = candidate.source;
|
|
5840
|
+
if (!isRecord5(source) || source.protocol !== "novnc" || !isPort(source.containerPort) || !isRecord5(source.control) || typeof source.control.snapshot !== "function" || typeof source.control.acquireHumanControl !== "function") {
|
|
5841
|
+
throw new TypeError(`${prefix}.source must be a valid noVNC view source.`);
|
|
5842
|
+
}
|
|
5843
|
+
const port = inspector.publishedPorts?.find(
|
|
5844
|
+
(candidate2) => candidate2.containerPort === source.containerPort && candidate2.protocol === "tcp"
|
|
5845
|
+
);
|
|
5846
|
+
if (port === void 0 || !isLoopbackHost(port.host)) {
|
|
5847
|
+
throw new TypeError(`${prefix}.source must resolve to a loopback-published TCP port.`);
|
|
5848
|
+
}
|
|
5849
|
+
const access = candidate.access;
|
|
5850
|
+
if (!isRecord5(access) || access.mode !== "local" && access.mode !== "authorize") {
|
|
5851
|
+
throw new TypeError(`${prefix}.access must explicitly select local or authorize mode.`);
|
|
5852
|
+
}
|
|
5853
|
+
if (access.mode === "authorize" && typeof access.authorize !== "function") {
|
|
5854
|
+
throw new TypeError(`${prefix}.access.authorize must be a function.`);
|
|
5855
|
+
}
|
|
5856
|
+
const authentication = candidate.authentication;
|
|
5857
|
+
if (!isRecord5(authentication) || authentication.type !== "none" && authentication.type !== "password") {
|
|
5858
|
+
throw new TypeError(`${prefix}.authentication must explicitly select none or password.`);
|
|
5859
|
+
}
|
|
5860
|
+
if (authentication.type === "password" && (typeof authentication.password !== "string" || authentication.password.length === 0)) {
|
|
5861
|
+
throw new TypeError(`${prefix}.authentication.password must be a non-empty string.`);
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5975
5864
|
}
|
|
5976
|
-
function
|
|
5977
|
-
return
|
|
5865
|
+
function isInspector(value) {
|
|
5866
|
+
return isRecord5(value) && typeof value.id === "string" && value.id.length > 0 && typeof value.provider === "string" && value.provider.length > 0 && typeof value.workdir === "string" && value.workdir.length > 0;
|
|
5978
5867
|
}
|
|
5979
|
-
function
|
|
5980
|
-
return typeof
|
|
5868
|
+
function supportsFiles(inspector) {
|
|
5869
|
+
return typeof inspector.listFiles === "function" && typeof inspector.readFile === "function";
|
|
5870
|
+
}
|
|
5871
|
+
function supportsPorts(inspector) {
|
|
5872
|
+
return Array.isArray(inspector.publishedPorts);
|
|
5873
|
+
}
|
|
5874
|
+
function supportsProcesses(inspector) {
|
|
5875
|
+
return typeof inspector.listProcesses === "function" && typeof inspector.readProcessLogs === "function";
|
|
5981
5876
|
}
|
|
5982
5877
|
function sandboxRef(provider, id) {
|
|
5983
5878
|
return Buffer.from(JSON.stringify([provider, id]), "utf8").toString("base64url");
|
|
@@ -5987,22 +5882,26 @@ function copySummary(summary) {
|
|
|
5987
5882
|
...summary,
|
|
5988
5883
|
agentIds: [...summary.agentIds],
|
|
5989
5884
|
toolNames: [...summary.toolNames],
|
|
5885
|
+
views: summary.views.map((view) => ({ ...view })),
|
|
5990
5886
|
capabilities: { ...summary.capabilities }
|
|
5991
5887
|
};
|
|
5992
5888
|
}
|
|
5993
|
-
|
|
5994
|
-
return
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5889
|
+
function isLoopbackHost(host) {
|
|
5890
|
+
return host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
5891
|
+
}
|
|
5892
|
+
async function listSandboxFiles(inspector, filePath, abortSignal) {
|
|
5893
|
+
return normalizeFileEntries(await inspector.listFiles({ path: filePath, abortSignal })).sort(
|
|
5894
|
+
(left, right) => {
|
|
5895
|
+
if (left.type === "directory" && right.type !== "directory") return -1;
|
|
5896
|
+
if (left.type !== "directory" && right.type === "directory") return 1;
|
|
5897
|
+
return left.path.localeCompare(right.path);
|
|
5898
|
+
}
|
|
5899
|
+
);
|
|
5999
5900
|
}
|
|
6000
5901
|
function normalizeFileEntries(value) {
|
|
6001
|
-
if (!Array.isArray(value))
|
|
6002
|
-
throw new TypeError("Sandbox listFiles returned an invalid payload");
|
|
6003
|
-
}
|
|
5902
|
+
if (!Array.isArray(value)) throw new TypeError("Sandbox listFiles returned an invalid payload");
|
|
6004
5903
|
return value.map((item) => {
|
|
6005
|
-
if (!
|
|
5904
|
+
if (!isRecord5(item) || typeof item.path !== "string" || !isFileType(item.type)) {
|
|
6006
5905
|
throw new TypeError("Sandbox listFiles returned an invalid entry");
|
|
6007
5906
|
}
|
|
6008
5907
|
let entryPath;
|
|
@@ -6013,7 +5912,7 @@ function normalizeFileEntries(value) {
|
|
|
6013
5912
|
}
|
|
6014
5913
|
const entry = { path: entryPath, type: item.type };
|
|
6015
5914
|
if (item.size !== void 0) {
|
|
6016
|
-
if (
|
|
5915
|
+
if (!Number.isSafeInteger(item.size) || Number(item.size) < 0) {
|
|
6017
5916
|
throw new TypeError("Sandbox listFiles returned an invalid file size");
|
|
6018
5917
|
}
|
|
6019
5918
|
entry.size = item.size;
|
|
@@ -6022,11 +5921,10 @@ function normalizeFileEntries(value) {
|
|
|
6022
5921
|
});
|
|
6023
5922
|
}
|
|
6024
5923
|
function normalizePorts(value) {
|
|
6025
|
-
if (!Array.isArray(value))
|
|
5924
|
+
if (!Array.isArray(value))
|
|
6026
5925
|
throw new TypeError("Sandbox publishedPorts returned an invalid payload");
|
|
6027
|
-
}
|
|
6028
5926
|
return value.map((item) => {
|
|
6029
|
-
if (!
|
|
5927
|
+
if (!isRecord5(item) || !isPort(item.containerPort) || typeof item.host !== "string" || !isPort(item.hostPort) || typeof item.protocol !== "string") {
|
|
6030
5928
|
throw new TypeError("Sandbox publishedPorts returned an invalid entry");
|
|
6031
5929
|
}
|
|
6032
5930
|
return {
|
|
@@ -6038,17 +5936,16 @@ function normalizePorts(value) {
|
|
|
6038
5936
|
});
|
|
6039
5937
|
}
|
|
6040
5938
|
function normalizeProcesses(value) {
|
|
6041
|
-
if (!Array.isArray(value))
|
|
5939
|
+
if (!Array.isArray(value))
|
|
6042
5940
|
throw new TypeError("Sandbox listProcesses returned an invalid payload");
|
|
6043
|
-
}
|
|
6044
5941
|
return value.map((item) => {
|
|
6045
|
-
if (!
|
|
5942
|
+
if (!isRecord5(item) || typeof item.id !== "string" || typeof item.command !== "string" || !Array.isArray(item.args) || !item.args.every((arg) => typeof arg === "string") || !isProcessStatus(item.status) || typeof item.startedAt !== "string") {
|
|
6046
5943
|
throw new TypeError("Sandbox listProcesses returned an invalid entry");
|
|
6047
5944
|
}
|
|
6048
5945
|
const process2 = {
|
|
6049
5946
|
id: item.id,
|
|
6050
5947
|
command: item.command,
|
|
6051
|
-
args: item.args,
|
|
5948
|
+
args: [...item.args],
|
|
6052
5949
|
status: item.status,
|
|
6053
5950
|
startedAt: item.startedAt
|
|
6054
5951
|
};
|
|
@@ -6059,11 +5956,11 @@ function normalizeProcesses(value) {
|
|
|
6059
5956
|
});
|
|
6060
5957
|
}
|
|
6061
5958
|
function normalizeProcessLogs(value, maxBytes) {
|
|
6062
|
-
if (!
|
|
5959
|
+
if (!isRecord5(value) || !(value.stdout instanceof Uint8Array) || !(value.stderr instanceof Uint8Array) || typeof value.stdoutTruncated !== "boolean" || typeof value.stderrTruncated !== "boolean") {
|
|
6063
5960
|
throw new TypeError("Sandbox readProcessLogs returned an invalid payload");
|
|
6064
5961
|
}
|
|
6065
|
-
const stdout =
|
|
6066
|
-
const stderr =
|
|
5962
|
+
const stdout = boundedLogBytes(value.stdout, maxBytes);
|
|
5963
|
+
const stderr = boundedLogBytes(value.stderr, maxBytes);
|
|
6067
5964
|
return {
|
|
6068
5965
|
stdout: stdout.text,
|
|
6069
5966
|
stderr: stderr.text,
|
|
@@ -6071,21 +5968,13 @@ function normalizeProcessLogs(value, maxBytes) {
|
|
|
6071
5968
|
stderrTruncated: value.stderrTruncated || stderr.truncated
|
|
6072
5969
|
};
|
|
6073
5970
|
}
|
|
6074
|
-
function
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
}
|
|
6079
|
-
if (maxBytes === 0) {
|
|
6080
|
-
return { text: "", truncated: true };
|
|
6081
|
-
}
|
|
6082
|
-
let start = bytes.byteLength - maxBytes;
|
|
6083
|
-
while (start < bytes.byteLength && (bytes[start] ?? 0) >> 6 === 2) {
|
|
6084
|
-
start += 1;
|
|
6085
|
-
}
|
|
5971
|
+
function boundedLogBytes(value, maxBytes) {
|
|
5972
|
+
if (maxBytes === 0) return { text: "", truncated: value.byteLength > 0 };
|
|
5973
|
+
let start = Math.max(0, value.byteLength - maxBytes);
|
|
5974
|
+
while (start < value.byteLength && ((value[start] ?? 0) & 192) === 128) start += 1;
|
|
6086
5975
|
return {
|
|
6087
|
-
text: new TextDecoder().decode(
|
|
6088
|
-
truncated:
|
|
5976
|
+
text: new TextDecoder("utf-8", { fatal: true }).decode(value.subarray(start)),
|
|
5977
|
+
truncated: start > 0
|
|
6089
5978
|
};
|
|
6090
5979
|
}
|
|
6091
5980
|
function normalizeSandboxPath(input, allowRoot) {
|
|
@@ -6108,9 +5997,7 @@ function normalizeSandboxPath(input, allowRoot) {
|
|
|
6108
5997
|
return normalized;
|
|
6109
5998
|
}
|
|
6110
5999
|
function parseTailBytes(value) {
|
|
6111
|
-
if (value === void 0)
|
|
6112
|
-
return defaultProcessLogBytes;
|
|
6113
|
-
}
|
|
6000
|
+
if (value === void 0) return defaultProcessLogBytes;
|
|
6114
6001
|
const parsed = Number(value);
|
|
6115
6002
|
if (!Number.isInteger(parsed) || parsed < 0 || parsed > maxProcessLogBytes) {
|
|
6116
6003
|
throw new SandboxRouteError(
|
|
@@ -6131,15 +6018,13 @@ function sandboxErrorResponse(c, error) {
|
|
|
6131
6018
|
if (error instanceof SandboxRouteError) {
|
|
6132
6019
|
return errorResponse(c, error.status, error.code, error.message);
|
|
6133
6020
|
}
|
|
6134
|
-
const
|
|
6135
|
-
if (
|
|
6136
|
-
|
|
6137
|
-
}
|
|
6138
|
-
if (name === "SandboxFileSizeError") {
|
|
6021
|
+
const code = errorCode(error);
|
|
6022
|
+
if (code === "invalid_path") return errorResponse(c, 400, "bad_request", errorMessage(error));
|
|
6023
|
+
if (code === "file_too_large") {
|
|
6139
6024
|
return errorResponse(c, 413, "payload_too_large", errorMessage(error));
|
|
6140
6025
|
}
|
|
6141
|
-
if (
|
|
6142
|
-
return errorResponse(c, 409, "conflict", "Sandbox
|
|
6026
|
+
if (code === "invalid_state" || code === "sandbox_not_found") {
|
|
6027
|
+
return errorResponse(c, 409, "conflict", "Sandbox is no longer available");
|
|
6143
6028
|
}
|
|
6144
6029
|
return errorResponse(
|
|
6145
6030
|
c,
|
|
@@ -6165,6 +6050,18 @@ var SandboxRouteError = class extends Error {
|
|
|
6165
6050
|
status;
|
|
6166
6051
|
code;
|
|
6167
6052
|
};
|
|
6053
|
+
function sortedUnique(values) {
|
|
6054
|
+
if (!Array.isArray(values)) throw new TypeError("Sandbox associations must be arrays.");
|
|
6055
|
+
const result = /* @__PURE__ */ new Set();
|
|
6056
|
+
for (const value of values) {
|
|
6057
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
6058
|
+
throw new TypeError("Sandbox associations must contain non-empty strings.");
|
|
6059
|
+
}
|
|
6060
|
+
if (result.has(value)) throw new TypeError(`Sandbox association is duplicated: ${value}`);
|
|
6061
|
+
result.add(value);
|
|
6062
|
+
}
|
|
6063
|
+
return [...result].sort();
|
|
6064
|
+
}
|
|
6168
6065
|
function isFileType(value) {
|
|
6169
6066
|
return value === "file" || value === "directory" || value === "symlink" || value === "other";
|
|
6170
6067
|
}
|
|
@@ -6174,14 +6071,14 @@ function isProcessStatus(value) {
|
|
|
6174
6071
|
function isPort(value) {
|
|
6175
6072
|
return Number.isInteger(value) && Number(value) >= 1 && Number(value) <= 65535;
|
|
6176
6073
|
}
|
|
6177
|
-
function
|
|
6178
|
-
return typeof value === "object" && value !== null;
|
|
6074
|
+
function isRecord5(value) {
|
|
6075
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6179
6076
|
}
|
|
6180
|
-
function
|
|
6181
|
-
return
|
|
6077
|
+
function errorCode(error) {
|
|
6078
|
+
return isRecord5(error) && typeof error.code === "string" ? error.code : void 0;
|
|
6182
6079
|
}
|
|
6183
6080
|
function errorMessage(error) {
|
|
6184
|
-
return
|
|
6081
|
+
return isRecord5(error) && typeof error.message === "string" ? error.message : "Sandbox failed";
|
|
6185
6082
|
}
|
|
6186
6083
|
|
|
6187
6084
|
// src/runtime/sessions.ts
|
|
@@ -6905,6 +6802,7 @@ var Studio = class {
|
|
|
6905
6802
|
options;
|
|
6906
6803
|
studio;
|
|
6907
6804
|
server;
|
|
6805
|
+
websocketServer;
|
|
6908
6806
|
sigintHandler;
|
|
6909
6807
|
constructor(targets = [], options = {}) {
|
|
6910
6808
|
this.options = studioOptionsFromTargets(targets, options);
|
|
@@ -6929,9 +6827,15 @@ var Studio = class {
|
|
|
6929
6827
|
this.studio = createStudioApp(this.options);
|
|
6930
6828
|
const port = serveOptions.port ?? Number(process.env.RUNNER_PORT ?? 4021);
|
|
6931
6829
|
const serverOptions = {
|
|
6932
|
-
fetch: (request) => this.fetch(request),
|
|
6830
|
+
fetch: (request, env) => this.studio.app.fetch(request, env),
|
|
6933
6831
|
port
|
|
6934
6832
|
};
|
|
6833
|
+
this.websocketServer = new WebSocketServer({ noServer: true });
|
|
6834
|
+
serverOptions.websocket = {
|
|
6835
|
+
// @types/ws overloads are stricter than Hono's structural adapter, while Hono officially
|
|
6836
|
+
// supports ws.WebSocketServer. Keep the interop assertion at this single ownership boundary.
|
|
6837
|
+
server: this.websocketServer
|
|
6838
|
+
};
|
|
6935
6839
|
if (serveOptions.hostname !== void 0) serverOptions.hostname = serveOptions.hostname;
|
|
6936
6840
|
this.server = serve(serverOptions);
|
|
6937
6841
|
const log = serveOptions.log ?? true;
|
|
@@ -6976,6 +6880,8 @@ var Studio = class {
|
|
|
6976
6880
|
}
|
|
6977
6881
|
this.server?.close();
|
|
6978
6882
|
this.server = void 0;
|
|
6883
|
+
this.websocketServer?.close();
|
|
6884
|
+
this.websocketServer = void 0;
|
|
6979
6885
|
this.studio.close();
|
|
6980
6886
|
}
|
|
6981
6887
|
logAddress(host, port) {
|
|
@@ -7054,6 +6960,21 @@ function studioOptionsFromTargets(targets, options) {
|
|
|
7054
6960
|
if (options.models !== void 0) runtimeOptions.models = options.models;
|
|
7055
6961
|
if (options.stores !== void 0) runtimeOptions.stores = options.stores;
|
|
7056
6962
|
if (options.ui !== void 0) runtimeOptions.ui = options.ui;
|
|
6963
|
+
if (options.sandboxes !== void 0) {
|
|
6964
|
+
runtimeOptions.sandboxes = options.sandboxes.map((registration) => {
|
|
6965
|
+
let snapshot = { inspector: registration.inspector };
|
|
6966
|
+
if (registration.agentIds !== void 0) {
|
|
6967
|
+
snapshot = { ...snapshot, agentIds: [...registration.agentIds] };
|
|
6968
|
+
}
|
|
6969
|
+
if (registration.toolNames !== void 0) {
|
|
6970
|
+
snapshot = { ...snapshot, toolNames: [...registration.toolNames] };
|
|
6971
|
+
}
|
|
6972
|
+
if (registration.views !== void 0) {
|
|
6973
|
+
snapshot = { ...snapshot, views: [...registration.views] };
|
|
6974
|
+
}
|
|
6975
|
+
return snapshot;
|
|
6976
|
+
});
|
|
6977
|
+
}
|
|
7057
6978
|
return runtimeOptions;
|
|
7058
6979
|
}
|
|
7059
6980
|
function inferStudioAgents(agents, quickPrompts) {
|
|
@@ -7116,9 +7037,8 @@ function createStudioApp(options) {
|
|
|
7116
7037
|
const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
|
|
7117
7038
|
const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
|
|
7118
7039
|
const evalMap = new Map(options.evals.map((suite) => [suite.id ?? suite.name, suite]));
|
|
7119
|
-
const
|
|
7120
|
-
const
|
|
7121
|
-
const sandboxRegistry = createStudioSandboxRegistry(agents);
|
|
7040
|
+
const continuationRegistry = createStudioContinuationRegistry();
|
|
7041
|
+
const sandboxRegistry = createStudioSandboxRegistry(agents, options.sandboxes ?? []);
|
|
7122
7042
|
const memorySources = createStudioMemorySourceRegistry(agents, stores.sessions);
|
|
7123
7043
|
const app = new HonoApp();
|
|
7124
7044
|
const uiOptions = isStudioUiEnabled(options.ui) ? resolveStudioUiOptions(options.ui) : void 0;
|
|
@@ -7169,8 +7089,7 @@ function createStudioApp(options) {
|
|
|
7169
7089
|
registerMcpRoutes(app, { agentMap });
|
|
7170
7090
|
registerToolRoutes(app, { agentMap });
|
|
7171
7091
|
registerSandboxRoutes(app, sandboxRegistry);
|
|
7172
|
-
|
|
7173
|
-
registerQuestionRoutes(app, questionRuntime);
|
|
7092
|
+
const closeSandboxViews = registerSandboxViewRoutes(app, sandboxRegistry);
|
|
7174
7093
|
registerObservabilityRoutes(app, observabilityHub);
|
|
7175
7094
|
registerEvalRoutes(app, {
|
|
7176
7095
|
evals: options.evals,
|
|
@@ -7190,8 +7109,7 @@ function createStudioApp(options) {
|
|
|
7190
7109
|
agentMap,
|
|
7191
7110
|
stores,
|
|
7192
7111
|
modelRegistry,
|
|
7193
|
-
|
|
7194
|
-
questionRuntime
|
|
7112
|
+
continuationRegistry
|
|
7195
7113
|
});
|
|
7196
7114
|
if (memorySources.size > 0 || stores.sessions !== void 0) {
|
|
7197
7115
|
registerMemoryRoutes(app, {
|
|
@@ -7223,6 +7141,8 @@ function createStudioApp(options) {
|
|
|
7223
7141
|
return buildConfig(options, agents, pipelines, stores, sandboxRegistry.size);
|
|
7224
7142
|
},
|
|
7225
7143
|
close() {
|
|
7144
|
+
continuationRegistry.clear();
|
|
7145
|
+
closeSandboxViews();
|
|
7226
7146
|
}
|
|
7227
7147
|
};
|
|
7228
7148
|
if (stores.sessions !== void 0) Object.assign(studio, { sessionStore: stores.sessions });
|
|
@@ -8271,36 +8191,36 @@ function messageFromRows(row, partRows) {
|
|
|
8271
8191
|
throw new TypeError("Stored Studio message metadata is not a strict JSON object.");
|
|
8272
8192
|
}
|
|
8273
8193
|
if (row.role === "system") {
|
|
8274
|
-
|
|
8194
|
+
let message = {
|
|
8275
8195
|
role: "system",
|
|
8276
|
-
content: systemContentFromParts(parts)
|
|
8277
|
-
...metadata === void 0 ? {} : { metadata }
|
|
8196
|
+
content: systemContentFromParts(parts)
|
|
8278
8197
|
};
|
|
8198
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8279
8199
|
return message;
|
|
8280
8200
|
}
|
|
8281
8201
|
if (row.role === "user") {
|
|
8282
|
-
|
|
8202
|
+
let message = {
|
|
8283
8203
|
role: "user",
|
|
8284
|
-
content: parts
|
|
8285
|
-
...metadata === void 0 ? {} : { metadata }
|
|
8204
|
+
content: parts
|
|
8286
8205
|
};
|
|
8206
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8287
8207
|
return message;
|
|
8288
8208
|
}
|
|
8289
8209
|
if (row.role === "assistant") {
|
|
8290
|
-
|
|
8210
|
+
let message = {
|
|
8291
8211
|
role: "assistant",
|
|
8292
|
-
content: parts
|
|
8293
|
-
...row.message_id === null ? {} : { id: row.message_id },
|
|
8294
|
-
...metadata === void 0 ? {} : { metadata }
|
|
8212
|
+
content: parts
|
|
8295
8213
|
};
|
|
8214
|
+
if (row.message_id !== null) message = { ...message, id: row.message_id };
|
|
8215
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8296
8216
|
return message;
|
|
8297
8217
|
}
|
|
8298
8218
|
if (row.role === "tool") {
|
|
8299
|
-
|
|
8219
|
+
let message = {
|
|
8300
8220
|
role: "tool",
|
|
8301
|
-
content: parts
|
|
8302
|
-
...metadata === void 0 ? {} : { metadata }
|
|
8221
|
+
content: parts
|
|
8303
8222
|
};
|
|
8223
|
+
if (metadata !== void 0) message = { ...message, metadata };
|
|
8304
8224
|
return message;
|
|
8305
8225
|
}
|
|
8306
8226
|
throw new Error(`Unsupported stored message role: ${row.role}`);
|