@axlsdk/studio 0.13.5 → 0.13.6

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
@@ -148,6 +148,7 @@ Studio exposes a REST API that the SPA consumes. You can also call these directl
148
148
  | `GET /api/evals/history` | List eval run history |
149
149
  | `POST /api/evals/:name/run` | Run a registered eval by name |
150
150
  | `POST /api/evals/compare` | Compare two eval results |
151
+ | `POST /api/playground/chat` | Chat with an agent directly (no workflow required). Accepts `{ message, agent?, sessionId? }`. Streams results via WebSocket |
151
152
  | `GET /api/decisions` | List pending decisions |
152
153
  | `POST /api/decisions/:id/resolve` | Resolve a pending decision |
153
154
 
@@ -162,7 +163,7 @@ Single endpoint at `ws://localhost:4400/ws` with channel multiplexing:
162
163
  { "type": "event", "channel": "trace:abc-123", "data": { ... } }
163
164
  ```
164
165
 
165
- Channels: `execution:{id}`, `trace:{id}`, `trace:*`, `costs`, `decisions`.
166
+ Channels: `execution:{id}`, `trace:{id}`, `trace:*`, `costs`, `decisions`. Execution channels have replay buffering — late subscribers receive the full event history (capped at 500 events, cleaned up 30s after stream completes).
166
167
 
167
168
  ## Embeddable Middleware
168
169
 
@@ -394,7 +395,7 @@ src/
394
395
  routes/ One file per resource (health, workflows, agents, tools, etc.)
395
396
  ws/
396
397
  handler.ts WebSocket message routing (Hono adapter)
397
- connection-manager.ts Channel subscriptions + broadcast (BroadcastTarget)
398
+ connection-manager.ts Channel subscriptions + broadcast (BroadcastTarget) + replay buffer for execution channels
398
399
  protocol.ts Shared WS protocol: handleWsMessage(), channel validation
399
400
  client/
400
401
  App.tsx React SPA — sidebar + 8 panel routes
@@ -32,11 +32,18 @@ async function errorHandler(c, next) {
32
32
  }
33
33
 
34
34
  // src/server/ws/connection-manager.ts
35
+ function isBufferedChannel(channel) {
36
+ return channel.startsWith("execution:");
37
+ }
38
+ var BUFFER_TTL_MS = 3e4;
39
+ var MAX_BUFFER_EVENTS = 500;
35
40
  var ConnectionManager = class {
36
41
  /** channel -> set of WS connections */
37
42
  channels = /* @__PURE__ */ new Map();
38
43
  /** ws -> set of subscribed channels (for cleanup) */
39
44
  connections = /* @__PURE__ */ new Map();
45
+ /** channel -> replay buffer for execution streams */
46
+ buffers = /* @__PURE__ */ new Map();
40
47
  maxConnections = 100;
41
48
  /** Register a new WS connection. */
42
49
  add(ws) {
@@ -59,7 +66,7 @@ var ConnectionManager = class {
59
66
  }
60
67
  this.connections.delete(ws);
61
68
  }
62
- /** Subscribe a connection to a channel. No-op if the connection was not added. */
69
+ /** Subscribe a connection to a channel. Replays buffered events for execution channels. */
63
70
  subscribe(ws, channel) {
64
71
  if (!this.connections.has(ws)) return;
65
72
  let subs = this.channels.get(channel);
@@ -69,6 +76,17 @@ var ConnectionManager = class {
69
76
  }
70
77
  subs.add(ws);
71
78
  this.connections.get(ws).add(channel);
79
+ const buffer = this.buffers.get(channel);
80
+ if (buffer) {
81
+ for (const msg of buffer.events) {
82
+ try {
83
+ ws.send(msg);
84
+ } catch {
85
+ this.remove(ws);
86
+ return;
87
+ }
88
+ }
89
+ }
72
90
  }
73
91
  /** Unsubscribe a connection from a channel. */
74
92
  unsubscribe(ws, channel) {
@@ -78,11 +96,30 @@ var ConnectionManager = class {
78
96
  }
79
97
  this.connections.get(ws)?.delete(channel);
80
98
  }
81
- /** Broadcast data to all subscribers of a channel. */
99
+ /** Broadcast data to all subscribers of a channel. Buffers events for execution channels. */
82
100
  broadcast(channel, data) {
101
+ const msg = JSON.stringify({ type: "event", channel, data });
102
+ if (isBufferedChannel(channel)) {
103
+ let buffer = this.buffers.get(channel);
104
+ if (!buffer) {
105
+ buffer = { events: [], complete: false };
106
+ this.buffers.set(channel, buffer);
107
+ }
108
+ const event = data;
109
+ const isTerminal = event.type === "done" || event.type === "error";
110
+ if (buffer.events.length < MAX_BUFFER_EVENTS || isTerminal) {
111
+ buffer.events.push(msg);
112
+ }
113
+ if (isTerminal) {
114
+ buffer.complete = true;
115
+ if (buffer.timer) clearTimeout(buffer.timer);
116
+ buffer.timer = setTimeout(() => {
117
+ this.buffers.delete(channel);
118
+ }, BUFFER_TTL_MS);
119
+ }
120
+ }
83
121
  const subs = this.channels.get(channel);
84
122
  if (!subs || subs.size === 0) return;
85
- const msg = JSON.stringify({ type: "event", channel, data });
86
123
  for (const ws of [...subs]) {
87
124
  try {
88
125
  ws.send(msg);
@@ -109,13 +146,17 @@ var ConnectionManager = class {
109
146
  }
110
147
  }
111
148
  }
112
- /** Close all connections and clear all state. Used during shutdown. */
149
+ /** Close all connections, clear all state and buffers. Used during shutdown. */
113
150
  closeAll() {
114
151
  for (const ws of this.connections.keys()) {
115
152
  ws.close?.();
116
153
  }
154
+ for (const buffer of this.buffers.values()) {
155
+ if (buffer.timer) clearTimeout(buffer.timer);
156
+ }
117
157
  this.connections.clear();
118
158
  this.channels.clear();
159
+ this.buffers.clear();
119
160
  }
120
161
  /** Get the number of active connections. */
121
162
  get connectionCount() {
@@ -320,15 +361,8 @@ function createWorkflowRoutes(connMgr) {
320
361
  const stream = runtime.stream(name, body.input ?? {}, { metadata: body.metadata });
321
362
  const executionId = `stream-${Date.now()}`;
322
363
  (async () => {
323
- try {
324
- for await (const event of stream) {
325
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
326
- }
327
- } catch (err) {
328
- connMgr.broadcastWithWildcard(`execution:${executionId}`, {
329
- type: "error",
330
- message: err instanceof Error ? err.message : "Stream error"
331
- });
364
+ for await (const event of stream) {
365
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
332
366
  }
333
367
  })();
334
368
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -409,15 +443,8 @@ function createSessionRoutes(connMgr) {
409
443
  const stream = await session.stream(body.workflow, body.message);
410
444
  const executionId = `session-${id}-${Date.now()}`;
411
445
  (async () => {
412
- try {
413
- for await (const event of stream) {
414
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
415
- }
416
- } catch (err) {
417
- connMgr.broadcastWithWildcard(`execution:${executionId}`, {
418
- type: "error",
419
- message: err instanceof Error ? err.message : "Stream error"
420
- });
446
+ for await (const event of stream) {
447
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
421
448
  }
422
449
  })();
423
450
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -728,26 +755,57 @@ function createPlaygroundRoutes(connMgr) {
728
755
  app7.post("/playground/chat", async (c) => {
729
756
  const runtime = c.get("runtime");
730
757
  const body = await c.req.json();
731
- const workflowName = body.workflow ?? runtime.getWorkflowNames()[0];
732
- if (!workflowName) {
758
+ if (!body.message || typeof body.message !== "string" || !body.message.trim()) {
759
+ return c.json(
760
+ {
761
+ ok: false,
762
+ error: {
763
+ code: "INVALID_INPUT",
764
+ message: "message is required and must be a non-empty string"
765
+ }
766
+ },
767
+ 400
768
+ );
769
+ }
770
+ const agents = runtime.getAgents();
771
+ const agent = body.agent ? agents.find((a) => a._name === body.agent) : agents[0];
772
+ if (!agent) {
733
773
  return c.json(
734
- { ok: false, error: { code: "NO_WORKFLOW", message: "No workflows registered" } },
774
+ {
775
+ ok: false,
776
+ error: { code: "NO_AGENT", message: `Agent "${body.agent ?? ""}" not found` }
777
+ },
735
778
  400
736
779
  );
737
780
  }
738
781
  const sessionId = body.sessionId ?? `playground-${Date.now()}`;
739
- const session = runtime.session(sessionId);
740
- const stream = await session.stream(workflowName, body.message);
741
782
  const executionId = `playground-${sessionId}-${Date.now()}`;
783
+ const store = runtime.getStateStore();
784
+ const history = await store.getSession(sessionId);
785
+ history.push({ role: "user", content: body.message });
786
+ const ctx = runtime.createContext({
787
+ sessionHistory: history,
788
+ onToken: (token) => {
789
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, {
790
+ type: "token",
791
+ data: token
792
+ });
793
+ }
794
+ });
742
795
  (async () => {
743
796
  try {
744
- for await (const event of stream) {
745
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
746
- }
797
+ const result = await ctx.ask(agent, body.message);
798
+ const resultText = typeof result === "string" ? result : JSON.stringify(result);
799
+ history.push({ role: "assistant", content: resultText });
800
+ await store.saveSession(sessionId, history);
801
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, {
802
+ type: "done",
803
+ data: resultText
804
+ });
747
805
  } catch (err) {
748
806
  connMgr.broadcastWithWildcard(`execution:${executionId}`, {
749
807
  type: "error",
750
- message: err instanceof Error ? err.message : "Stream error"
808
+ message: err instanceof Error ? err.message : String(err)
751
809
  });
752
810
  }
753
811
  })();
@@ -859,19 +917,24 @@ function createServer(options) {
859
917
  app7.use("/*", async (c, next) => {
860
918
  const reqPath = c.req.path;
861
919
  const resolved = basePath && reqPath.startsWith(basePath) ? reqPath.slice(basePath.length) || "/" : reqPath;
862
- if (resolved === "/" || resolved === "/index.html") {
920
+ if (resolved === "/" || resolved === "/index.html" || resolved === "/ws") {
863
921
  return next();
864
922
  }
865
923
  return staticHandler(c, next);
866
924
  });
867
925
  if (spaHtml) {
868
- app7.get("*", (c) => c.html(spaHtml));
926
+ app7.get("*", async (c, next) => {
927
+ const resolved = basePath && c.req.path.startsWith(basePath) ? c.req.path.slice(basePath.length) || "/" : c.req.path;
928
+ if (resolved === "/ws") return next();
929
+ return c.html(spaHtml);
930
+ });
869
931
  }
870
932
  }
871
933
  return {
872
934
  app: app7,
873
935
  connMgr,
874
936
  costAggregator,
937
+ /** Create WS handlers. Call before registering static/SPA routes are reached. */
875
938
  createWsHandlers: () => createWsHandlers(connMgr),
876
939
  traceListener
877
940
  };
@@ -883,4 +946,4 @@ export {
883
946
  CostAggregator,
884
947
  createServer
885
948
  };
886
- //# sourceMappingURL=chunk-NU6255VY.js.map
949
+ //# sourceMappingURL=chunk-6MWSGTJ4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/index.ts","../src/server/middleware/error-handler.ts","../src/server/ws/connection-manager.ts","../src/server/ws/protocol.ts","../src/server/ws/handler.ts","../src/server/cost-aggregator.ts","../src/server/routes/health.ts","../src/server/routes/workflows.ts","../src/server/routes/executions.ts","../src/server/routes/sessions.ts","../src/server/routes/agents.ts","../src/server/routes/tools.ts","../src/server/routes/memory.ts","../src/server/routes/decisions.ts","../src/server/routes/costs.ts","../src/server/routes/evals.ts","../src/server/routes/playground.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport type { AxlRuntime } from '@axlsdk/axl';\nimport type { StudioEnv } from './types.js';\nimport { errorHandler } from './middleware/error-handler.js';\nimport { ConnectionManager } from './ws/connection-manager.js';\nimport { createWsHandlers } from './ws/handler.js';\nimport { CostAggregator } from './cost-aggregator.js';\nimport healthRoutes from './routes/health.js';\nimport { createWorkflowRoutes } from './routes/workflows.js';\nimport executionRoutes from './routes/executions.js';\nimport { createSessionRoutes } from './routes/sessions.js';\nimport agentRoutes from './routes/agents.js';\nimport toolRoutes from './routes/tools.js';\nimport memoryRoutes from './routes/memory.js';\nimport decisionRoutes from './routes/decisions.js';\nimport { createCostRoutes } from './routes/costs.js';\nimport { createEvalRoutes } from './routes/evals.js';\nimport { createPlaygroundRoutes } from './routes/playground.js';\n\nexport type { StudioEnv } from './types.js';\nexport { ConnectionManager } from './ws/connection-manager.js';\nexport type { BroadcastTarget } from './ws/connection-manager.js';\nexport { CostAggregator } from './cost-aggregator.js';\n\nexport type CreateServerOptions = {\n runtime: AxlRuntime;\n /** Root path for serving pre-built SPA static assets. */\n staticRoot?: string;\n /** Base URL path for client-side routing and API calls. Injected into index.html at serve time. */\n basePath?: string;\n /** When true, disable all mutating API endpoints. */\n readOnly?: boolean;\n /** Apply CORS headers. Default: true (standalone CLI). Set false for embedded middleware. */\n cors?: boolean;\n /** Lazy eval file loader. Called before eval routes access the runtime's registered evals. */\n evalLoader?: () => Promise<void>;\n};\n\nexport function createServer(options: CreateServerOptions) {\n const { runtime, staticRoot, basePath = '', readOnly = false } = options;\n const app = new Hono<StudioEnv>();\n const connMgr = new ConnectionManager();\n const costAggregator = new CostAggregator(connMgr);\n\n // ── Middleware ──────────────────────────────────────────────────────\n if (options.cors !== false) {\n app.use('*', cors());\n }\n app.use('*', errorHandler);\n app.use('*', async (c, next) => {\n c.set('runtime', runtime);\n await next();\n });\n\n // ── Read-only mode ──────────────────────────────────────────────────\n if (readOnly) {\n const blocked = [\n 'POST /api/workflows',\n 'POST /api/executions',\n 'POST /api/sessions',\n 'DELETE /api/sessions',\n 'PUT /api/memory',\n 'DELETE /api/memory',\n 'POST /api/decisions',\n 'POST /api/costs',\n 'POST /api/tools',\n 'POST /api/evals',\n 'POST /api/playground',\n ];\n app.use('/api/*', async (c, next) => {\n // c.req.path returns the full path including any parent route prefix\n // (e.g., /studio/api/workflows when mounted via app.route('/studio', ...)).\n // Extract just the /api/... portion for matching.\n const apiIdx = c.req.path.indexOf('/api/');\n const apiPath = apiIdx >= 0 ? c.req.path.slice(apiIdx) : c.req.path;\n const key = `${c.req.method} ${apiPath}`;\n if (blocked.some((b) => key.startsWith(b))) {\n return c.json(\n {\n ok: false,\n error: { code: 'READ_ONLY', message: 'Studio is mounted in read-only mode' },\n },\n 405,\n );\n }\n await next();\n });\n }\n\n // ── API Routes ─────────────────────────────────────────────────────\n const api = new Hono<StudioEnv>();\n api.route('/', healthRoutes);\n api.route('/', createWorkflowRoutes(connMgr));\n api.route('/', executionRoutes);\n api.route('/', createSessionRoutes(connMgr));\n api.route('/', agentRoutes);\n api.route('/', toolRoutes);\n api.route('/', memoryRoutes);\n api.route('/', decisionRoutes);\n api.route('/', createCostRoutes(costAggregator));\n api.route('/', createEvalRoutes(options.evalLoader));\n api.route('/', createPlaygroundRoutes(connMgr));\n\n app.route('/api', api);\n\n // ── Trace event bridging ───────────────────────────────────────────\n const traceListener = (event: unknown) => {\n const traceEvent = event as {\n executionId?: string;\n type?: string;\n agent?: string;\n model?: string;\n workflow?: string;\n cost?: number;\n tokens?: { input?: number; output?: number; reasoning?: number };\n };\n\n // Broadcast to trace channels\n if (traceEvent.executionId) {\n connMgr.broadcastWithWildcard(`trace:${traceEvent.executionId}`, traceEvent);\n }\n\n // Feed cost aggregator\n costAggregator.onTrace(traceEvent);\n\n // Broadcast pending decisions\n if (traceEvent.type === 'await_human') {\n connMgr.broadcast('decisions', traceEvent);\n }\n };\n runtime.on('trace', traceListener);\n\n // ── Static SPA serving (production) ────────────────────────────────\n if (staticRoot) {\n // Read index.html once at startup. When basePath is set, inject the <base>\n // tag and runtime config so asset paths and client-side routing work\n // regardless of whether the mount URL has a trailing slash.\n const indexPath = resolve(staticRoot, 'index.html');\n let spaHtml: string | undefined;\n\n if (!existsSync(indexPath)) {\n console.warn(`[axl-studio] index.html not found at ${indexPath}`);\n } else {\n const rawHtml = readFileSync(indexPath, 'utf-8');\n\n if (basePath) {\n // Escape '<' to prevent </script> in basePath from breaking out of\n // the script tag. JSON.stringify alone is insufficient because the\n // HTML parser processes </script> before JavaScript runs.\n const safeBasePath = JSON.stringify(basePath).replace(/</g, '\\\\u003c');\n const injected = rawHtml.replace(\n '<head>',\n `<head>\\n<base href=\"${basePath}/\">\\n` +\n `<script>window.__AXL_STUDIO_BASE__=${safeBasePath}</script>`,\n );\n\n if (injected === rawHtml) {\n console.warn(\n '[axl-studio] Could not inject basePath into index.html — ' +\n '<head> tag not found. The SPA may not route correctly.',\n );\n }\n spaHtml = injected;\n } else {\n spaHtml = rawHtml;\n }\n }\n\n // Serve static assets (JS, CSS, images). index.html is excluded so the\n // SPA fallback below always serves the version with basePath injection.\n // Without this guard, serveStatic would serve the raw index.html for root\n // requests (/ or /index.html), missing <base> and __AXL_STUDIO_BASE__.\n const staticHandler = serveStatic({\n root: staticRoot,\n rewriteRequestPath: basePath\n ? (path) => (path.startsWith(basePath) ? path.slice(basePath.length) || '/' : path)\n : undefined,\n });\n\n app.use('/*', async (c, next) => {\n const reqPath = c.req.path;\n const resolved =\n basePath && reqPath.startsWith(basePath) ? reqPath.slice(basePath.length) || '/' : reqPath;\n // Skip index.html (handled by SPA fallback) and /ws (handled by WebSocket upgrader)\n if (resolved === '/' || resolved === '/index.html' || resolved === '/ws') {\n return next();\n }\n return staticHandler(c, next);\n });\n\n // SPA fallback: serve the (possibly injected) index.html for all\n // non-API, non-static-asset routes so React Router handles routing.\n // Skip /ws so the WebSocket upgrader (registered after createServer) can handle it.\n if (spaHtml) {\n app.get('*', async (c, next) => {\n const resolved =\n basePath && c.req.path.startsWith(basePath)\n ? c.req.path.slice(basePath.length) || '/'\n : c.req.path;\n if (resolved === '/ws') return next();\n return c.html(spaHtml!);\n });\n }\n }\n\n return {\n app,\n connMgr,\n costAggregator,\n /** Create WS handlers. Call before registering static/SPA routes are reached. */\n createWsHandlers: () => createWsHandlers(connMgr),\n traceListener,\n };\n}\n","import type { Context, Next } from 'hono';\nimport type { StudioEnv, ApiError } from '../types.js';\n\nexport async function errorHandler(c: Context<StudioEnv>, next: Next) {\n try {\n await next();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const code = (err as { code?: string }).code ?? 'INTERNAL_ERROR';\n\n // Determine HTTP status from error properties\n let status = 500;\n if ('status' in (err as object)) {\n const errStatus = (err as { status: unknown }).status;\n if (typeof errStatus === 'number' && errStatus >= 400 && errStatus < 600) {\n status = errStatus;\n }\n } else if (\n code === 'NOT_FOUND' ||\n message.includes('not found') ||\n message.includes('not registered')\n ) {\n status = 404;\n } else if (\n code === 'VALIDATION_ERROR' ||\n message.includes('Expected') ||\n message.includes('invalid')\n ) {\n status = 400;\n }\n\n const body: ApiError = {\n ok: false,\n error: { code, message },\n };\n\n return c.json(body, status as 400 | 404 | 500);\n }\n}\n","/**\n * Minimal interface for a connection that can receive broadcast messages.\n * Satisfied by WSContext (Hono), ws.WebSocket (Node.js), and the middleware's\n * adapted socket. Internal to ConnectionManager — not part of the public API.\n */\nexport interface BroadcastTarget {\n send(data: string): void;\n close?(): void;\n}\n\n/**\n * Short-lived event buffer for execution streams.\n * Solves the race where a fast provider (e.g., MockProvider) completes\n * before the client's WS subscription is established. Events are buffered\n * per-channel and replayed to late subscribers.\n */\ninterface ChannelBuffer {\n events: string[]; // Pre-serialized JSON messages\n complete: boolean; // True after done/error event\n timer?: ReturnType<typeof setTimeout>;\n}\n\n/** Channels eligible for replay buffering (execution streams). */\nfunction isBufferedChannel(channel: string): boolean {\n return channel.startsWith('execution:');\n}\n\nconst BUFFER_TTL_MS = 30_000; // Clean up buffers 30s after stream completes\nconst MAX_BUFFER_EVENTS = 500; // Cap replay buffer size\n\n/**\n * Manages WebSocket connections and channel subscriptions.\n * Supports channel multiplexing: clients subscribe/unsubscribe to channels\n * and receive events only for channels they're subscribed to.\n *\n * Execution channels (`execution:*`) are replay-buffered: events are stored\n * so that late subscribers receive the full event history. Buffers are cleaned\n * up shortly after the stream completes.\n */\nexport class ConnectionManager {\n /** channel -> set of WS connections */\n private channels = new Map<string, Set<BroadcastTarget>>();\n /** ws -> set of subscribed channels (for cleanup) */\n private connections = new Map<BroadcastTarget, Set<string>>();\n /** channel -> replay buffer for execution streams */\n private buffers = new Map<string, ChannelBuffer>();\n private maxConnections = 100;\n\n /** Register a new WS connection. */\n add(ws: BroadcastTarget): void {\n if (this.connections.size >= this.maxConnections) {\n ws.close?.();\n return;\n }\n this.connections.set(ws, new Set());\n }\n\n /** Remove a WS connection and all its subscriptions. */\n remove(ws: BroadcastTarget): void {\n const channels = this.connections.get(ws);\n if (channels) {\n for (const ch of channels) {\n this.channels.get(ch)?.delete(ws);\n if (this.channels.get(ch)?.size === 0) {\n this.channels.delete(ch);\n }\n }\n }\n this.connections.delete(ws);\n }\n\n /** Subscribe a connection to a channel. Replays buffered events for execution channels. */\n subscribe(ws: BroadcastTarget, channel: string): void {\n if (!this.connections.has(ws)) return;\n let subs = this.channels.get(channel);\n if (!subs) {\n subs = new Set();\n this.channels.set(channel, subs);\n }\n subs.add(ws);\n this.connections.get(ws)!.add(channel);\n\n // Replay buffered events for late subscribers\n const buffer = this.buffers.get(channel);\n if (buffer) {\n for (const msg of buffer.events) {\n try {\n ws.send(msg);\n } catch {\n this.remove(ws);\n return;\n }\n }\n }\n }\n\n /** Unsubscribe a connection from a channel. */\n unsubscribe(ws: BroadcastTarget, channel: string): void {\n this.channels.get(channel)?.delete(ws);\n if (this.channels.get(channel)?.size === 0) {\n this.channels.delete(channel);\n }\n this.connections.get(ws)?.delete(channel);\n }\n\n /** Broadcast data to all subscribers of a channel. Buffers events for execution channels. */\n broadcast(channel: string, data: unknown): void {\n const msg = JSON.stringify({ type: 'event', channel, data });\n\n // Buffer events for execution channels so late subscribers can replay\n if (isBufferedChannel(channel)) {\n let buffer = this.buffers.get(channel);\n if (!buffer) {\n buffer = { events: [], complete: false };\n this.buffers.set(channel, buffer);\n }\n // Always buffer terminal events; skip non-terminal if at capacity\n const event = data as { type?: string };\n const isTerminal = event.type === 'done' || event.type === 'error';\n if (buffer.events.length < MAX_BUFFER_EVENTS || isTerminal) {\n buffer.events.push(msg);\n }\n\n // Schedule buffer cleanup after terminal events\n if (isTerminal) {\n buffer.complete = true;\n if (buffer.timer) clearTimeout(buffer.timer);\n buffer.timer = setTimeout(() => {\n this.buffers.delete(channel);\n }, BUFFER_TTL_MS);\n }\n }\n\n // Send to current subscribers\n const subs = this.channels.get(channel);\n if (!subs || subs.size === 0) return;\n\n for (const ws of [...subs]) {\n try {\n ws.send(msg);\n } catch {\n this.remove(ws);\n }\n }\n }\n\n /** Broadcast to channel and all wildcard subscribers (e.g., trace:* matches trace:abc). */\n broadcastWithWildcard(channel: string, data: unknown): void {\n this.broadcast(channel, data);\n\n // Check for wildcard subscribers: \"prefix:*\" matches \"prefix:anything\"\n // Send with the actual channel name so wildcard subscribers know the source.\n const colonIdx = channel.indexOf(':');\n if (colonIdx > 0) {\n const wildcardChannel = channel.substring(0, colonIdx) + ':*';\n const subs = this.channels.get(wildcardChannel);\n if (!subs || subs.size === 0) return;\n\n const msg = JSON.stringify({ type: 'event', channel, data });\n for (const ws of [...subs]) {\n try {\n ws.send(msg);\n } catch {\n this.remove(ws);\n }\n }\n }\n }\n\n /** Close all connections, clear all state and buffers. Used during shutdown. */\n closeAll(): void {\n for (const ws of this.connections.keys()) {\n ws.close?.();\n }\n for (const buffer of this.buffers.values()) {\n if (buffer.timer) clearTimeout(buffer.timer);\n }\n this.connections.clear();\n this.channels.clear();\n this.buffers.clear();\n }\n\n /** Get the number of active connections. */\n get connectionCount(): number {\n return this.connections.size;\n }\n\n /** Check if any connections are subscribed to a channel. */\n hasSubscribers(channel: string): boolean {\n return (this.channels.get(channel)?.size ?? 0) > 0;\n }\n}\n","import type { BroadcastTarget } from './connection-manager.js';\nimport type { ConnectionManager } from './connection-manager.js';\n\n/** Channel prefixes that accept suffixes (e.g., execution:abc, trace:*). */\nconst VALID_CHANNEL_PREFIXES = ['execution:', 'trace:'];\n/** Channels that must match exactly (no suffix allowed). */\nconst VALID_EXACT_CHANNELS = ['costs', 'decisions'];\nconst MAX_CHANNEL_LENGTH = 256;\n\n/**\n * Handle a single WebSocket message according to the Studio protocol.\n * Returns a JSON string to send back to the client, or null for no response.\n *\n * Used by both the Hono WS handler (ws/handler.ts) and the Node.js\n * middleware (middleware.ts) to keep the protocol in one place.\n */\nexport function handleWsMessage(\n raw: string,\n socket: BroadcastTarget,\n connMgr: ConnectionManager,\n): string | null {\n // Reject oversized messages (64KB limit)\n if (raw.length > 65536) {\n return JSON.stringify({ type: 'error', message: 'Message too large' });\n }\n\n let msg: { type: string; channel?: string };\n try {\n msg = JSON.parse(raw);\n } catch {\n return JSON.stringify({ type: 'error', message: 'Invalid JSON' });\n }\n\n switch (msg.type) {\n case 'subscribe': {\n const error = validateChannel(msg.channel);\n if (error) return JSON.stringify({ type: 'error', message: error });\n connMgr.subscribe(socket, msg.channel!);\n return JSON.stringify({ type: 'subscribed', channel: msg.channel });\n }\n case 'unsubscribe': {\n const error = validateChannel(msg.channel);\n if (error) return JSON.stringify({ type: 'error', message: error });\n connMgr.unsubscribe(socket, msg.channel!);\n return JSON.stringify({ type: 'unsubscribed', channel: msg.channel });\n }\n case 'ping':\n return JSON.stringify({ type: 'pong' });\n default:\n return JSON.stringify({ type: 'error', message: 'Unknown message type' });\n }\n}\n\nfunction validateChannel(channel: unknown): string | null {\n if (typeof channel !== 'string' || !channel) {\n return 'Missing or invalid channel';\n }\n if (channel.length > MAX_CHANNEL_LENGTH) {\n return `Channel name exceeds ${MAX_CHANNEL_LENGTH} characters`;\n }\n if (\n !VALID_EXACT_CHANNELS.includes(channel as (typeof VALID_EXACT_CHANNELS)[number]) &&\n !VALID_CHANNEL_PREFIXES.some((p) => channel.startsWith(p))\n ) {\n return `Invalid channel: ${channel}`;\n }\n return null;\n}\n","import type { WSContext } from 'hono/ws';\nimport type { ConnectionManager } from './connection-manager.js';\nimport { handleWsMessage } from './protocol.js';\n\n/** Create WS event handlers for a Hono WebSocket connection. */\nexport function createWsHandlers(connMgr: ConnectionManager) {\n return {\n onOpen(_event: Event, ws: WSContext) {\n connMgr.add(ws);\n },\n\n onMessage(event: MessageEvent, ws: WSContext) {\n const reply = handleWsMessage(String(event.data), ws, connMgr);\n if (reply) ws.send(reply);\n },\n\n onClose(_event: CloseEvent, ws: WSContext) {\n connMgr.remove(ws);\n },\n\n onError(_event: Event, ws: WSContext) {\n connMgr.remove(ws);\n },\n };\n}\n","import type { CostData } from './types.js';\nimport type { ConnectionManager } from './ws/connection-manager.js';\n\n/**\n * Accumulates cost data from trace events.\n * Broadcasts updates to the 'costs' WS channel.\n */\nexport class CostAggregator {\n private data: CostData = {\n totalCost: 0,\n totalTokens: { input: 0, output: 0, reasoning: 0 },\n byAgent: {},\n byModel: {},\n byWorkflow: {},\n };\n\n constructor(private connMgr: ConnectionManager) {}\n\n /** Process a trace event and update cost data. */\n onTrace(event: {\n type?: string;\n agent?: string;\n model?: string;\n workflow?: string;\n cost?: number;\n tokens?: { input?: number; output?: number; reasoning?: number };\n }): void {\n if (event.cost == null && !event.tokens) return;\n\n const cost = Number.isFinite(event.cost) ? event.cost! : 0;\n const tokens = event.tokens ?? {};\n\n this.data.totalCost += cost;\n this.data.totalTokens.input += tokens.input ?? 0;\n this.data.totalTokens.output += tokens.output ?? 0;\n this.data.totalTokens.reasoning += tokens.reasoning ?? 0;\n\n if (event.agent) {\n const entry = this.data.byAgent[event.agent] ?? { cost: 0, calls: 0 };\n entry.cost += cost;\n entry.calls += 1;\n this.data.byAgent[event.agent] = entry;\n }\n\n if (event.model) {\n const entry = this.data.byModel[event.model] ?? {\n cost: 0,\n calls: 0,\n tokens: { input: 0, output: 0 },\n };\n entry.cost += cost;\n entry.calls += 1;\n entry.tokens.input += tokens.input ?? 0;\n entry.tokens.output += tokens.output ?? 0;\n this.data.byModel[event.model] = entry;\n }\n\n if (event.workflow) {\n const entry = this.data.byWorkflow[event.workflow] ?? { cost: 0, executions: 0 };\n entry.cost += cost;\n if (event.type === 'workflow_start') entry.executions += 1;\n this.data.byWorkflow[event.workflow] = entry;\n }\n\n // Broadcast to WS subscribers\n this.connMgr.broadcast('costs', this.data);\n }\n\n /** Get current aggregated cost data. */\n getData(): CostData {\n return this.data;\n }\n\n /** Reset all accumulated data. */\n reset(): void {\n this.data = {\n totalCost: 0,\n totalTokens: { input: 0, output: 0, reasoning: 0 },\n byAgent: {},\n byModel: {},\n byWorkflow: {},\n };\n }\n}\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\n\nconst app = new Hono<StudioEnv>();\n\napp.get('/health', (c) => {\n const runtime = c.get('runtime');\n return c.json({\n ok: true,\n data: {\n status: 'healthy',\n workflows: runtime.getWorkflowNames().length,\n agents: runtime.getAgents().length,\n tools: runtime.getTools().length,\n },\n });\n});\n\nexport default app;\n","import { Hono } from 'hono';\nimport { zodToJsonSchema } from '@axlsdk/axl';\nimport type { StudioEnv, WorkflowSummary } from '../types.js';\nimport type { ConnectionManager } from '../ws/connection-manager.js';\n\nexport function createWorkflowRoutes(connMgr: ConnectionManager) {\n const app = new Hono<StudioEnv>();\n\n // List all workflows\n app.get('/workflows', (c) => {\n const runtime = c.get('runtime');\n const workflows: WorkflowSummary[] = runtime.getWorkflows().map((w) => ({\n name: w.name,\n hasInputSchema: !!w.inputSchema,\n hasOutputSchema: !!w.outputSchema,\n }));\n return c.json({ ok: true, data: workflows });\n });\n\n // Get workflow detail (including schemas)\n app.get('/workflows/:name', (c) => {\n const runtime = c.get('runtime');\n const name = c.req.param('name');\n const workflow = runtime.getWorkflow(name);\n if (!workflow) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Workflow \"${name}\" not found` } },\n 404,\n );\n }\n\n return c.json({\n ok: true,\n data: {\n name: workflow.name,\n inputSchema: workflow.inputSchema ? zodToJsonSchema(workflow.inputSchema) : null,\n outputSchema: workflow.outputSchema ? zodToJsonSchema(workflow.outputSchema) : null,\n },\n });\n });\n\n // Execute a workflow\n app.post('/workflows/:name/execute', async (c) => {\n const runtime = c.get('runtime');\n const name = c.req.param('name');\n\n const workflow = runtime.getWorkflow(name);\n if (!workflow) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Workflow \"${name}\" not found` } },\n 404,\n );\n }\n\n const body = await c.req.json<{\n input?: unknown;\n stream?: boolean;\n metadata?: Record<string, unknown>;\n }>();\n\n if (body.stream) {\n // Streaming execution — pipe events to WS channel\n const stream = runtime.stream(name, body.input ?? {}, { metadata: body.metadata });\n const executionId = `stream-${Date.now()}`;\n\n // Forward stream events to WS (error events flow through the iterator)\n (async () => {\n for await (const event of stream) {\n connMgr.broadcastWithWildcard(`execution:${executionId}`, event);\n }\n })();\n\n return c.json({ ok: true, data: { executionId, streaming: true } });\n }\n\n const result = await runtime.execute(name, body.input ?? {}, { metadata: body.metadata });\n return c.json({ ok: true, data: { result } });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\n\nconst app = new Hono<StudioEnv>();\n\n// List all executions\napp.get('/executions', async (c) => {\n const runtime = c.get('runtime');\n const executions = await runtime.getExecutions();\n return c.json({ ok: true, data: executions });\n});\n\n// Get execution by ID\napp.get('/executions/:id', async (c) => {\n const runtime = c.get('runtime');\n const id = c.req.param('id');\n const execution = await runtime.getExecution(id);\n if (!execution) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Execution \"${id}\" not found` } },\n 404,\n );\n }\n return c.json({ ok: true, data: execution });\n});\n\n// Abort a running execution\napp.post('/executions/:id/abort', (c) => {\n const runtime = c.get('runtime');\n const id = c.req.param('id');\n runtime.abort(id);\n return c.json({ ok: true, data: { aborted: true } });\n});\n\nexport default app;\n","import { Hono } from 'hono';\nimport type { StudioEnv, SessionSummary } from '../types.js';\nimport type { ConnectionManager } from '../ws/connection-manager.js';\n\nexport function createSessionRoutes(connMgr: ConnectionManager) {\n const app = new Hono<StudioEnv>();\n\n // List all sessions\n app.get('/sessions', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n if (!store.listSessions) {\n return c.json({ ok: true, data: [] });\n }\n const ids = await store.listSessions();\n const sessions: SessionSummary[] = [];\n for (const id of ids) {\n const history = await store.getSession(id);\n sessions.push({ id, messageCount: history.length });\n }\n return c.json({ ok: true, data: sessions });\n });\n\n // Get session history\n app.get('/sessions/:id', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n const id = c.req.param('id');\n const history = await store.getSession(id);\n const handoffHistory = await store.getSessionMeta(id, 'handoffHistory');\n return c.json({ ok: true, data: { id, history, handoffHistory: handoffHistory ?? [] } });\n });\n\n // Send message to session (non-streaming)\n app.post('/sessions/:id/send', async (c) => {\n const runtime = c.get('runtime');\n const id = c.req.param('id');\n const body = await c.req.json<{ message: string; workflow: string }>();\n\n const session = runtime.session(id);\n const result = await session.send(body.workflow, body.message);\n return c.json({ ok: true, data: { result } });\n });\n\n // Stream session message\n app.post('/sessions/:id/stream', async (c) => {\n const runtime = c.get('runtime');\n const id = c.req.param('id');\n const body = await c.req.json<{ message: string; workflow: string }>();\n\n const session = runtime.session(id);\n const stream = await session.stream(body.workflow, body.message);\n const executionId = `session-${id}-${Date.now()}`;\n\n // Forward stream events to WS (error events flow through the iterator)\n (async () => {\n for await (const event of stream) {\n connMgr.broadcastWithWildcard(`execution:${executionId}`, event);\n }\n })();\n\n return c.json({ ok: true, data: { executionId, streaming: true } });\n });\n\n // Delete session\n app.delete('/sessions/:id', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n const id = c.req.param('id');\n await store.deleteSession(id);\n return c.json({ ok: true, data: { deleted: true } });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { zodToJsonSchema } from '@axlsdk/axl';\nimport type { StudioEnv, AgentSummary } from '../types.js';\n\nconst app = new Hono<StudioEnv>();\n\n// List all agents\napp.get('/agents', (c) => {\n const runtime = c.get('runtime');\n const agents: AgentSummary[] = runtime.getAgents().map((a) => ({\n name: a._name,\n model: a.resolveModel(),\n system: a.resolveSystem(),\n tools: a._config.tools?.map((t) => t.name) ?? [],\n handoffs:\n typeof a._config.handoffs === 'function'\n ? ['(dynamic)']\n : (a._config.handoffs?.map((h) => h.agent._name) ?? []),\n maxTurns: a._config.maxTurns,\n temperature: a._config.temperature,\n maxTokens: a._config.maxTokens,\n effort: a._config.effort,\n thinkingBudget: a._config.thinkingBudget,\n includeThoughts: a._config.includeThoughts,\n toolChoice: a._config.toolChoice,\n stop: a._config.stop,\n }));\n return c.json({ ok: true, data: agents });\n});\n\n// Get agent detail\napp.get('/agents/:name', (c) => {\n const runtime = c.get('runtime');\n const name = c.req.param('name');\n const agent = runtime.getAgent(name);\n if (!agent) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Agent \"${name}\" not found` } },\n 404,\n );\n }\n\n const cfg = agent._config;\n return c.json({\n ok: true,\n data: {\n name: agent._name,\n model: agent.resolveModel(),\n system: agent.resolveSystem(),\n tools:\n cfg.tools?.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: zodToJsonSchema(t.inputSchema),\n })) ?? [],\n handoffs:\n typeof cfg.handoffs === 'function'\n ? [\n {\n agent: '(dynamic)',\n description: 'Resolved at runtime from metadata',\n mode: 'oneway' as const,\n },\n ]\n : (cfg.handoffs?.map((h) => ({\n agent: h.agent._name,\n description: h.description,\n mode: h.mode ?? 'oneway',\n })) ?? []),\n maxTurns: cfg.maxTurns,\n temperature: cfg.temperature,\n maxTokens: cfg.maxTokens,\n effort: cfg.effort,\n thinkingBudget: cfg.thinkingBudget,\n includeThoughts: cfg.includeThoughts,\n toolChoice: cfg.toolChoice,\n stop: cfg.stop,\n timeout: cfg.timeout,\n maxContext: cfg.maxContext,\n version: cfg.version,\n mcp: cfg.mcp,\n mcpTools: cfg.mcpTools,\n hasGuardrails: !!cfg.guardrails,\n guardrails: cfg.guardrails\n ? {\n hasInput: !!cfg.guardrails.input,\n hasOutput: !!cfg.guardrails.output,\n onBlock: cfg.guardrails.onBlock ?? 'throw',\n maxRetries: cfg.guardrails.maxRetries,\n }\n : null,\n },\n });\n});\n\nexport default app;\n","import { Hono } from 'hono';\nimport { zodToJsonSchema } from '@axlsdk/axl';\nimport type { StudioEnv, ToolSummary } from '../types.js';\n\nconst app = new Hono<StudioEnv>();\n\n// List all tools\napp.get('/tools', (c) => {\n const runtime = c.get('runtime');\n const tools: ToolSummary[] = runtime.getTools().map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema ? zodToJsonSchema(t.inputSchema) : {},\n sensitive: t.sensitive ?? false,\n requireApproval: t.requireApproval ?? false,\n }));\n return c.json({ ok: true, data: tools });\n});\n\n// Get tool detail\napp.get('/tools/:name', (c) => {\n const runtime = c.get('runtime');\n const name = c.req.param('name');\n const tool = runtime.getTool(name);\n if (!tool) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Tool \"${name}\" not found` } },\n 404,\n );\n }\n\n return c.json({\n ok: true,\n data: {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema ? zodToJsonSchema(tool.inputSchema) : {},\n sensitive: tool.sensitive,\n requireApproval: tool.requireApproval,\n retry: tool.retry,\n hasHooks: !!tool.hooks,\n hooks: tool.hooks\n ? {\n hasBefore: !!tool.hooks.before,\n hasAfter: !!tool.hooks.after,\n }\n : null,\n },\n });\n});\n\n// Test a tool directly\napp.post('/tools/:name/test', async (c) => {\n const runtime = c.get('runtime');\n const name = c.req.param('name');\n const tool = runtime.getTool(name);\n if (!tool) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Tool \"${name}\" not found` } },\n 404,\n );\n }\n\n const body = await c.req.json<{ input: unknown }>();\n const ctx = runtime.createContext();\n const result = await tool.run(ctx, body.input);\n return c.json({ ok: true, data: { result } });\n});\n\nexport default app;\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\n\nconst app = new Hono<StudioEnv>();\n\n// Get all memory entries for a scope\napp.get('/memory/:scope', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n const scope = c.req.param('scope');\n\n if (!store.getAllMemory) {\n return c.json({ ok: true, data: [] });\n }\n\n const entries = await store.getAllMemory(scope);\n return c.json({ ok: true, data: entries });\n});\n\n// Get a specific memory entry\napp.get('/memory/:scope/:key', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n const scope = c.req.param('scope');\n const key = c.req.param('key');\n\n if (!store.getMemory) {\n return c.json(\n { ok: false, error: { code: 'NOT_SUPPORTED', message: 'Memory not supported' } },\n 501,\n );\n }\n\n const value = await store.getMemory(scope, key);\n if (value === null) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Memory \"${scope}/${key}\" not found` } },\n 404,\n );\n }\n\n return c.json({ ok: true, data: { key, value } });\n});\n\n// Save a memory entry\napp.put('/memory/:scope/:key', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n const scope = c.req.param('scope');\n const key = c.req.param('key');\n\n if (!store.saveMemory) {\n return c.json(\n { ok: false, error: { code: 'NOT_SUPPORTED', message: 'Memory not supported' } },\n 501,\n );\n }\n\n const body = await c.req.json<{ value: unknown }>();\n await store.saveMemory(scope, key, body.value);\n return c.json({ ok: true, data: { saved: true } });\n});\n\n// Delete a memory entry\napp.delete('/memory/:scope/:key', async (c) => {\n const runtime = c.get('runtime');\n const store = runtime.getStateStore();\n const scope = c.req.param('scope');\n const key = c.req.param('key');\n\n if (!store.deleteMemory) {\n return c.json(\n { ok: false, error: { code: 'NOT_SUPPORTED', message: 'Memory not supported' } },\n 501,\n );\n }\n\n await store.deleteMemory(scope, key);\n return c.json({ ok: true, data: { deleted: true } });\n});\n\n// Semantic search\napp.post('/memory/search', async (c) => {\n // TODO: Connect to MemoryManager's vector search once exposed\n return c.json({\n ok: true,\n data: { results: [], message: 'Semantic search requires MemoryManager with vector store' },\n });\n});\n\nexport default app;\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\n\nconst app = new Hono<StudioEnv>();\n\n// List pending decisions\napp.get('/decisions', async (c) => {\n const runtime = c.get('runtime');\n const decisions = await runtime.getPendingDecisions();\n return c.json({ ok: true, data: decisions });\n});\n\n// Resolve a pending decision\napp.post('/decisions/:executionId/resolve', async (c) => {\n const runtime = c.get('runtime');\n const executionId = c.req.param('executionId');\n const body = await c.req.json<{ approved: boolean; reason?: string }>();\n\n await runtime.resolveDecision(executionId, body);\n return c.json({ ok: true, data: { resolved: true } });\n});\n\nexport default app;\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\nimport type { CostAggregator } from '../cost-aggregator.js';\n\nexport function createCostRoutes(costAggregator: CostAggregator) {\n const app = new Hono<StudioEnv>();\n\n app.get('/costs', (c) => {\n return c.json({ ok: true, data: costAggregator.getData() });\n });\n\n app.post('/costs/reset', (c) => {\n costAggregator.reset();\n return c.json({ ok: true, data: { reset: true } });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\n\nexport function createEvalRoutes(evalLoader?: () => Promise<void>) {\n const app = new Hono<StudioEnv>();\n\n // List registered eval configs\n app.get('/evals', async (c) => {\n if (evalLoader) await evalLoader();\n const runtime = c.get('runtime');\n const evals = runtime.getRegisteredEvals();\n return c.json({ ok: true, data: evals });\n });\n\n // Get eval run history\n app.get('/evals/history', async (c) => {\n const runtime = c.get('runtime');\n const history = await runtime.getEvalHistory();\n return c.json({ ok: true, data: history });\n });\n\n // Run a registered eval by name\n app.post('/evals/:name/run', async (c) => {\n if (evalLoader) await evalLoader();\n const runtime = c.get('runtime');\n const name = c.req.param('name');\n\n const entry = runtime.getRegisteredEval(name);\n if (!entry) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: `Eval \"${name}\" not found` } },\n 404,\n );\n }\n\n try {\n // Runtime persists eval result to history automatically\n const result = await runtime.runRegisteredEval(name);\n return c.json({ ok: true, data: result });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: { code: 'EVAL_ERROR', message } }, 400);\n }\n });\n\n // Compare eval results\n app.post('/evals/compare', async (c) => {\n const runtime = c.get('runtime');\n const body = await c.req.json<{ baseline: unknown; candidate: unknown }>();\n\n try {\n const result = await runtime.evalCompare(body.baseline, body.candidate);\n return c.json({ ok: true, data: result });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: { code: 'EVAL_ERROR', message } }, 400);\n }\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { StudioEnv } from '../types.js';\nimport type { ConnectionManager } from '../ws/connection-manager.js';\n\nexport function createPlaygroundRoutes(connMgr: ConnectionManager) {\n const app = new Hono<StudioEnv>();\n\n // Chat with an agent directly — no workflow required\n app.post('/playground/chat', async (c) => {\n const runtime = c.get('runtime');\n const body = await c.req.json<{\n sessionId?: string;\n message: string;\n agent?: string;\n }>();\n\n if (!body.message || typeof body.message !== 'string' || !body.message.trim()) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_INPUT',\n message: 'message is required and must be a non-empty string',\n },\n },\n 400,\n );\n }\n\n const agents = runtime.getAgents();\n const agent = body.agent ? agents.find((a) => a._name === body.agent) : agents[0];\n if (!agent) {\n return c.json(\n {\n ok: false,\n error: { code: 'NO_AGENT', message: `Agent \"${body.agent ?? ''}\" not found` },\n },\n 400,\n );\n }\n\n const sessionId = body.sessionId ?? `playground-${Date.now()}`;\n const executionId = `playground-${sessionId}-${Date.now()}`;\n const store = runtime.getStateStore();\n\n // Load session history for multi-turn conversations\n const history = await store.getSession(sessionId);\n history.push({ role: 'user', content: body.message });\n\n // Create a context wired to stream events to the WS channel\n const ctx = runtime.createContext({\n sessionHistory: history,\n onToken: (token: string) => {\n connMgr.broadcastWithWildcard(`execution:${executionId}`, {\n type: 'token',\n data: token,\n });\n },\n });\n\n // Run the agent ask asynchronously, stream results via WS\n (async () => {\n try {\n const result = await ctx.ask(agent, body.message);\n const resultText = typeof result === 'string' ? result : JSON.stringify(result);\n\n // Save assistant response to session history\n history.push({ role: 'assistant', content: resultText });\n await store.saveSession(sessionId, history);\n\n connMgr.broadcastWithWildcard(`execution:${executionId}`, {\n type: 'done',\n data: resultText,\n });\n } catch (err) {\n connMgr.broadcastWithWildcard(`execution:${executionId}`, {\n type: 'error',\n message: err instanceof Error ? err.message : String(err),\n });\n }\n })();\n\n return c.json({\n ok: true,\n data: { sessionId, executionId, streaming: true },\n });\n });\n\n return app;\n}\n"],"mappings":"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","names":["Hono","Hono","app","Hono","app","Hono","app","Hono","zodToJsonSchema","app","Hono","zodToJsonSchema","app","Hono","app","Hono","app","Hono","app","Hono","app","Hono","app","app","Hono"]}
package/dist/cli.cjs CHANGED
@@ -63,11 +63,18 @@ async function errorHandler(c, next) {
63
63
  }
