@axlsdk/studio 0.20.0 → 0.21.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 CHANGED
@@ -68,7 +68,9 @@ Your application imports from `src/runtime.ts` directly. Studio discovers everyt
68
68
  npx @axlsdk/studio --open
69
69
  ```
70
70
 
71
- This loads your config, starts the server on `http://localhost:4400`, and opens the browser.
71
+ This loads your config and starts the server on `http://127.0.0.1:4400`. The
72
+ standalone CLI binds only to IPv4 loopback, emits no CORS headers, and rejects
73
+ non-local HTTP hosts and browser origins across both REST and WebSocket routes.
72
74
 
73
75
  ## CLI Options
74
76
 
@@ -77,19 +79,33 @@ axl-studio [options]
77
79
 
78
80
  Options:
79
81
  --port <number> Server port (default: 4400)
82
+ --dangerously-bind <ip> Bind for a loopback-published container/local-forward tunnel
80
83
  --config <path> Path to config file (default: auto-detect)
81
84
  --conditions <list> Comma-separated Node.js import conditions (e.g., development)
85
+ --read-only Disable mutating endpoints
82
86
  --open Auto-open browser
83
87
  -h, --help Show help
84
88
  ```
85
89
 
90
+ For a container or devcontainer, bind the published host port to loopback and
91
+ make only the container listener broad:
92
+
93
+ ```bash
94
+ docker run -p 127.0.0.1:4400:4400 ... axl-studio --dangerously-bind 0.0.0.0
95
+ ```
96
+
97
+ The Host/Origin guard remains active. Do not publish that port on a public or
98
+ LAN interface; standalone Studio has no user authentication.
99
+
86
100
  The `--conditions` flag is useful in monorepos where workspace packages use the `"development"` export condition to point at source instead of built dist files. Pass `--conditions development` to resolve imports through source paths.
87
101
 
88
102
  **Note:** `--conditions` only applies to ESM `import()` resolution. Transitive `require()` calls from packages without `"type": "module"` bypass the hook and use default conditions. If a dependency loads via CJS, it will still resolve from `dist/` regardless of the flag.
89
103
 
90
104
  ## Panels
91
105
 
92
- Eight panels, all live over WebSocket. The screenshots below are placeholders — see [`docs/assets/CAPTURE.md`](../../docs/assets/CAPTURE.md) for how to record the real thing.
106
+ Eight panels, all live over WebSocket. The captures below use representative seeded
107
+ runs; see [`docs/assets/CAPTURE.md`](../../docs/assets/CAPTURE.md) for how to regenerate
108
+ them.
93
109
 
94
110
  ### Agent Playground
95
111
 
@@ -101,7 +117,12 @@ Chat with any registered agent in real time. Streaming tokens, tool calls with e
101
117
 
102
118
  ### Trace Explorer
103
119
 
104
- Waterfall visualization of execution traces. Filter by type, agent, or tool; see token counts, cost per step, and duration. The Stats view shows the event-type distribution, top tools, and retry stacks.
120
+ Waterfall visualization of execution traces. Filter by type, agent, or tool; see token
121
+ counts, cost per step, and duration. Tool rows distinguish calls that succeeded,
122
+ failed, were denied, were cancelled, or were rejected before they started. Studio also
123
+ keeps older trace history readable and identifies incomplete runs instead of presenting
124
+ missing events as a complete story. The Stats view shows the event-type distribution,
125
+ top tools, and retry stacks.
105
126
 
106
127
  > **Studio vs `ctx.events`.** Studio consumes the same `AxlEvent` firehose via `runtime.on('trace', …)` — every event from every execution. Inside a workflow handler, `ctx.events` is the in-handler counterpart (per-context, scoped to the current workflow). The two coexist: Studio is for cross-execution observability and replay; `ctx.events` is for in-handler streaming UIs. See [`docs/observability.md`](../../docs/observability.md#observation-paths).
107
128
 
@@ -115,7 +136,14 @@ Track spending across agents, models, workflows, and embedders with time-window
115
136
 
116
137
  ### Eval Runner
117
138
 
118
- Run evaluations from the UI, watch items stream in, and drill into per-item scores, timing, cost, and LLM-judge reasoning. Compare two runs (baseline vs candidate) with bootstrap-CI significance, a score-distribution chart, and an item-level diff table. The History tab groups multi-run results and tracks mean scores across runs. Toggle **Capture traces** to render each item's events inline. Requires `@axlsdk/eval` as an optional peer dependency.
139
+ Run evaluations from the UI, watch items stream in, and drill into per-item scores,
140
+ timing, cost, and LLM-judge reasoning. Compare two runs (baseline vs candidate) with
141
+ bootstrap-CI significance, a score-distribution chart, and an item-level diff table.
142
+ The History tab groups multi-run results and tracks mean scores across runs. Toggle
143
+ **Capture traces** to render each item's events inline. For tool-using workflows, those
144
+ events show whether a call failed, was denied, was cancelled, or never started, giving
145
+ you better evidence for debugging and custom scorers. Studio does not turn those states
146
+ into scores automatically. Requires `@axlsdk/eval` as an optional peer dependency.
119
147
 
120
148
  Amber banners flag runs you shouldn't fully trust — a scorer that failed on too many items, a subset run, or annotations dropped by the dataset schema — so a thinned or misconfigured run can't quietly look clean.
121
149
 
@@ -165,24 +193,32 @@ const studio = createStudioMiddleware({
165
193
  runtime,
166
194
  basePath: '/studio',
167
195
  // WebSocket upgrades bypass Express middleware — always authenticate here.
168
- verifyUpgrade: (req) => {
169
- const url = new URL(req.url!, `http://${req.headers.host}`);
170
- return url.searchParams.get('token') === process.env.MY_SECRET;
171
- },
196
+ verifyUpgrade: (req) => authenticateStudioRequest(req)?.isAdmin === true,
172
197
  });
