@axlsdk/studio 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-YGLWYDQ3.js → chunk-Z374DI7D.js} +226 -11
- package/dist/chunk-Z374DI7D.js.map +1 -0
- package/dist/cli.cjs +226 -11
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-7nGvwNtb.js +318 -0
- package/dist/client/assets/index-Bc4iQdXy.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/middleware.cjs +225 -10
- package/dist/middleware.cjs.map +1 -1
- package/dist/middleware.js +1 -1
- package/dist/server/index.cjs +225 -10
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +5 -5
- package/dist/chunk-YGLWYDQ3.js.map +0 -1
- package/dist/client/assets/index-C8R_CZcW.css +0 -1
- package/dist/client/assets/index-Dq8QQtXe.js +0 -313
package/dist/middleware.js
CHANGED
package/dist/server/index.cjs
CHANGED
|
@@ -121,6 +121,19 @@ function redactStreamEvent(event, redact) {
|
|
|
121
121
|
if (!redact) return event;
|
|
122
122
|
return (0, import_axl.redactHistoricalEvent)(event);
|
|
123
123
|
}
|
|
124
|
+
function sanitizeRichInputFailure(event) {
|
|
125
|
+
const message = "Playground media input failed";
|
|
126
|
+
if (event.type === "ask_end" && !event.outcome.ok) {
|
|
127
|
+
return { ...event, outcome: { ok: false, error: message } };
|
|
128
|
+
}
|
|
129
|
+
if (event.type === "agent_call_end" && event.data.error !== void 0) {
|
|
130
|
+
return { ...event, data: { ...event.data, error: message } };
|
|
131
|
+
}
|
|
132
|
+
if (event.type === "error") {
|
|
133
|
+
return { ...event, data: { ...event.data, message } };
|
|
134
|
+
}
|
|
135
|
+
return event;
|
|
136
|
+
}
|
|
124
137
|
function redactEvalItem(item) {
|
|
125
138
|
const scrubbed = {
|
|
126
139
|
...item,
|
|
@@ -839,11 +852,11 @@ function reduceCost(acc, event) {
|
|
|
839
852
|
byWorkflow2[event.workflow] = { ...prev, executions: prev.executions + 1 };
|
|
840
853
|
return { ...acc, byWorkflow: byWorkflow2 };
|
|
841
854
|
}
|
|
842
|
-
|
|
855
|
+
const unpricedLeaf = (0, import_axl2.isUnpricedLeaf)(event);
|
|
856
|
+
if (event.cost == null && !event.tokens && !unpricedLeaf) return acc;
|
|
843
857
|
const cost = (0, import_axl2.eventCostContribution)(event);
|
|
844
858
|
if (event.type === "ask_end") return acc;
|
|
845
859
|
const tokens = event.tokens ?? {};
|
|
846
|
-
const unpricedLeaf = (0, import_axl2.isUnpricedLeaf)(event);
|
|
847
860
|
const totalTokens = event.type === "agent_call_end" ? {
|
|
848
861
|
input: acc.totalTokens.input + finite(tokens.input),
|
|
849
862
|
output: acc.totalTokens.output + finite(tokens.output),
|
|
@@ -1356,6 +1369,24 @@ var executions_default = createExecutionRoutes();
|
|
|
1356
1369
|
|
|
1357
1370
|
// src/server/routes/sessions.ts
|
|
1358
1371
|
var import_hono4 = require("hono");
|
|
1372
|
+
function describeStudioInput(content) {
|
|
1373
|
+
if (typeof content === "string") return void 0;
|
|
1374
|
+
return {
|
|
1375
|
+
parts: content.map((part) => {
|
|
1376
|
+
if (part.type === "text") return { type: "text", characters: part.text.length };
|
|
1377
|
+
const { source } = part;
|
|
1378
|
+
const bytes = source.type === "bytes" ? source.data.byteLength : source.type === "base64" ? Math.floor(source.data.length * 3 / 4) - (source.data.endsWith("==") ? 2 : source.data.endsWith("=") ? 1 : 0) : void 0;
|
|
1379
|
+
return {
|
|
1380
|
+
type: "image",
|
|
1381
|
+
source: source.type,
|
|
1382
|
+
...source.mediaType ? { mediaType: source.mediaType } : {},
|
|
1383
|
+
...bytes !== void 0 ? { bytes } : {}
|
|
1384
|
+
// URLs, provider-file references, and labels are intentionally not
|
|
1385
|
+
// included: this is a Studio-safe descriptor, not a replay payload.
|
|
1386
|
+
};
|
|
1387
|
+
})
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1359
1390
|
function createSessionRoutes(connMgr) {
|
|
1360
1391
|
const app5 = new import_hono4.Hono();
|
|
1361
1392
|
app5.get("/sessions", async (c) => {
|
|
@@ -1378,11 +1409,17 @@ function createSessionRoutes(connMgr) {
|
|
|
1378
1409
|
const id = c.req.param("id");
|
|
1379
1410
|
const history = await store.getSession(id);
|
|
1380
1411
|
const handoffHistory = await store.getSessionMeta(id, "handoffHistory");
|
|
1412
|
+
const redact = runtime.isRedactEnabled();
|
|
1413
|
+
const studioHistory = history.map((message) => {
|
|
1414
|
+
const input = describeStudioInput(message.content);
|
|
1415
|
+
if (input) return { ...message, content: input };
|
|
1416
|
+
return redact ? redactSessionHistory([message], true)[0] : message;
|
|
1417
|
+
});
|
|
1381
1418
|
return c.json({
|
|
1382
1419
|
ok: true,
|
|
1383
1420
|
data: {
|
|
1384
1421
|
id,
|
|
1385
|
-
history:
|
|
1422
|
+
history: studioHistory,
|
|
1386
1423
|
// HandoffRecord has no content fields (source/target/mode/
|
|
1387
1424
|
// timestamp/duration) — nothing to scrub.
|
|
1388
1425
|
handoffHistory: handoffHistory ?? []
|
|
@@ -15952,6 +15989,54 @@ function date4(params) {
|
|
|
15952
15989
|
// ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
15953
15990
|
config(en_default());
|
|
15954
15991
|
|
|
15992
|
+
// src/playground-image.ts
|
|
15993
|
+
var PLAYGROUND_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
15994
|
+
var PLAYGROUND_IMAGE_MAX_BASE64_CHARACTERS = Math.ceil(PLAYGROUND_IMAGE_MAX_BYTES / 3) * 4;
|
|
15995
|
+
var PLAYGROUND_IMAGE_REQUEST_MAX_BYTES = PLAYGROUND_IMAGE_MAX_BASE64_CHARACTERS + 16 * 1024;
|
|
15996
|
+
var PLAYGROUND_IMAGE_MEDIA_TYPES = [
|
|
15997
|
+
"image/png",
|
|
15998
|
+
"image/jpeg",
|
|
15999
|
+
"image/webp",
|
|
16000
|
+
"image/gif"
|
|
16001
|
+
];
|
|
16002
|
+
function isPlaygroundImageMediaType(value) {
|
|
16003
|
+
return typeof value === "string" && PLAYGROUND_IMAGE_MEDIA_TYPES.includes(value);
|
|
16004
|
+
}
|
|
16005
|
+
function base64ByteLength(value) {
|
|
16006
|
+
if (value.length === 0 || value.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
|
|
16007
|
+
return void 0;
|
|
16008
|
+
}
|
|
16009
|
+
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
16010
|
+
return value.length / 4 * 3 - padding;
|
|
16011
|
+
}
|
|
16012
|
+
function decodedPrefix(value, length) {
|
|
16013
|
+
try {
|
|
16014
|
+
const prefix = value.slice(0, Math.ceil(length / 3) * 4);
|
|
16015
|
+
const decoded = atob(prefix);
|
|
16016
|
+
return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
16017
|
+
} catch {
|
|
16018
|
+
return void 0;
|
|
16019
|
+
}
|
|
16020
|
+
}
|
|
16021
|
+
function hasMatchingImageSignature(mediaType, data) {
|
|
16022
|
+
const bytes = decodedPrefix(data, 12);
|
|
16023
|
+
if (!bytes) return false;
|
|
16024
|
+
const startsWith = (...prefix) => prefix.every((byte, index) => bytes[index] === byte);
|
|
16025
|
+
switch (mediaType) {
|
|
16026
|
+
case "image/png":
|
|
16027
|
+
return startsWith(137, 80, 78, 71, 13, 10, 26, 10);
|
|
16028
|
+
case "image/jpeg":
|
|
16029
|
+
return startsWith(255, 216, 255);
|
|
16030
|
+
case "image/gif":
|
|
16031
|
+
return startsWith(71, 73, 70, 56) && (bytes[4] === 55 || bytes[4] === 57) && bytes[5] === 97;
|
|
16032
|
+
case "image/webp":
|
|
16033
|
+
return startsWith(82, 73, 70, 70) && startsWithAt(bytes, 8, 87, 69, 66, 80);
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
function startsWithAt(bytes, offset, ...prefix) {
|
|
16037
|
+
return prefix.every((byte, index) => bytes[offset + index] === byte);
|
|
16038
|
+
}
|
|
16039
|
+
|
|
15955
16040
|
// src/server/routes/playground.ts
|
|
15956
16041
|
var DEMO_SCHEMA_BY_AGENT = {
|
|
15957
16042
|
"streaming-structured-agent": external_exports.object({
|
|
@@ -15961,11 +16046,74 @@ var DEMO_SCHEMA_BY_AGENT = {
|
|
|
15961
16046
|
confidence: external_exports.number().min(0).max(1)
|
|
15962
16047
|
})
|
|
15963
16048
|
};
|
|
16049
|
+
var PlaygroundBodyTooLargeError = class extends Error {
|
|
16050
|
+
};
|
|
16051
|
+
async function readPlaygroundBody(request) {
|
|
16052
|
+
const contentLength = request.headers.get("content-length");
|
|
16053
|
+
if (contentLength !== null) {
|
|
16054
|
+
const bytes = Number(contentLength);
|
|
16055
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0 || bytes > PLAYGROUND_IMAGE_REQUEST_MAX_BYTES) {
|
|
16056
|
+
throw new PlaygroundBodyTooLargeError();
|
|
16057
|
+
}
|
|
16058
|
+
}
|
|
16059
|
+
if (!request.body) throw new SyntaxError("missing body");
|
|
16060
|
+
const reader = request.body.getReader();
|
|
16061
|
+
const chunks = [];
|
|
16062
|
+
let total = 0;
|
|
16063
|
+
try {
|
|
16064
|
+
while (true) {
|
|
16065
|
+
const next = await reader.read();
|
|
16066
|
+
if (next.done) break;
|
|
16067
|
+
total += next.value.byteLength;
|
|
16068
|
+
if (total > PLAYGROUND_IMAGE_REQUEST_MAX_BYTES) {
|
|
16069
|
+
await reader.cancel();
|
|
16070
|
+
throw new PlaygroundBodyTooLargeError();
|
|
16071
|
+
}
|
|
16072
|
+
chunks.push(next.value);
|
|
16073
|
+
}
|
|
16074
|
+
} finally {
|
|
16075
|
+
reader.releaseLock();
|
|
16076
|
+
}
|
|
16077
|
+
const body = new Uint8Array(total);
|
|
16078
|
+
let offset = 0;
|
|
16079
|
+
for (const chunk of chunks) {
|
|
16080
|
+
body.set(chunk, offset);
|
|
16081
|
+
offset += chunk.byteLength;
|
|
16082
|
+
}
|
|
16083
|
+
return JSON.parse(new TextDecoder().decode(body));
|
|
16084
|
+
}
|
|
15964
16085
|
function createPlaygroundRoutes(connMgr) {
|
|
15965
16086
|
const app5 = new import_hono11.Hono();
|
|
15966
16087
|
app5.post("/playground/chat", async (c) => {
|
|
15967
16088
|
const runtime = c.get("runtime");
|
|
15968
|
-
|
|
16089
|
+
let parsedBody;
|
|
16090
|
+
try {
|
|
16091
|
+
parsedBody = await readPlaygroundBody(c.req.raw);
|
|
16092
|
+
} catch (error48) {
|
|
16093
|
+
if (error48 instanceof PlaygroundBodyTooLargeError) {
|
|
16094
|
+
return c.json(
|
|
16095
|
+
{
|
|
16096
|
+
ok: false,
|
|
16097
|
+
error: { code: "REQUEST_TOO_LARGE", message: "playground image request is too large" }
|
|
16098
|
+
},
|
|
16099
|
+
413
|
|
16100
|
+
);
|
|
16101
|
+
}
|
|
16102
|
+
return c.json(
|
|
16103
|
+
{ ok: false, error: { code: "INVALID_INPUT", message: "request body must be valid JSON" } },
|
|
16104
|
+
400
|
|
16105
|
+
);
|
|
16106
|
+
}
|
|
16107
|
+
if (!parsedBody || typeof parsedBody !== "object" || Array.isArray(parsedBody)) {
|
|
16108
|
+
return c.json(
|
|
16109
|
+
{
|
|
16110
|
+
ok: false,
|
|
16111
|
+
error: { code: "INVALID_INPUT", message: "request body must be a JSON object" }
|
|
16112
|
+
},
|
|
16113
|
+
400
|
|
16114
|
+
);
|
|
16115
|
+
}
|
|
16116
|
+
const body = parsedBody;
|
|
15969
16117
|
if (!body.message || typeof body.message !== "string" || !body.message.trim()) {
|
|
15970
16118
|
return c.json(
|
|
15971
16119
|
{
|
|
@@ -15978,6 +16126,58 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
15978
16126
|
400
|
|
15979
16127
|
);
|
|
15980
16128
|
}
|
|
16129
|
+
const message = body.message;
|
|
16130
|
+
let image;
|
|
16131
|
+
if (body.image !== void 0) {
|
|
16132
|
+
const candidate = body.image;
|
|
16133
|
+
if (!candidate || typeof candidate !== "object" || !isPlaygroundImageMediaType(candidate.mediaType) || typeof candidate.data !== "string") {
|
|
16134
|
+
return c.json(
|
|
16135
|
+
{
|
|
16136
|
+
ok: false,
|
|
16137
|
+
error: { code: "INVALID_IMAGE", message: "image must be a supported base64 image" }
|
|
16138
|
+
},
|
|
16139
|
+
400
|
|
16140
|
+
);
|
|
16141
|
+
}
|
|
16142
|
+
if (candidate.data.length > PLAYGROUND_IMAGE_MAX_BASE64_CHARACTERS) {
|
|
16143
|
+
return c.json(
|
|
16144
|
+
{ ok: false, error: { code: "IMAGE_TOO_LARGE", message: "image must be at most 5 MiB" } },
|
|
16145
|
+
400
|
|
16146
|
+
);
|
|
16147
|
+
}
|
|
16148
|
+
const bytes = base64ByteLength(candidate.data);
|
|
16149
|
+
if (bytes === void 0) {
|
|
16150
|
+
return c.json(
|
|
16151
|
+
{
|
|
16152
|
+
ok: false,
|
|
16153
|
+
error: { code: "INVALID_IMAGE", message: "image data must be valid base64" }
|
|
16154
|
+
},
|
|
16155
|
+
400
|
|
16156
|
+
);
|
|
16157
|
+
}
|
|
16158
|
+
if (bytes > PLAYGROUND_IMAGE_MAX_BYTES) {
|
|
16159
|
+
return c.json(
|
|
16160
|
+
{
|
|
16161
|
+
ok: false,
|
|
16162
|
+
error: {
|
|
16163
|
+
code: "IMAGE_TOO_LARGE",
|
|
16164
|
+
message: `image must be at most ${PLAYGROUND_IMAGE_MAX_BYTES / 1024 / 1024} MiB`
|
|
16165
|
+
}
|
|
16166
|
+
},
|
|
16167
|
+
400
|
|
16168
|
+
);
|
|
16169
|
+
}
|
|
16170
|
+
if (!hasMatchingImageSignature(candidate.mediaType, candidate.data)) {
|
|
16171
|
+
return c.json(
|
|
16172
|
+
{
|
|
16173
|
+
ok: false,
|
|
16174
|
+
error: { code: "INVALID_IMAGE", message: "image data does not match its media type" }
|
|
16175
|
+
},
|
|
16176
|
+
400
|
|
16177
|
+
);
|
|
16178
|
+
}
|
|
16179
|
+
image = { mediaType: candidate.mediaType, data: candidate.data };
|
|
16180
|
+
}
|
|
15981
16181
|
const agents = runtime.getAgents();
|
|
15982
16182
|
const agent = body.agent ? agents.find((a) => a._name === body.agent) : agents[0];
|
|
15983
16183
|
if (!agent) {
|
|
@@ -15992,14 +16192,18 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
15992
16192
|
const sessionId = body.sessionId ?? `playground-${Date.now()}`;
|
|
15993
16193
|
const store = runtime.getStateStore();
|
|
15994
16194
|
const history = await store.getSession(sessionId);
|
|
15995
|
-
history
|
|
16195
|
+
const persistedHistory = [...history, { role: "user", content: message }];
|
|
15996
16196
|
const redactOn = runtime.isRedactEnabled();
|
|
15997
16197
|
const ctx = runtime.createContext({ sessionHistory: history });
|
|
15998
16198
|
const executionId = ctx.executionId;
|
|
15999
16199
|
void ctx.events;
|
|
16000
16200
|
const traceListener = (event) => {
|
|
16001
16201
|
if (event.executionId !== executionId) return;
|
|
16002
|
-
|
|
16202
|
+
const safeEvent = image ? sanitizeRichInputFailure(event) : event;
|
|
16203
|
+
connMgr.broadcastWithWildcard(
|
|
16204
|
+
`execution:${executionId}`,
|
|
16205
|
+
redactStreamEvent(safeEvent, redactOn)
|
|
16206
|
+
);
|
|
16003
16207
|
};
|
|
16004
16208
|
runtime.on("trace", traceListener);
|
|
16005
16209
|
(async () => {
|
|
@@ -16012,10 +16216,17 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
16012
16216
|
});
|
|
16013
16217
|
try {
|
|
16014
16218
|
const schema = DEMO_SCHEMA_BY_AGENT[agent._name];
|
|
16015
|
-
const
|
|
16219
|
+
const input = image ? [
|
|
16220
|
+
{ type: "text", text: message },
|
|
16221
|
+
{
|
|
16222
|
+
type: "image",
|
|
16223
|
+
source: { type: "base64", data: image.data, mediaType: image.mediaType }
|
|
16224
|
+
}
|
|
16225
|
+
] : message;
|
|
16226
|
+
const result = await ctx.ask(agent, input, schema ? { schema } : void 0);
|
|
16016
16227
|
const resultText = typeof result === "string" ? result : JSON.stringify(result);
|
|
16017
|
-
|
|
16018
|
-
await store.saveSession(sessionId,
|
|
16228
|
+
persistedHistory.push({ role: "assistant", content: resultText });
|
|
16229
|
+
await store.saveSession(sessionId, persistedHistory);
|
|
16019
16230
|
const doneEvent = {
|
|
16020
16231
|
...terminalFields(),
|
|
16021
16232
|
type: "done",
|
|
@@ -16029,7 +16240,11 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
16029
16240
|
const errorEvent = {
|
|
16030
16241
|
...terminalFields(),
|
|
16031
16242
|
type: "error",
|
|
16032
|
-
|
|
16243
|
+
// Media payloads can make providers echo user-controlled content in
|
|
16244
|
+
// errors. The manual terminal event is deliberately fixed and safe.
|
|
16245
|
+
data: {
|
|
16246
|
+
message: image ? "Playground media input failed" : err instanceof Error ? err.message : String(err)
|
|
16247
|
+
}
|
|
16033
16248
|
};
|
|
16034
16249
|
connMgr.broadcastWithWildcard(
|
|
16035
16250
|
`execution:${executionId}`,
|