64
64
 
65
65
  // src/server/ws/connection-manager.ts
66
+ function isBufferedChannel(channel) {
67
+ return channel.startsWith("execution:");
68
+ }
69
+ var BUFFER_TTL_MS = 3e4;
70
+ var MAX_BUFFER_EVENTS = 500;
66
71
  var ConnectionManager = class {
67
72
  /** channel -> set of WS connections */
68
73
  channels = /* @__PURE__ */ new Map();
69
74
  /** ws -> set of subscribed channels (for cleanup) */
70
75
  connections = /* @__PURE__ */ new Map();
76
+ /** channel -> replay buffer for execution streams */
77
+ buffers = /* @__PURE__ */ new Map();
71
78
  maxConnections = 100;
72
79
  /** Register a new WS connection. */
73
80
  add(ws) {
@@ -90,7 +97,7 @@ var ConnectionManager = class {
90
97
  }
91
98
  this.connections.delete(ws);
92
99
  }
93
- /** Subscribe a connection to a channel. No-op if the connection was not added. */
100
+ /** Subscribe a connection to a channel. Replays buffered events for execution channels. */
94
101
  subscribe(ws, channel) {
95
102
  if (!this.connections.has(ws)) return;
96
103
  let subs = this.channels.get(channel);
@@ -100,6 +107,17 @@ var ConnectionManager = class {
100
107
  }
101
108
  subs.add(ws);
102
109
  this.connections.get(ws).add(channel);
110
+ const buffer = this.buffers.get(channel);
111
+ if (buffer) {
112
+ for (const msg of buffer.events) {
113
+ try {
114
+ ws.send(msg);
115
+ } catch {
116
+ this.remove(ws);
117
+ return;
118
+ }
119
+ }
120
+ }
103
121
  }
104
122
  /** Unsubscribe a connection from a channel. */
105
123
  unsubscribe(ws, channel) {
@@ -109,11 +127,30 @@ var ConnectionManager = class {
109
127
  }
110
128
  this.connections.get(ws)?.delete(channel);
111
129
  }
112
- /** Broadcast data to all subscribers of a channel. */
130
+ /** Broadcast data to all subscribers of a channel. Buffers events for execution channels. */
113
131
  broadcast(channel, data) {
132
+ const msg = JSON.stringify({ type: "event", channel, data });
133
+ if (isBufferedChannel(channel)) {
134
+ let buffer = this.buffers.get(channel);
135
+ if (!buffer) {
136
+ buffer = { events: [], complete: false };
137
+ this.buffers.set(channel, buffer);
138
+ }
139
+ const event = data;
140
+ const isTerminal = event.type === "done" || event.type === "error";
141
+ if (buffer.events.length < MAX_BUFFER_EVENTS || isTerminal) {
142
+ buffer.events.push(msg);
143
+ }
144
+ if (isTerminal) {
145
+ buffer.complete = true;
146
+ if (buffer.timer) clearTimeout(buffer.timer);
147
+ buffer.timer = setTimeout(() => {
148
+ this.buffers.delete(channel);
149
+ }, BUFFER_TTL_MS);
150
+ }
151
+ }
114
152
  const subs = this.channels.get(channel);
115
153
  if (!subs || subs.size === 0) return;
116
- const msg = JSON.stringify({ type: "event", channel, data });
117
154
  for (const ws of [...subs]) {
118
155
  try {
119
156
  ws.send(msg);
@@ -140,13 +177,17 @@ var ConnectionManager = class {
140
177
  }
141
178
  }
142
179
  }
143
- /** Close all connections and clear all state. Used during shutdown. */
180
+ /** Close all connections, clear all state and buffers. Used during shutdown. */
144
181
  closeAll() {
145
182
  for (const ws of this.connections.keys()) {
146
183
  ws.close?.();
147
184
  }
185
+ for (const buffer of this.buffers.values()) {
186
+ if (buffer.timer) clearTimeout(buffer.timer);
187
+ }
148
188
  this.connections.clear();
149
189
  this.channels.clear();
190
+ this.buffers.clear();
150
191
  }
151
192
  /** Get the number of active connections. */
152
193
  get connectionCount() {
@@ -351,15 +392,8 @@ function createWorkflowRoutes(connMgr) {
351
392
  const stream = runtime.stream(name, body.input ?? {}, { metadata: body.metadata });
352
393
  const executionId = `stream-${Date.now()}`;
353
394
  (async () => {
354
- try {
355
- for await (const event of stream) {
356
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
357
- }
358
- } catch (err) {
359
- connMgr.broadcastWithWildcard(`execution:${executionId}`, {
360
- type: "error",
361
- message: err instanceof Error ? err.message : "Stream error"
362
- });
395
+ for await (const event of stream) {
396
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
363
397
  }
364
398
  })();
365
399
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -440,15 +474,8 @@ function createSessionRoutes(connMgr) {
440
474
  const stream = await session.stream(body.workflow, body.message);
441
475
  const executionId = `session-${id}-${Date.now()}`;
442
476
  (async () => {
443
- try {
444
- for await (const event of stream) {
445
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
446
- }
447
- } catch (err) {
448
- connMgr.broadcastWithWildcard(`execution:${executionId}`, {
449
- type: "error",
450
- message: err instanceof Error ? err.message : "Stream error"
451
- });
477
+ for await (const event of stream) {
478
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
452
479
  }
453
480
  })();
454
481
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -759,26 +786,57 @@ function createPlaygroundRoutes(connMgr) {
759
786
  app7.post("/playground/chat", async (c) => {
760
787
  const runtime = c.get("runtime");
761
788
  const body = await c.req.json();
762
- const workflowName = body.workflow ?? runtime.getWorkflowNames()[0];
763
- if (!workflowName) {
789
+ if (!body.message || typeof body.message !== "string" || !body.message.trim()) {
790
+ return c.json(
791
+ {
792
+ ok: false,
793
+ error: {
794
+ code: "INVALID_INPUT",
795
+ message: "message is required and must be a non-empty string"
796
+ }
797
+ },
798
+ 400
799
+ );
800
+ }
801
+ const agents = runtime.getAgents();
802
+ const agent = body.agent ? agents.find((a) => a._name === body.agent) : agents[0];
803
+ if (!agent) {
764
804
  return c.json(
765
- { ok: false, error: { code: "NO_WORKFLOW", message: "No workflows registered" } },
805
+ {
806
+ ok: false,
807
+ error: { code: "NO_AGENT", message: `Agent "${body.agent ?? ""}" not found` }
808
+ },
766
809
  400
767
810
  );
768
811
  }
769
812
  const sessionId = body.sessionId ?? `playground-${Date.now()}`;
770
- const session = runtime.session(sessionId);
771
- const stream = await session.stream(workflowName, body.message);
772
813
  const executionId = `playground-${sessionId}-${Date.now()}`;
814
+ const store = runtime.getStateStore();
815
+ const history = await store.getSession(sessionId);
816
+ history.push({ role: "user", content: body.message });
817
+ const ctx = runtime.createContext({
818
+ sessionHistory: history,
819
+ onToken: (token) => {
820
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, {
821
+ type: "token",
822
+ data: token
823
+ });
824
+ }
825
+ });
773
826
  (async () => {
774
827
  try {
775
- for await (const event of stream) {
776
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
777
- }
828
+ const result = await ctx.ask(agent, body.message);
829
+ const resultText = typeof result === "string" ? result : JSON.stringify(result);
830
+ history.push({ role: "assistant", content: resultText });
831
+ await store.saveSession(sessionId, history);
832
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, {
833
+ type: "done",
834
+ data: resultText
835
+ });
778
836
  } catch (err) {
779
837
  connMgr.broadcastWithWildcard(`execution:${executionId}`, {
780
838
  type: "error",
781
- message: err instanceof Error ? err.message : "Stream error"
839
+ message: err instanceof Error ? err.message : String(err)
782
840
  });
783
841
  }
784
842
  })();
@@ -890,19 +948,24 @@ function createServer(options) {
890
948
  app7.use("/*", async (c, next) => {
891
949
  const reqPath = c.req.path;
892
950
  const resolved = basePath && reqPath.startsWith(basePath) ? reqPath.slice(basePath.length) || "/" : reqPath;
893
- if (resolved === "/" || resolved === "/index.html") {
951
+ if (resolved === "/" || resolved === "/index.html" || resolved === "/ws") {
894
952
  return next();
895
953
  }
896
954
  return staticHandler(c, next);
897
955
  });
898
956
  if (spaHtml) {
899
- app7.get("*", (c) => c.html(spaHtml));
957
+ app7.get("*", async (c, next) => {
958
+ const resolved = basePath && c.req.path.startsWith(basePath) ? c.req.path.slice(basePath.length) || "/" : c.req.path;
959
+ if (resolved === "/ws") return next();
960
+ return c.html(spaHtml);
961
+ });
900
962
  }
901
963
  }
902
964
  return {
903
965
  app: app7,
904
966
  connMgr,
905
967
  costAggregator,
968
+ /** Create WS handlers. Call before registering static/SPA routes are reached. */
906
969
  createWsHandlers: () => createWsHandlers(connMgr),
907
970
  traceListener
908
971
  };