@axlsdk/studio 0.21.1 → 0.22.1
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/middleware.js.map +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/cli.cjs
CHANGED
|
@@ -118,6 +118,19 @@ function redactStreamEvent(event, redact) {
|
|
|
118
118
|
if (!redact) return event;
|
|
119
119
|
return (0, import_axl.redactHistoricalEvent)(event);
|
|
120
120
|
}
|
|
121
|
+
function sanitizeRichInputFailure(event) {
|
|
122
|
+
const message = "Playground media input failed";
|
|
123
|
+
if (event.type === "ask_end" && !event.outcome.ok) {
|
|
124
|
+
return { ...event, outcome: { ok: false, error: message } };
|
|
125
|
+
}
|
|
126
|
+
if (event.type === "agent_call_end" && event.data.error !== void 0) {
|
|
127
|
+
return { ...event, data: { ...event.data, error: message } };
|
|
128
|
+
}
|
|
129
|
+
if (event.type === "error") {
|
|
130
|
+
return { ...event, data: { ...event.data, message } };
|
|
131
|
+
}
|
|
132
|
+
return event;
|
|
133
|
+
}
|
|
121
134
|
function redactEvalItem(item) {
|
|
122
135
|
const scrubbed = {
|
|
123
136
|
...item,
|
|
@@ -836,11 +849,11 @@ function reduceCost(acc, event) {
|
|
|
836
849
|
byWorkflow2[event.workflow] = { ...prev, executions: prev.executions + 1 };
|
|
837
850
|
return { ...acc, byWorkflow: byWorkflow2 };
|
|
838
851
|
}
|
|
839
|
-
|
|
852
|
+
const unpricedLeaf = (0, import_axl2.isUnpricedLeaf)(event);
|
|
853
|
+
if (event.cost == null && !event.tokens && !unpricedLeaf) return acc;
|
|
840
854
|
const cost = (0, import_axl2.eventCostContribution)(event);
|
|
841
855
|
if (event.type === "ask_end") return acc;
|
|
842
856
|
const tokens = event.tokens ?? {};
|
|
843
|
-
const unpricedLeaf = (0, import_axl2.isUnpricedLeaf)(event);
|
|
844
857
|
const totalTokens = event.type === "agent_call_end" ? {
|
|
845
858
|
input: acc.totalTokens.input + finite(tokens.input),
|
|
846
859
|
output: acc.totalTokens.output + finite(tokens.output),
|
|
@@ -1353,6 +1366,24 @@ var executions_default = createExecutionRoutes();
|
|
|
1353
1366
|
|
|
1354
1367
|
// src/server/routes/sessions.ts
|
|
1355
1368
|
var import_hono4 = require("hono");
|
|
1369
|
+
function describeStudioInput(content) {
|
|
1370
|
+
if (typeof content === "string") return void 0;
|
|
1371
|
+
return {
|
|
1372
|
+
parts: content.map((part) => {
|
|
1373
|
+
if (part.type === "text") return { type: "text", characters: part.text.length };
|
|
1374
|
+
const { source } = part;
|
|
1375
|
+
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;
|
|
1376
|
+
return {
|
|
1377
|
+
type: "image",
|
|
1378
|
+
source: source.type,
|
|
1379
|
+
...source.mediaType ? { mediaType: source.mediaType } : {},
|
|
1380
|
+
...bytes !== void 0 ? { bytes } : {}
|
|
1381
|
+
// URLs, provider-file references, and labels are intentionally not
|
|
1382
|
+
// included: this is a Studio-safe descriptor, not a replay payload.
|
|
1383
|
+
};
|
|
1384
|
+
})
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1356
1387
|
function createSessionRoutes(connMgr) {
|
|
1357
1388
|
const app5 = new import_hono4.Hono();
|
|
1358
1389
|
app5.get("/sessions", async (c) => {
|
|
@@ -1375,11 +1406,17 @@ function createSessionRoutes(connMgr) {
|
|
|
1375
1406
|
const id = c.req.param("id");
|
|
1376
1407
|
const history = await store.getSession(id);
|
|
1377
1408
|
const handoffHistory = await store.getSessionMeta(id, "handoffHistory");
|
|
1409
|
+
const redact = runtime.isRedactEnabled();
|
|
1410
|
+
const studioHistory = history.map((message) => {
|
|
1411
|
+
const input = describeStudioInput(message.content);
|
|
1412
|
+
if (input) return { ...message, content: input };
|
|
1413
|
+
return redact ? redactSessionHistory([message], true)[0] : message;
|
|
1414
|
+
});
|
|
1378
1415
|
return c.json({
|
|
1379
1416
|
ok: true,
|
|
1380
1417
|
data: {
|
|
1381
1418
|
id,
|
|
1382
|
-
history:
|
|
1419
|
+
history: studioHistory,
|
|
1383
1420
|
// HandoffRecord has no content fields (source/target/mode/
|
|
1384
1421
|
// timestamp/duration) — nothing to scrub.
|
|
1385
1422
|
handoffHistory: handoffHistory ?? []
|
|
@@ -15949,6 +15986,54 @@ function date4(params) {
|
|
|
15949
15986
|
// ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
15950
15987
|
config(en_default());
|
|
15951
15988
|
|
|
15989
|
+
// src/playground-image.ts
|
|
15990
|
+
var PLAYGROUND_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
15991
|
+
var PLAYGROUND_IMAGE_MAX_BASE64_CHARACTERS = Math.ceil(PLAYGROUND_IMAGE_MAX_BYTES / 3) * 4;
|
|
15992
|
+
var PLAYGROUND_IMAGE_REQUEST_MAX_BYTES = PLAYGROUND_IMAGE_MAX_BASE64_CHARACTERS + 16 * 1024;
|
|
15993
|
+
var PLAYGROUND_IMAGE_MEDIA_TYPES = [
|
|
15994
|
+
"image/png",
|
|
15995
|
+
"image/jpeg",
|
|
15996
|
+
"image/webp",
|
|
15997
|
+
"image/gif"
|
|
15998
|
+
];
|
|
15999
|
+
function isPlaygroundImageMediaType(value) {
|
|
16000
|
+
return typeof value === "string" && PLAYGROUND_IMAGE_MEDIA_TYPES.includes(value);
|
|
16001
|
+
}
|
|
16002
|
+
function base64ByteLength(value) {
|
|
16003
|
+
if (value.length === 0 || value.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
|
|
16004
|
+
return void 0;
|
|
16005
|
+
}
|
|
16006
|
+
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
16007
|
+
return value.length / 4 * 3 - padding;
|
|
16008
|
+
}
|
|
16009
|
+
function decodedPrefix(value, length) {
|
|
16010
|
+
try {
|
|
16011
|
+
const prefix = value.slice(0, Math.ceil(length / 3) * 4);
|
|
16012
|
+
const decoded = atob(prefix);
|
|
16013
|
+
return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
16014
|
+
} catch {
|
|
16015
|
+
return void 0;
|
|
16016
|
+
}
|
|
16017
|
+
}
|
|
16018
|
+
function hasMatchingImageSignature(mediaType, data) {
|
|
16019
|
+
const bytes = decodedPrefix(data, 12);
|
|
16020
|
+
if (!bytes) return false;
|
|
16021
|
+
const startsWith = (...prefix) => prefix.every((byte, index) => bytes[index] === byte);
|
|
16022
|
+
switch (mediaType) {
|
|
16023
|
+
case "image/png":
|
|
16024
|
+
return startsWith(137, 80, 78, 71, 13, 10, 26, 10);
|
|
16025
|
+
case "image/jpeg":
|
|
16026
|
+
return startsWith(255, 216, 255);
|
|
16027
|
+
case "image/gif":
|
|
16028
|
+
return startsWith(71, 73, 70, 56) && (bytes[4] === 55 || bytes[4] === 57) && bytes[5] === 97;
|
|
16029
|
+
case "image/webp":
|
|
16030
|
+
return startsWith(82, 73, 70, 70) && startsWithAt(bytes, 8, 87, 69, 66, 80);
|
|
16031
|
+
}
|
|
16032
|
+
}
|
|
16033
|
+
function startsWithAt(bytes, offset, ...prefix) {
|
|
16034
|
+
return prefix.every((byte, index) => bytes[offset + index] === byte);
|
|
16035
|
+
}
|
|
16036
|
+
|
|
15952
16037
|
// src/server/routes/playground.ts
|
|
15953
16038
|
var DEMO_SCHEMA_BY_AGENT = {
|
|
15954
16039
|
"streaming-structured-agent": external_exports.object({
|
|
@@ -15958,11 +16043,74 @@ var DEMO_SCHEMA_BY_AGENT = {
|
|
|
15958
16043
|
confidence: external_exports.number().min(0).max(1)
|
|
15959
16044
|
})
|
|
15960
16045
|
};
|
|
16046
|
+
var PlaygroundBodyTooLargeError = class extends Error {
|
|
16047
|
+
};
|
|
16048
|
+
async function readPlaygroundBody(request) {
|
|
16049
|
+
const contentLength = request.headers.get("content-length");
|
|
16050
|
+
if (contentLength !== null) {
|
|
16051
|
+
const bytes = Number(contentLength);
|
|
16052
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0 || bytes > PLAYGROUND_IMAGE_REQUEST_MAX_BYTES) {
|
|
16053
|
+
throw new PlaygroundBodyTooLargeError();
|
|
16054
|
+
}
|
|
16055
|
+
}
|
|
16056
|
+
if (!request.body) throw new SyntaxError("missing body");
|
|
16057
|
+
const reader = request.body.getReader();
|
|
16058
|
+
const chunks = [];
|
|
16059
|
+
let total = 0;
|
|
16060
|
+
try {
|
|
16061
|
+
while (true) {
|
|
16062
|
+
const next = await reader.read();
|
|
16063
|
+
if (next.done) break;
|
|
16064
|
+
total += next.value.byteLength;
|
|
16065
|
+
if (total > PLAYGROUND_IMAGE_REQUEST_MAX_BYTES) {
|
|
16066
|
+
await reader.cancel();
|
|
16067
|
+
throw new PlaygroundBodyTooLargeError();
|
|
16068
|
+
}
|
|
16069
|
+
chunks.push(next.value);
|
|
16070
|
+
}
|
|
16071
|
+
} finally {
|
|
16072
|
+
reader.releaseLock();
|
|
16073
|
+
}
|
|
16074
|
+
const body = new Uint8Array(total);
|
|
16075
|
+
let offset = 0;
|
|
16076
|
+
for (const chunk of chunks) {
|
|
16077
|
+
body.set(chunk, offset);
|
|
16078
|
+
offset += chunk.byteLength;
|
|
16079
|
+
}
|
|
16080
|
+
return JSON.parse(new TextDecoder().decode(body));
|
|
16081
|
+
}
|
|
15961
16082
|
function createPlaygroundRoutes(connMgr) {
|
|
15962
16083
|
const app5 = new import_hono11.Hono();
|
|
15963
16084
|
app5.post("/playground/chat", async (c) => {
|
|
15964
16085
|
const runtime = c.get("runtime");
|
|
15965
|
-
|
|
16086
|
+
let parsedBody;
|
|
16087
|
+
try {
|
|
16088
|
+
parsedBody = await readPlaygroundBody(c.req.raw);
|
|
16089
|
+
} catch (error48) {
|
|
16090
|
+
if (error48 instanceof PlaygroundBodyTooLargeError) {
|
|
16091
|
+
return c.json(
|
|
16092
|
+
{
|
|
16093
|
+
ok: false,
|
|
16094
|
+
error: { code: "REQUEST_TOO_LARGE", message: "playground image request is too large" }
|
|
16095
|
+
},
|
|
16096
|
+
413
|
|
16097
|
+
);
|
|
16098
|
+
}
|
|
16099
|
+
return c.json(
|
|
16100
|
+
{ ok: false, error: { code: "INVALID_INPUT", message: "request body must be valid JSON" } },
|
|
16101
|
+
400
|
|
16102
|
+
);
|
|
16103
|
+
}
|
|
16104
|
+
if (!parsedBody || typeof parsedBody !== "object" || Array.isArray(parsedBody)) {
|
|
16105
|
+
return c.json(
|
|
16106
|
+
{
|
|
16107
|
+
ok: false,
|
|
16108
|
+
error: { code: "INVALID_INPUT", message: "request body must be a JSON object" }
|
|
16109
|
+
},
|
|
16110
|
+
400
|
|
16111
|
+
);
|
|
16112
|
+
}
|
|
16113
|
+
const body = parsedBody;
|
|
15966
16114
|
if (!body.message || typeof body.message !== "string" || !body.message.trim()) {
|
|
15967
16115
|
return c.json(
|
|
15968
16116
|
{
|
|
@@ -15975,6 +16123,58 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
15975
16123
|
400
|
|
15976
16124
|
);
|
|
15977
16125
|
}
|
|
16126
|
+
const message = body.message;
|
|
16127
|
+
let image;
|
|
16128
|
+
if (body.image !== void 0) {
|
|
16129
|
+
const candidate = body.image;
|
|
16130
|
+
if (!candidate || typeof candidate !== "object" || !isPlaygroundImageMediaType(candidate.mediaType) || typeof candidate.data !== "string") {
|
|
16131
|
+
return c.json(
|
|
16132
|
+
{
|
|
16133
|
+
ok: false,
|
|
16134
|
+
error: { code: "INVALID_IMAGE", message: "image must be a supported base64 image" }
|
|
16135
|
+
},
|
|
16136
|
+
400
|
|
16137
|
+
);
|
|
16138
|
+
}
|
|
16139
|
+
if (candidate.data.length > PLAYGROUND_IMAGE_MAX_BASE64_CHARACTERS) {
|
|
16140
|
+
return c.json(
|
|
16141
|
+
{ ok: false, error: { code: "IMAGE_TOO_LARGE", message: "image must be at most 5 MiB" } },
|
|
16142
|
+
400
|
|
16143
|
+
);
|
|
16144
|
+
}
|
|
16145
|
+
const bytes = base64ByteLength(candidate.data);
|
|
16146
|
+
if (bytes === void 0) {
|
|
16147
|
+
return c.json(
|
|
16148
|
+
{
|
|
16149
|
+
ok: false,
|
|
16150
|
+
error: { code: "INVALID_IMAGE", message: "image data must be valid base64" }
|
|
16151
|
+
},
|
|
16152
|
+
400
|
|
16153
|
+
);
|
|
16154
|
+
}
|
|
16155
|
+
if (bytes > PLAYGROUND_IMAGE_MAX_BYTES) {
|
|
16156
|
+
return c.json(
|
|
16157
|
+
{
|
|
16158
|
+
ok: false,
|
|
16159
|
+
error: {
|
|
16160
|
+
code: "IMAGE_TOO_LARGE",
|
|
16161
|
+
message: `image must be at most ${PLAYGROUND_IMAGE_MAX_BYTES / 1024 / 1024} MiB`
|
|
16162
|
+
}
|
|
16163
|
+
},
|
|
16164
|
+
400
|
|
16165
|
+
);
|
|
16166
|
+
}
|
|
16167
|
+
if (!hasMatchingImageSignature(candidate.mediaType, candidate.data)) {
|
|
16168
|
+
return c.json(
|
|
16169
|
+
{
|
|
16170
|
+
ok: false,
|
|
16171
|
+
error: { code: "INVALID_IMAGE", message: "image data does not match its media type" }
|
|
16172
|
+
},
|
|
16173
|
+
400
|
|
16174
|
+
);
|
|
16175
|
+
}
|
|
16176
|
+
image = { mediaType: candidate.mediaType, data: candidate.data };
|
|
16177
|
+
}
|
|
15978
16178
|
const agents = runtime.getAgents();
|
|
15979
16179
|
const agent = body.agent ? agents.find((a) => a._name === body.agent) : agents[0];
|
|
15980
16180
|
if (!agent) {
|
|
@@ -15989,14 +16189,18 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
15989
16189
|
const sessionId = body.sessionId ?? `playground-${Date.now()}`;
|
|
15990
16190
|
const store = runtime.getStateStore();
|
|
15991
16191
|
const history = await store.getSession(sessionId);
|
|
15992
|
-
history
|
|
16192
|
+
const persistedHistory = [...history, { role: "user", content: message }];
|
|
15993
16193
|
const redactOn = runtime.isRedactEnabled();
|
|
15994
16194
|
const ctx = runtime.createContext({ sessionHistory: history });
|
|
15995
16195
|
const executionId = ctx.executionId;
|
|
15996
16196
|
void ctx.events;
|
|
15997
16197
|
const traceListener = (event) => {
|
|
15998
16198
|
if (event.executionId !== executionId) return;
|
|
15999
|
-
|
|
16199
|
+
const safeEvent = image ? sanitizeRichInputFailure(event) : event;
|
|
16200
|
+
connMgr.broadcastWithWildcard(
|
|
16201
|
+
`execution:${executionId}`,
|
|
16202
|
+
redactStreamEvent(safeEvent, redactOn)
|
|
16203
|
+
);
|
|
16000
16204
|
};
|
|
16001
16205
|
runtime.on("trace", traceListener);
|
|
16002
16206
|
(async () => {
|
|
@@ -16009,10 +16213,17 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
16009
16213
|
});
|
|
16010
16214
|
try {
|
|
16011
16215
|
const schema = DEMO_SCHEMA_BY_AGENT[agent._name];
|
|
16012
|
-
const
|
|
16216
|
+
const input = image ? [
|
|
16217
|
+
{ type: "text", text: message },
|
|
16218
|
+
{
|
|
16219
|
+
type: "image",
|
|
16220
|
+
source: { type: "base64", data: image.data, mediaType: image.mediaType }
|
|
16221
|
+
}
|
|
16222
|
+
] : message;
|
|
16223
|
+
const result = await ctx.ask(agent, input, schema ? { schema } : void 0);
|
|
16013
16224
|
const resultText = typeof result === "string" ? result : JSON.stringify(result);
|
|
16014
|
-
|
|
16015
|
-
await store.saveSession(sessionId,
|
|
16225
|
+
persistedHistory.push({ role: "assistant", content: resultText });
|
|
16226
|
+
await store.saveSession(sessionId, persistedHistory);
|
|
16016
16227
|
const doneEvent = {
|
|
16017
16228
|
...terminalFields(),
|
|
16018
16229
|
type: "done",
|
|
@@ -16026,7 +16237,11 @@ function createPlaygroundRoutes(connMgr) {
|
|
|
16026
16237
|
const errorEvent = {
|
|
16027
16238
|
...terminalFields(),
|
|
16028
16239
|
type: "error",
|
|
16029
|
-
|
|
16240
|
+
// Media payloads can make providers echo user-controlled content in
|
|
16241
|
+
// errors. The manual terminal event is deliberately fixed and safe.
|
|
16242
|
+
data: {
|
|
16243
|
+
message: image ? "Playground media input failed" : err instanceof Error ? err.message : String(err)
|
|
16244
|
+
}
|
|
16030
16245
|
};
|
|
16031
16246
|
connMgr.broadcastWithWildcard(
|
|
16032
16247
|
`execution:${executionId}`,
|
|
@@ -16419,7 +16634,7 @@ Tip: Use .mts for configs with top-level await or in projects without "type": "m
|
|
|
16419
16634
|
].join("\n");
|
|
16420
16635
|
nodeModule.register(`data:text/javascript,${encodeURIComponent(hookCode)}`);
|
|
16421
16636
|
} catch {
|
|
16422
|
-
console.warn(`[axl-studio] Warning:
|
|
16637
|
+
console.warn(`[axl-studio] Warning: this Node.js runtime does not support --conditions`);
|
|
16423
16638
|
}
|
|
16424
16639
|
}
|
|
16425
16640
|
console.log(`[axl-studio] Loading config from ${configPath}`);
|