173
198
 
174
199
  const app = express();
175
- app.use('/studio', studio.handler);
200
+ const authenticateStudioHttp: express.RequestHandler = (req, res, next) => {
201
+ if (authenticateStudioRequest(req)?.isAdmin !== true) return res.sendStatus(403);
202
+ next();
203
+ };
204
+ app.use('/studio', authenticateStudioHttp, studio.handler);
176
205
  const server = app.listen(3000);
177
206
  studio.upgradeWebSocket(server); // required for live data
178
207
  ```
179
208
 
180
- Key options: `readOnly` (disable mutating endpoints for production monitoring), `evals` (lazy-load eval files), `filterTraceEvent` (per-tenant broadcast scoping), `bufferCaps` (WS replay-buffer limits). `basePath` must match your framework's mount path.
209
+ Key options: `readOnly` (disable mutating endpoints for production monitoring), `evals` (lazy-load eval files), `filterTraceEvent` (per-tenant broadcast scoping), `bufferCaps` (WS replay-buffer limits). `basePath` must match your framework's mount path. In production, `upgradeWebSocket()` refuses to attach without `verifyUpgrade`; `dangerouslyAllowUnauthenticatedWebSockets` is the explicit escape hatch only when an outer upgrade gate is independently guaranteed.
181
210
 
182
211
  **See [`docs/studio-api.md`](../../docs/studio-api.md) for the full reference:** every REST endpoint, the WebSocket protocol, the complete middleware options/return tables, NestJS/Fastify/Hono examples, host body-limit guidance, lazy eval loading, multi-tenant setup, and the internal architecture.
183
212
 
184
213
  ## Security
185
214
 
215
+ - **Treat Studio as an administrative surface, never as a public application
216
+ API.** Agent routes intentionally expose resolved system prompts, tool
217
+ descriptions, schemas, and runtime configuration. `trace.redact` protects
218
+ user/model observability content; it is not authentication and does not hide
219
+ static agent configuration.
220
+ - Protect the Studio HTTP mount with your framework's authentication and
221
+ authorization middleware.
186
222
  - **Always** provide `verifyUpgrade` — WebSocket upgrades bypass Express/Fastify/Koa middleware, so your auth middleware does **not** protect WebSocket connections.
187
223
  - Consider `readOnly: true` for production monitoring — view traces, costs, and schemas without execution capability.
188
224
  - CORS is not applied in embedded mode — the host framework owns CORS policy.
@@ -1363,7 +1363,10 @@ function createSessionRoutes(connMgr) {
1363
1363
  const body = await c.req.json();
1364
1364
  const session = runtime.session(id);
1365
1365
  const result = await session.send(body.workflow, body.message);
1366
- return c.json({ ok: true, data: { result } });
1366
+ return c.json({
1367
+ ok: true,
1368
+ data: { result: redactValue(result, runtime.isRedactEnabled()) }
1369
+ });
1367
1370
  });
1368
1371
  app5.post("/sessions/:id/stream", async (c) => {
1369
1372
  const runtime = c.get("runtime");
@@ -1372,9 +1375,13 @@ function createSessionRoutes(connMgr) {
1372
1375
  const session = runtime.session(id);
1373
1376
  const stream = await session.stream(body.workflow, body.message);
1374
1377
  const executionId = `session-${id}-${Date.now()}`;
1378
+ const redactOn = runtime.isRedactEnabled();
1375
1379
  (async () => {
1376
1380
  for await (const event of stream) {
1377
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
1381
+ connMgr.broadcastWithWildcard(
1382
+ `execution:${executionId}`,
1383
+ redactStreamEvent(event, redactOn)
1384
+ );
1378
1385
  }
1379
1386
  })();
1380
1387
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -16080,7 +16087,7 @@ function createServer(options) {
16080
16087
  emptyState: emptyEvalTrendData,
16081
16088
  windows
16082
16089
  });
16083
- if (options.cors !== false) {
16090
+ if (options.cors === true) {
16084
16091
  app5.use("*", cors());
16085
16092
  }
16086
16093
  app5.onError(errorHandler);
@@ -16239,4 +16246,4 @@ export {
16239
16246
  EvalAggregator,
16240
16247
  createServer
16241
16248
  };
16242
- //# sourceMappingURL=chunk-KQ3HBRTC.js.map
16249
+ //# sourceMappingURL=chunk-YGLWYDQ3.js.map