@tuttiai/cli 0.5.0 → 0.6.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/dist/index.js CHANGED
@@ -167,24 +167,47 @@ async function runCommand(scorePath) {
167
167
  }
168
168
  }
169
169
  }
170
+ for (const agent of Object.values(score.agents)) {
171
+ agent.streaming = true;
172
+ }
170
173
  const runtime = new TuttiRuntime(score);
171
174
  const spinner = ora({ color: "cyan" });
175
+ let streaming = false;
172
176
  runtime.events.on("agent:start", (e) => {
173
177
  logger2.info({ agent: e.agent_name }, "Running agent");
174
178
  });
175
179
  runtime.events.on("llm:request", () => {
176
180
  spinner.start("Thinking...");
177
181
  });
182
+ runtime.events.on("token:stream", (e) => {
183
+ if (!streaming) {
184
+ spinner.stop();
185
+ streaming = true;
186
+ }
187
+ process.stdout.write(e.text);
188
+ });
178
189
  runtime.events.on("llm:response", () => {
179
- spinner.stop();
190
+ if (streaming) {
191
+ process.stdout.write("\n");
192
+ } else {
193
+ spinner.stop();
194
+ }
180
195
  });
181
196
  runtime.events.on("tool:start", (e) => {
182
- logger2.info({ tool: e.tool_name }, "Using tool");
197
+ if (streaming) {
198
+ process.stdout.write(chalk2.dim("\n [using: " + e.tool_name + "]"));
199
+ } else {
200
+ spinner.stop();
201
+ console.log(chalk2.dim(" [using: " + e.tool_name + "]"));
202
+ }
183
203
  });
184
204
  runtime.events.on("tool:end", (e) => {
185
- logger2.debug({ tool: e.tool_name }, "Tool done");
205
+ if (streaming) {
206
+ process.stdout.write(chalk2.dim(" [done: " + e.tool_name + "]\n"));
207
+ }
186
208
  });
187
209
  runtime.events.on("tool:error", (e) => {
210
+ spinner.stop();
188
211
  logger2.error({ tool: e.tool_name }, "Tool error");
189
212
  });
190
213
  runtime.events.on("security:injection_detected", (e) => {
@@ -203,7 +226,9 @@ async function runCommand(scorePath) {
203
226
  console.log(chalk2.dim('Tutti REPL \u2014 type "exit" to quit\n'));
204
227
  let sessionId;
205
228
  process.on("SIGINT", () => {
206
- console.log(chalk2.dim("\nGoodbye!"));
229
+ if (streaming) process.stdout.write("\n");
230
+ spinner.stop();
231
+ console.log(chalk2.dim("Goodbye!"));
207
232
  rl.close();
208
233
  process.exit(0);
209
234
  });
@@ -213,13 +238,17 @@ async function runCommand(scorePath) {
213
238
  const trimmed = input.trim();
214
239
  if (!trimmed) continue;
215
240
  if (trimmed === "exit" || trimmed === "quit") break;
241
+ streaming = false;
216
242
  try {
217
243
  const result = await runtime.run("assistant", trimmed, sessionId);
218
244
  sessionId = result.session_id;
219
- console.log(`
220
- ${result.output}
221
- `);
245
+ if (!streaming) {
246
+ console.log("\n" + result.output + "\n");
247
+ } else {
248
+ console.log();
249
+ }
222
250
  } catch (err) {
251
+ if (streaming) process.stdout.write("\n");
223
252
  spinner.stop();
224
253
  logger2.error(
225
254
  { error: err instanceof Error ? err.message : String(err) },
@@ -594,7 +623,7 @@ process.on("uncaughtException", (err) => {
594
623
  process.exit(1);
595
624
  });
596
625
  var program = new Command();
597
- program.name("tutti-ai").description("Tutti \u2014 multi-agent orchestration. All agents. All together.").version("0.6.0");
626
+ program.name("tutti-ai").description("Tutti \u2014 multi-agent orchestration. All agents. All together.").version("0.7.0");
598
627
  program.command("init [project-name]").description("Create a new Tutti project").action(async (projectName) => {
599
628
  await initCommand(projectName);
600
629
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/commands/run.ts","../src/commands/add.ts","../src/commands/check.ts","../src/commands/studio.ts"],"sourcesContent":["import { config } from \"dotenv\";\nconfig();\n\nimport { createLogger } from \"@tuttiai/core\";\nconst logger = createLogger(\"tutti-cli\");\n\nprocess.on(\"unhandledRejection\", (reason) => {\n logger.error({ error: reason instanceof Error ? reason.message : String(reason) }, \"Unhandled rejection\");\n process.exit(1);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n logger.error({ error: err.message }, \"Fatal error\");\n process.exit(1);\n});\n\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { runCommand } from \"./commands/run.js\";\nimport { addCommand } from \"./commands/add.js\";\nimport { checkCommand } from \"./commands/check.js\";\nimport { studioCommand } from \"./commands/studio.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"tutti-ai\")\n .description(\"Tutti — multi-agent orchestration. All agents. All together.\")\n .version(\"0.6.0\");\n\nprogram\n .command(\"init [project-name]\")\n .description(\"Create a new Tutti project\")\n .action(async (projectName?: string) => {\n await initCommand(projectName);\n });\n\nprogram\n .command(\"run [score]\")\n .description(\"Run a Tutti score interactively\")\n .action(async (score?: string) => {\n await runCommand(score);\n });\n\nprogram\n .command(\"add <voice>\")\n .description(\"Add a voice to your project\")\n .action(async (voice: string) => {\n await addCommand(voice);\n });\n\nprogram\n .command(\"check [score]\")\n .description(\"Validate a score file without running it\")\n .action(async (score?: string) => {\n await checkCommand(score);\n });\n\nprogram\n .command(\"doctor [score]\")\n .description(\"Alias for check — validate a score file\")\n .action(async (score?: string) => {\n await checkCommand(score);\n });\n\nprogram\n .command(\"studio [score]\")\n .description(\"Launch Tutti Studio — local web UI for inspecting agent runs\")\n .action(async (score?: string) => {\n await studioCommand(score);\n });\n\nprogram.parse();\n","import { mkdirSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport Enquirer from \"enquirer\";\nimport { createLogger } from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nconst { prompt } = Enquirer;\n\nexport async function initCommand(projectName?: string): Promise<void> {\n if (!projectName) {\n const response = await prompt<{ projectName: string }>({\n type: \"input\",\n name: \"projectName\",\n message: \"Project name?\",\n });\n projectName = response.projectName;\n }\n\n if (!projectName) {\n logger.error(\"Project name is required\");\n process.exit(1);\n }\n\n const dir = join(process.cwd(), projectName);\n\n if (existsSync(dir)) {\n logger.error({ dir: `${projectName}/` }, \"Directory already exists\");\n process.exit(1);\n }\n\n mkdirSync(dir, { recursive: true });\n\n const files: Record<string, string> = {\n \"package.json\": JSON.stringify(\n {\n name: projectName,\n version: \"0.0.1\",\n type: \"module\",\n scripts: {\n dev: \"tsx watch tutti.score.ts\",\n start: \"tsx tutti.score.ts\",\n },\n dependencies: {\n \"@tuttiai/core\": \"*\",\n \"@tuttiai/types\": \"*\",\n },\n devDependencies: {\n tsx: \"^4.0.0\",\n typescript: \"^5.7.0\",\n },\n },\n null,\n 2,\n ),\n\n \".env.example\":\n \"ANTHROPIC_API_KEY=your_key_here\\n\\n\" +\n \"# Log level: debug | info | warn | error (default: info)\\n\" +\n \"TUTTI_LOG_LEVEL=info\\n\\n\" +\n \"# OpenTelemetry (optional)\\n\" +\n \"# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318\\n\" +\n \"# OTEL_SERVICE_NAME=tutti\\n\",\n\n \".gitignore\": \"node_modules\\ndist\\n.env\\n\",\n\n \"tsconfig.json\": JSON.stringify(\n {\n compilerOptions: {\n strict: true,\n target: \"ES2022\",\n module: \"ES2022\",\n moduleResolution: \"bundler\",\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: \"dist\",\n rootDir: \".\",\n },\n include: [\".\"],\n },\n null,\n 2,\n ),\n\n \"tutti.score.ts\": `import { defineScore, AnthropicProvider } from \"@tuttiai/core\"\n\nexport default defineScore({\n provider: new AnthropicProvider(),\n default_model: \"claude-sonnet-4-20250514\",\n agents: {\n assistant: {\n name: \"Assistant\",\n system_prompt: \"You are a helpful assistant.\",\n voices: [],\n }\n }\n})\n`,\n\n \"README.md\": `# ${projectName}\n\nA Tutti agent project. All agents. All together.\n\n## Setup\n\n\\`\\`\\`bash\ncp .env.example .env\n# Add your ANTHROPIC_API_KEY to .env\nnpm install\n\\`\\`\\`\n\n## Run\n\n\\`\\`\\`bash\nnpm run dev\n\\`\\`\\`\n`,\n };\n\n for (const [filename, content] of Object.entries(files)) {\n writeFileSync(join(dir, filename), content);\n }\n\n console.log();\n console.log(chalk.green(` ✔ Created ${projectName}/`));\n console.log();\n console.log(\" Next steps:\");\n console.log(chalk.cyan(` cd ${projectName}`));\n console.log(chalk.cyan(\" cp .env.example .env\"));\n console.log(chalk.cyan(\" npm install\"));\n console.log(chalk.cyan(\" npm run dev\"));\n console.log();\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport {\n TuttiRuntime,\n ScoreLoader,\n AnthropicProvider,\n OpenAIProvider,\n GeminiProvider,\n SecretsManager,\n createLogger,\n} from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nexport async function runCommand(scorePath?: string): Promise<void> {\n const file = resolve(scorePath ?? \"./tutti.score.ts\");\n\n if (!existsSync(file)) {\n logger.error({ file }, \"Score file not found\");\n console.error(chalk.dim('Run \"tutti-ai init\" to create a new project.'));\n process.exit(1);\n }\n\n let score;\n try {\n score = await ScoreLoader.load(file);\n } catch (err) {\n logger.error(\n { error: err instanceof Error ? err.message : String(err) },\n \"Failed to load score\",\n );\n process.exit(1);\n }\n\n // Validate that the provider has a valid API key\n const providerKeyMap: [unknown, string][] = [\n [AnthropicProvider, \"ANTHROPIC_API_KEY\"],\n [OpenAIProvider, \"OPENAI_API_KEY\"],\n [GeminiProvider, \"GEMINI_API_KEY\"],\n ];\n\n for (const [ProviderClass, envVar] of providerKeyMap) {\n if (score.provider instanceof (ProviderClass as new (...args: unknown[]) => unknown)) {\n const key = SecretsManager.optional(envVar);\n if (!key) {\n logger.error({ envVar }, \"Missing API key\");\n process.exit(1);\n }\n }\n }\n\n const runtime = new TuttiRuntime(score);\n const spinner = ora({ color: \"cyan\" });\n\n // Event-based execution trace\n runtime.events.on(\"agent:start\", (e) => {\n logger.info({ agent: e.agent_name }, \"Running agent\");\n });\n\n runtime.events.on(\"llm:request\", () => {\n spinner.start(\"Thinking...\");\n });\n\n runtime.events.on(\"llm:response\", () => {\n spinner.stop();\n });\n\n runtime.events.on(\"tool:start\", (e) => {\n logger.info({ tool: e.tool_name }, \"Using tool\");\n });\n\n runtime.events.on(\"tool:end\", (e) => {\n logger.debug({ tool: e.tool_name }, \"Tool done\");\n });\n\n runtime.events.on(\"tool:error\", (e) => {\n logger.error({ tool: e.tool_name }, \"Tool error\");\n });\n\n runtime.events.on(\"security:injection_detected\", (e) => {\n logger.warn({ tool: e.tool_name }, \"Potential prompt injection detected\");\n });\n\n runtime.events.on(\"budget:warning\", () => {\n logger.warn(\"Approaching token budget (80%)\");\n });\n\n runtime.events.on(\"budget:exceeded\", () => {\n logger.error(\"Token budget exceeded — stopping\");\n });\n\n // REPL\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(chalk.dim('Tutti REPL — type \"exit\" to quit\\n'));\n\n let sessionId: string | undefined;\n\n // Handle Ctrl+C\n process.on(\"SIGINT\", () => {\n console.log(chalk.dim(\"\\nGoodbye!\"));\n rl.close();\n process.exit(0);\n });\n\n try {\n while (true) {\n const input = await rl.question(chalk.cyan(\"> \"));\n const trimmed = input.trim();\n\n if (!trimmed) continue;\n if (trimmed === \"exit\" || trimmed === \"quit\") break;\n\n try {\n const result = await runtime.run(\"assistant\", trimmed, sessionId);\n sessionId = result.session_id;\n console.log(`\\n${result.output}\\n`);\n } catch (err) {\n spinner.stop();\n logger.error(\n { error: err instanceof Error ? err.message : String(err) },\n \"Something went wrong\",\n );\n }\n }\n } catch {\n // readline closed\n }\n\n console.log(chalk.dim(\"Goodbye!\"));\n rl.close();\n process.exit(0);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { createLogger } from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nconst OFFICIAL_VOICES: Record<string, { package: string; setup: string }> = {\n filesystem: {\n package: \"@tuttiai/filesystem\",\n setup: ` Add to your score:\n ${chalk.cyan('import { FilesystemVoice } from \"@tuttiai/filesystem\"')}\n ${chalk.cyan(\"voices: [new FilesystemVoice()]\")}`,\n },\n github: {\n package: \"@tuttiai/github\",\n setup: ` Add ${chalk.bold(\"GITHUB_TOKEN\")} to your .env file:\n ${chalk.cyan(\"GITHUB_TOKEN=ghp_your_token_here\")}\n\n Add to your score:\n ${chalk.cyan('import { GitHubVoice } from \"@tuttiai/github\"')}\n ${chalk.cyan(\"voices: [new GitHubVoice()]\")}`,\n },\n playwright: {\n package: \"@tuttiai/playwright\",\n setup: ` Install the browser:\n ${chalk.cyan(\"npx playwright install chromium\")}\n\n Add to your score:\n ${chalk.cyan('import { PlaywrightVoice } from \"@tuttiai/playwright\"')}\n ${chalk.cyan(\"voices: [new PlaywrightVoice()]\")}`,\n },\n postgres: {\n package: \"pg\",\n setup: ` Add ${chalk.bold(\"DATABASE_URL\")} to your .env file:\n ${chalk.cyan(\"DATABASE_URL=postgres://user:pass@localhost:5432/tutti\")}\n\n Add to your score:\n ${chalk.cyan(\"memory: { provider: 'postgres' }\")}\n\n Or with an explicit URL:\n ${chalk.cyan(\"memory: { provider: 'postgres', url: process.env.DATABASE_URL }\")}\n\n Use the async factory for initialization:\n ${chalk.cyan(\"const tutti = await TuttiRuntime.create(score)\")}`,\n },\n};\n\nfunction resolvePackageName(input: string): string {\n // Known official voice\n if (OFFICIAL_VOICES[input]) {\n return OFFICIAL_VOICES[input].package;\n }\n // Already a scoped package\n if (input.startsWith(\"@\")) {\n return input;\n }\n // Try @tuttiai/<name> convention\n return `@tuttiai/${input}`;\n}\n\nfunction isAlreadyInstalled(packageName: string): boolean {\n const pkgPath = resolve(process.cwd(), \"package.json\");\n if (!existsSync(pkgPath)) return false;\n\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n return packageName in deps;\n } catch {\n return false;\n }\n}\n\nexport async function addCommand(voiceName: string): Promise<void> {\n const packageName = resolvePackageName(voiceName);\n\n // Check if package.json exists in cwd\n const pkgPath = resolve(process.cwd(), \"package.json\");\n if (!existsSync(pkgPath)) {\n logger.error(\"No package.json found in the current directory\");\n console.error(chalk.dim('Run \"tutti-ai init\" to create a new project first.'));\n process.exit(1);\n }\n\n // Check if already installed\n if (isAlreadyInstalled(packageName)) {\n console.log(chalk.green(` ✔ ${packageName} is already installed`));\n return;\n }\n\n // Install\n const spinner = ora(`Installing ${packageName}...`).start();\n\n try {\n execSync(`npm install ${packageName}`, {\n cwd: process.cwd(),\n stdio: \"pipe\",\n });\n spinner.succeed(`Installed ${packageName}`);\n } catch (error) {\n spinner.fail(`Failed to install ${packageName}`);\n const message = error instanceof Error ? error.message : String(error);\n logger.error({ error: message, package: packageName }, \"Installation failed\");\n process.exit(1);\n }\n\n // Print setup instructions\n const official = OFFICIAL_VOICES[voiceName];\n if (official) {\n console.log();\n console.log(\" Setup:\");\n console.log(official.setup);\n console.log();\n } else {\n console.log();\n console.log(\n chalk.dim(\" Check the package README for setup instructions.\"),\n );\n console.log();\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n ScoreLoader,\n AnthropicProvider,\n OpenAIProvider,\n GeminiProvider,\n SecretsManager,\n createLogger,\n} from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nconst ok = (msg: string) => console.log(chalk.green(\" \\u2714 \" + msg));\nconst fail = (msg: string) => console.log(chalk.red(\" \\u2718 \" + msg));\n\nexport async function checkCommand(scorePath?: string): Promise<void> {\n const file = resolve(scorePath ?? \"./tutti.score.ts\");\n\n console.log(chalk.cyan(`\\nChecking ${file}...\\n`));\n\n if (!existsSync(file)) {\n fail(\"Score file not found: \" + file);\n process.exit(1);\n }\n\n // 1. Load and validate\n let score;\n try {\n score = await ScoreLoader.load(file);\n ok(\"Score file is valid\");\n } catch (err) {\n fail(\"Score validation failed\");\n logger.error(\n { error: err instanceof Error ? err.message : String(err) },\n \"Score validation failed\",\n );\n process.exit(1);\n }\n\n let hasErrors = false;\n\n // 2. Check provider and API key\n const providerChecks: [unknown, string, string][] = [\n [AnthropicProvider, \"AnthropicProvider\", \"ANTHROPIC_API_KEY\"],\n [OpenAIProvider, \"OpenAIProvider\", \"OPENAI_API_KEY\"],\n [GeminiProvider, \"GeminiProvider\", \"GEMINI_API_KEY\"],\n ];\n\n let providerDetected = false;\n for (const [ProviderClass, name, envVar] of providerChecks) {\n if (\n score.provider instanceof\n (ProviderClass as new (...args: unknown[]) => unknown)\n ) {\n providerDetected = true;\n const key = SecretsManager.optional(envVar);\n if (key) {\n ok(\"Provider: \" + name + \" (\" + envVar + \" is set)\");\n } else {\n fail(\"Provider: \" + name + \" (\" + envVar + \" is NOT set)\");\n hasErrors = true;\n }\n }\n }\n\n if (!providerDetected) {\n ok(\"Provider: custom LLMProvider\");\n }\n\n // 3. Count agents\n const agentKeys = Object.keys(score.agents);\n ok(agentKeys.length + \" agent\" + (agentKeys.length === 1 ? \"\" : \"s\") + \" configured\");\n\n // 4. Check voices\n for (const [agentKey, agent] of Object.entries(score.agents)) {\n for (const voice of agent.voices) {\n const voiceName = voice.name;\n\n // Check for known voices and their env vars\n const voiceEnvMap: Record<string, string> = {\n github: \"GITHUB_TOKEN\",\n };\n\n const envVar = voiceEnvMap[voiceName];\n if (envVar) {\n const key = SecretsManager.optional(envVar);\n if (key) {\n ok(\n \"Voice: \" + voiceName + \" on \" + agentKey + \" (\" + envVar + \" is set)\",\n );\n } else {\n fail(\n \"Voice: \" + voiceName + \" on \" + agentKey + \" (\" + envVar + \" is NOT set)\",\n );\n hasErrors = true;\n }\n } else {\n ok(\"Voice: \" + voiceName + \" on \" + agentKey + \" (installed)\");\n }\n }\n }\n\n // Final summary\n console.log(\"\");\n if (hasErrors) {\n console.log(\n chalk.yellow(\"Some checks failed. Fix the issues above and re-run.\"),\n );\n process.exit(1);\n } else {\n console.log(\n chalk.green(\"All checks passed.\") +\n chalk.dim(\" Run tutti-ai run to start.\"),\n );\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { exec } from \"node:child_process\";\nimport express from \"express\";\nimport chalk from \"chalk\";\nimport {\n TuttiRuntime,\n ScoreLoader,\n createLogger,\n} from \"@tuttiai/core\";\nimport type { Response as ExpressResponse } from \"express\";\n\nconst logger = createLogger(\"tutti-studio\");\nconst PORT = 4747;\n\nfunction safeStringify(obj: unknown): string {\n return JSON.stringify(obj, (_key, value) => {\n if (value instanceof Error) return { message: value.message, name: value.name };\n if (typeof value === \"function\") return undefined;\n return value;\n });\n}\n\nfunction openBrowser(url: string): void {\n const cmd =\n process.platform === \"darwin\" ? \"open\" :\n process.platform === \"win32\" ? \"start\" :\n \"xdg-open\";\n exec(cmd + \" \" + url);\n}\n\nexport async function studioCommand(scorePath?: string): Promise<void> {\n const file = resolve(scorePath ?? \"./tutti.score.ts\");\n\n if (!existsSync(file)) {\n logger.error({ file }, \"Score file not found\");\n console.error(chalk.dim('Run \"tutti-ai init\" to create a new project.'));\n process.exit(1);\n }\n\n let score;\n try {\n score = await ScoreLoader.load(file);\n } catch (err) {\n logger.error({ error: err instanceof Error ? err.message : String(err) }, \"Failed to load score\");\n process.exit(1);\n }\n\n const runtime = new TuttiRuntime(score);\n\n // Track sessions via events\n const sessionRegistry = new Map<string, { agent_name: string; created_at: Date }>();\n runtime.events.on(\"agent:start\", (e) => {\n if (!sessionRegistry.has(e.session_id)) {\n sessionRegistry.set(e.session_id, { agent_name: e.agent_name, created_at: new Date() });\n }\n });\n\n // SSE clients\n const sseClients = new Set<ExpressResponse>();\n runtime.events.onAny((event) => {\n const data = safeStringify(event);\n for (const client of sseClients) {\n client.write(\"event: tutti\\ndata: \" + data + \"\\n\\n\");\n }\n });\n\n // Express\n const app = express();\n app.use(express.json());\n\n // SSE endpoint\n app.get(\"/events\", (_req, res) => {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n res.write(\":\\n\\n\");\n sseClients.add(res);\n _req.on(\"close\", () => sseClients.delete(res));\n });\n\n // REST API\n app.get(\"/api/score\", (_req, res) => {\n const agents = Object.fromEntries(\n Object.entries(runtime.score.agents).map(([id, agent]) => [\n id,\n {\n name: agent.name,\n description: agent.description,\n model: agent.model,\n role: agent.role,\n delegates: agent.delegates,\n voice_count: agent.voices.length,\n voices: agent.voices.map((v) => v.name),\n },\n ]),\n );\n res.json({\n name: runtime.score.name,\n description: runtime.score.description,\n default_model: runtime.score.default_model,\n entry: runtime.score.entry,\n agents,\n });\n });\n\n app.get(\"/api/sessions\", (_req, res) => {\n const sessions = Array.from(sessionRegistry.entries()).map(([id, meta]) => {\n const session = runtime.getSession(id);\n return {\n id,\n agent_name: meta.agent_name,\n message_count: session?.messages.length ?? 0,\n created_at: meta.created_at,\n };\n });\n res.json(sessions.reverse());\n });\n\n app.get(\"/api/sessions/:id\", (req, res) => {\n const session = runtime.getSession(req.params.id);\n if (!session) { res.status(404).json({ error: \"Session not found\" }); return; }\n res.json(session);\n });\n\n app.post(\"/api/run\", async (req, res) => {\n const { agent, input, session_id } = req.body as { agent: string; input: string; session_id?: string };\n if (!agent || !input) { res.status(400).json({ error: \"agent and input are required\" }); return; }\n try {\n const result = await runtime.run(agent, input, session_id);\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: err instanceof Error ? err.message : String(err) });\n }\n });\n\n // Serve UI\n app.get(\"/\", (_req, res) => {\n res.type(\"html\").send(getStudioHtml());\n });\n\n app.listen(PORT, () => {\n const url = \"http://localhost:\" + PORT;\n console.log();\n console.log(chalk.bold(\" Tutti Studio\"));\n console.log(chalk.dim(\" \" + url));\n console.log();\n console.log(chalk.dim(\" Score: \") + (runtime.score.name ?? file));\n console.log(chalk.dim(\" Agents: \") + Object.keys(runtime.score.agents).join(\", \"));\n console.log();\n openBrowser(url);\n });\n\n process.on(\"SIGINT\", () => {\n console.log(chalk.dim(\"\\nShutting down Tutti Studio...\"));\n process.exit(0);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Inline HTML UI\n// ---------------------------------------------------------------------------\n\nfunction getStudioHtml(): string {\n return '<!DOCTYPE html>\\\n<html lang=\"en\">\\\n<head>\\\n<meta charset=\"utf-8\">\\\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\\\n<title>Tutti Studio</title>\\\n<style>\\\n*{margin:0;padding:0;box-sizing:border-box}\\\n:root{\\\n--bg:#0a0a0f;--panel:#12121a;--card:#1a1a26;--input:#0f0f17;\\\n--border:#2a2a3a;--text:#e2e8f0;--muted:#64748b;\\\n--purple:#8b5cf6;--teal:#14b8a6;--blue:#3b82f6;--green:#10b981;\\\n--red:#ef4444;--orange:#f97316;--amber:#f59e0b;--indigo:#6366f1;\\\n}\\\nhtml,body{height:100%;font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);font-size:13px}\\\n#app{display:flex;flex-direction:column;height:100vh}\\\n\\\nheader{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;border-bottom:1px solid var(--border);background:var(--panel)}\\\nheader .logo{font-weight:700;font-size:15px;letter-spacing:.5px}\\\nheader .logo span{color:var(--purple)}\\\nheader .meta{color:var(--muted);font-size:12px}\\\nheader .status{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--muted)}\\\nheader .dot{width:7px;height:7px;border-radius:50%;background:var(--green)}\\\nheader .dot.off{background:var(--red)}\\\n\\\nmain{display:grid;grid-template-columns:260px 1fr 280px;flex:1;overflow:hidden;border-bottom:1px solid var(--border)}\\\n\\\n.panel{display:flex;flex-direction:column;border-right:1px solid var(--border);overflow:hidden}\\\n.panel:last-child{border-right:none}\\\n.panel-title{padding:10px 14px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);border-bottom:1px solid var(--border);background:var(--panel);flex-shrink:0}\\\n.panel-body{flex:1;overflow-y:auto;padding:10px}\\\n.panel-body::-webkit-scrollbar{width:5px}\\\n.panel-body::-webkit-scrollbar-track{background:transparent}\\\n.panel-body::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}\\\n\\\n#graph-panel .panel-body{padding:0;display:flex;align-items:center;justify-content:center}\\\n#graph-panel svg text{font-family:system-ui,-apple-system,sans-serif}\\\n\\\n#events-panel{display:flex;flex-direction:column}\\\n#event-stream{flex:1;overflow-y:auto;padding:10px}\\\n#event-stream::-webkit-scrollbar{width:5px}\\\n#event-stream::-webkit-scrollbar-track{background:transparent}\\\n#event-stream::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}\\\n\\\n.ev{padding:7px 10px;margin-bottom:6px;border-radius:6px;background:var(--card);border-left:3px solid var(--muted);font-size:12px;line-height:1.5}\\\n.ev .ev-head{display:flex;justify-content:space-between;align-items:center}\\\n.ev .ev-type{font-weight:600;font-family:\"SF Mono\",Menlo,monospace;font-size:11px}\\\n.ev .ev-time{color:var(--muted);font-size:10px;font-family:\"SF Mono\",Menlo,monospace}\\\n.ev .ev-detail{color:var(--muted);margin-top:3px;font-size:11px;word-break:break-all}\\\n.ev.agent{border-left-color:var(--purple)}.ev.agent .ev-type{color:var(--purple)}\\\n.ev.turn{border-left-color:var(--blue)}.ev.turn .ev-type{color:var(--blue)}\\\n.ev.llm{border-left-color:var(--green)}.ev.llm .ev-type{color:var(--green)}\\\n.ev.tool{border-left-color:var(--teal)}.ev.tool .ev-type{color:var(--teal)}\\\n.ev.tool-error{border-left-color:var(--red)}.ev.tool-error .ev-type{color:var(--red)}\\\n.ev.security{border-left-color:var(--orange)}.ev.security .ev-type{color:var(--orange)}\\\n.ev.budget-warn{border-left-color:var(--amber)}.ev.budget-warn .ev-type{color:var(--amber)}\\\n.ev.budget-exceed{border-left-color:var(--red)}.ev.budget-exceed .ev-type{color:var(--red)}\\\n.ev.delegate{border-left-color:var(--indigo)}.ev.delegate .ev-type{color:var(--indigo)}\\\n\\\n#input-bar{display:flex;gap:8px;padding:10px 12px;border-top:1px solid var(--border);background:var(--panel);flex-shrink:0}\\\n#agent-select{background:var(--input);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:6px 10px;font-size:12px;outline:none;cursor:pointer;min-width:110px}\\\n#user-input{flex:1;background:var(--input);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:6px 12px;font-size:13px;outline:none}\\\n#user-input:focus{border-color:var(--purple)}\\\n#send-btn{background:var(--purple);color:#fff;border:none;border-radius:6px;padding:6px 16px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap}\\\n#send-btn:hover{opacity:.9}\\\n#send-btn:disabled{opacity:.4;cursor:default}\\\n\\\n.session-item{padding:8px 10px;margin-bottom:4px;border-radius:6px;background:var(--card);cursor:pointer;transition:background .15s}\\\n.session-item:hover{background:#22223a}\\\n.session-item.active{background:#22223a;border:1px solid var(--purple)}\\\n.session-id{font-family:\"SF Mono\",Menlo,monospace;font-size:11px;color:var(--purple)}\\\n.session-meta{font-size:11px;color:var(--muted);margin-top:2px}\\\n\\\n#session-detail{margin-top:10px;border-top:1px solid var(--border);padding-top:10px}\\\n.msg{padding:6px 8px;margin-bottom:4px;border-radius:5px;font-size:12px;line-height:1.5;word-break:break-word}\\\n.msg.user{background:#1c1c3a;border-left:2px solid var(--blue)}\\\n.msg.assistant{background:#1a2a1a;border-left:2px solid var(--green)}\\\n.msg .msg-role{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;margin-bottom:2px}\\\n.msg.user .msg-role{color:var(--blue)}\\\n.msg.assistant .msg-role{color:var(--green)}\\\n\\\nfooter{display:flex;align-items:center;gap:32px;padding:8px 20px;background:var(--panel);font-size:12px}\\\n.token-item{display:flex;align-items:center;gap:6px}\\\n.token-label{color:var(--muted)}\\\n.token-val{font-family:\"SF Mono\",Menlo,monospace;font-weight:600}\\\n.token-val.input{color:var(--blue)}\\\n.token-val.output{color:var(--green)}\\\n.token-val.cost{color:var(--amber)}\\\n\\\n.empty{color:var(--muted);text-align:center;padding:30px 10px;font-size:12px}\\\n</style>\\\n</head>\\\n<body>\\\n<div id=\"app\">\\\n\\\n<header>\\\n <div class=\"logo\"><span>&#9835;</span> Tutti Studio</div>\\\n <div class=\"meta\" id=\"score-name\"></div>\\\n <div class=\"status\"><div class=\"dot\" id=\"sse-dot\"></div><span id=\"sse-label\">connecting</span></div>\\\n</header>\\\n\\\n<main>\\\n <div class=\"panel\" id=\"graph-panel\">\\\n <div class=\"panel-title\">Agent Graph</div>\\\n <div class=\"panel-body\" id=\"graph-body\"></div>\\\n </div>\\\n\\\n <div class=\"panel\" id=\"events-panel\">\\\n <div class=\"panel-title\">Live Event Stream</div>\\\n <div id=\"event-stream\"><div class=\"empty\">Waiting for events&hellip;<br>Send a message below to start an agent run.</div></div>\\\n <div id=\"input-bar\">\\\n <select id=\"agent-select\"></select>\\\n <input id=\"user-input\" placeholder=\"Type a message&hellip;\" autocomplete=\"off\">\\\n <button id=\"send-btn\">Send</button>\\\n </div>\\\n </div>\\\n\\\n <div class=\"panel\" id=\"sessions-panel\">\\\n <div class=\"panel-title\">Sessions</div>\\\n <div class=\"panel-body\" id=\"sessions-body\"><div class=\"empty\">No sessions yet</div></div>\\\n </div>\\\n</main>\\\n\\\n<footer>\\\n <div class=\"token-item\"><span class=\"token-label\">&#x2193; Input</span><span class=\"token-val input\" id=\"tok-in\">0</span></div>\\\n <div class=\"token-item\"><span class=\"token-label\">&#x2191; Output</span><span class=\"token-val output\" id=\"tok-out\">0</span></div>\\\n <div class=\"token-item\"><span class=\"token-label\">$ Est. cost</span><span class=\"token-val cost\" id=\"tok-cost\">0.0000</span></div>\\\n</footer>\\\n\\\n</div>\\\n\\\n<script>\\\n(function(){\\\n\\\nvar tokIn=0,tokOut=0;\\\nvar sessionMap={};\\\nvar activeSession=null;\\\n\\\n/* ---- helpers ---- */\\\nfunction esc(s){var d=document.createElement(\"div\");d.textContent=s;return d.innerHTML}\\\nfunction fmt(n){return n.toLocaleString()}\\\nfunction timeStr(){var d=new Date();return (\"0\"+d.getHours()).slice(-2)+\":\"+(\"0\"+d.getMinutes()).slice(-2)+\":\"+(\"0\"+d.getSeconds()).slice(-2)}\\\nfunction truncId(id){return id.slice(0,8)}\\\n\\\n/* ---- score + graph ---- */\\\nfunction loadScore(){\\\n fetch(\"/api/score\").then(function(r){return r.json()}).then(function(s){\\\n document.getElementById(\"score-name\").textContent=s.name||\"tutti.score.ts\";\\\n var sel=document.getElementById(\"agent-select\");\\\n sel.innerHTML=\"\";\\\n Object.keys(s.agents).forEach(function(id){\\\n var o=document.createElement(\"option\");o.value=id;o.textContent=s.agents[id].name;sel.appendChild(o);\\\n });\\\n renderGraph(s);\\\n });\\\n}\\\n\\\nfunction renderGraph(score){\\\n var body=document.getElementById(\"graph-body\");\\\n var W=260,ids=Object.keys(score.agents),N=ids.length;\\\n if(N===0){body.innerHTML=\"<div class=\\\\\"empty\\\\\">No agents</div>\";return}\\\n var hasDelegate=false;\\\n ids.forEach(function(id){if(score.agents[id].delegates&&score.agents[id].delegates.length)hasDelegate=true});\\\n var nodeR=26,padY=90,padTop=50;\\\n var leftIds=[],rightIds=[];\\\n if(hasDelegate){\\\n ids.forEach(function(id){var a=score.agents[id];if(a.delegates&&a.delegates.length)leftIds.push(id);else rightIds.push(id)});\\\n }else{leftIds=ids}\\\n var cols=hasDelegate?2:1;\\\n var cx1=cols===1?W/2:72,cx2=W-72;\\\n var H=Math.max(leftIds.length,rightIds.length)*padY+padTop*2;\\\n if(H<200)H=200;\\\n var pos={};\\\n var svg=\\'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"\\'+W+\\'\" height=\"\\'+H+\\'\" viewBox=\"0 0 \\'+W+\" \"+H+\\'\">\\';\\\n svg+=\\'<defs><marker id=\"ah\" viewBox=\"0 0 10 10\" refX=\"10\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#64748b\"/></marker></defs>\\';\\\n function drawNode(id,cx,cy){\\\n var a=score.agents[id];\\\n var col=a.role===\"orchestrator\"?\"#8b5cf6\":\"#14b8a6\";\\\n pos[id]={x:cx,y:cy};\\\n svg+=\\'<circle cx=\"\\'+cx+\\'\" cy=\"\\'+cy+\\'\" r=\"\\'+nodeR+\\'\" fill=\"\\'+col+\\'\" fill-opacity=\"0.15\" stroke=\"\\'+col+\\'\" stroke-width=\"2\"/>\\';\\\n svg+=\\'<text x=\"\\'+cx+\\'\" y=\"\\'+(cy+4)+\\'\" text-anchor=\"middle\" fill=\"#e2e8f0\" font-size=\"10\" font-weight=\"600\">\\'+esc(a.name)+\\'</text>\\';\\\n var model=a.model||score.default_model||\"\";\\\n if(model){var sh=model.replace(/-\\\\d{8}$/,\"\");if(sh.length>18)sh=sh.slice(0,18)+\"\\\\u2026\";svg+=\\'<text x=\"\\'+cx+\\'\" y=\"\\'+(cy+nodeR+14)+\\'\" text-anchor=\"middle\" fill=\"#64748b\" font-size=\"9\">\\'+esc(sh)+\\'</text>\\'}\\\n svg+=\\'<text x=\"\\'+cx+\\'\" y=\"\\'+(cy+nodeR+26)+\\'\" text-anchor=\"middle\" fill=\"#64748b\" font-size=\"9\">\\'+a.voice_count+\" voice\"+(a.voice_count!==1?\"s\":\"\")+\\'</text>\\';\\\n }\\\n leftIds.forEach(function(id,i){drawNode(id,cx1,padTop+i*padY)});\\\n rightIds.forEach(function(id,i){drawNode(id,cx2,padTop+i*padY)});\\\n ids.forEach(function(id){\\\n var a=score.agents[id];\\\n if(a.delegates)a.delegates.forEach(function(did){\\\n if(pos[id]&&pos[did]){\\\n var x1=pos[id].x+nodeR,y1=pos[id].y,x2=pos[did].x-nodeR,y2=pos[did].y;\\\n var mx=(x1+x2)/2;\\\n svg+=\\'<path d=\"M\\'+x1+\" \"+y1+\" C\"+mx+\" \"+y1+\" \"+mx+\" \"+y2+\" \"+x2+\" \"+y2+\\'\" fill=\"none\" stroke=\"#64748b\" stroke-width=\"1.5\" stroke-dasharray=\"4 3\" marker-end=\"url(#ah)\"/>\\';\\\n }\\\n });\\\n });\\\n svg+=\"</svg>\";\\\n body.innerHTML=svg;\\\n}\\\n\\\n/* ---- SSE ---- */\\\nfunction connectSSE(){\\\n var es=new EventSource(\"/events\");\\\n es.addEventListener(\"tutti\",function(e){\\\n var ev=JSON.parse(e.data);\\\n addEvent(ev);\\\n if(ev.type===\"llm:response\"&&ev.response&&ev.response.usage){\\\n tokIn+=ev.response.usage.input_tokens||0;\\\n tokOut+=ev.response.usage.output_tokens||0;\\\n document.getElementById(\"tok-in\").textContent=fmt(tokIn);\\\n document.getElementById(\"tok-out\").textContent=fmt(tokOut);\\\n document.getElementById(\"tok-cost\").textContent=estimateCost(tokIn,tokOut);\\\n }\\\n if(ev.type===\"agent:start\"||ev.type===\"agent:end\")refreshSessions();\\\n });\\\n es.onopen=function(){document.getElementById(\"sse-dot\").className=\"dot\";document.getElementById(\"sse-label\").textContent=\"connected\"};\\\n es.onerror=function(){document.getElementById(\"sse-dot\").className=\"dot off\";document.getElementById(\"sse-label\").textContent=\"disconnected\"};\\\n}\\\n\\\nfunction estimateCost(inp,out){\\\n var c=(inp/1e6)*3+(out/1e6)*15;\\\n return c.toFixed(4);\\\n}\\\n\\\nfunction evClass(t){\\\n if(t.indexOf(\"agent\")===0)return \"agent\";\\\n if(t.indexOf(\"turn\")===0)return \"turn\";\\\n if(t===\"llm:request\"||t===\"llm:response\")return \"llm\";\\\n if(t===\"tool:error\")return \"tool-error\";\\\n if(t.indexOf(\"tool\")===0)return \"tool\";\\\n if(t.indexOf(\"security\")===0)return \"security\";\\\n if(t===\"budget:warning\")return \"budget-warn\";\\\n if(t===\"budget:exceeded\")return \"budget-exceed\";\\\n if(t.indexOf(\"delegate\")===0)return \"delegate\";\\\n return \"\";\\\n}\\\n\\\nfunction evDetail(ev){\\\n var parts=[];\\\n if(ev.agent_name)parts.push(\"agent: \"+ev.agent_name);\\\n if(ev.session_id)parts.push(\"session: \"+truncId(ev.session_id));\\\n if(ev.turn!==undefined)parts.push(\"turn: \"+ev.turn);\\\n if(ev.tool_name)parts.push(\"tool: \"+ev.tool_name);\\\n if(ev.from)parts.push(\"from: \"+ev.from);\\\n if(ev.to)parts.push(\"to: \"+ev.to);\\\n if(ev.tokens!==undefined)parts.push(\"tokens: \"+fmt(ev.tokens));\\\n if(ev.cost_usd!==undefined)parts.push(\"cost: $\"+ev.cost_usd.toFixed(4));\\\n if(ev.response&&ev.response.usage)parts.push(\"tokens: \"+fmt(ev.response.usage.input_tokens)+\" in / \"+fmt(ev.response.usage.output_tokens)+\" out\");\\\n if(ev.error){var em=typeof ev.error===\"object\"?ev.error.message||\"\":ev.error;if(em)parts.push(\"error: \"+em)}\\\n if(ev.patterns)parts.push(\"patterns: \"+ev.patterns.join(\", \"));\\\n return parts.join(\" &middot; \");\\\n}\\\n\\\nvar firstEvent=true;\\\nfunction addEvent(ev){\\\n var stream=document.getElementById(\"event-stream\");\\\n if(firstEvent){stream.innerHTML=\"\";firstEvent=false}\\\n var div=document.createElement(\"div\");\\\n div.className=\"ev \"+evClass(ev.type);\\\n div.innerHTML=\\'<div class=\"ev-head\"><span class=\"ev-type\">\\'+esc(ev.type)+\\'</span><span class=\"ev-time\">\\'+timeStr()+\\'</span></div>\\';\\\n var det=evDetail(ev);\\\n if(det)div.innerHTML+=\\'<div class=\"ev-detail\">\\'+det+\"</div>\";\\\n stream.appendChild(div);\\\n stream.scrollTop=stream.scrollHeight;\\\n}\\\n\\\n/* ---- sessions ---- */\\\nfunction refreshSessions(){\\\n fetch(\"/api/sessions\").then(function(r){return r.json()}).then(function(list){\\\n var body=document.getElementById(\"sessions-body\");\\\n if(!list.length){body.innerHTML=\\'<div class=\"empty\">No sessions yet</div>\\';return}\\\n var html=\"\";\\\n list.forEach(function(s){\\\n var cls=\"session-item\"+(activeSession===s.id?\" active\":\"\");\\\n html+=\\'<div class=\"\\'+cls+\\'\" data-id=\"\\'+s.id+\\'\">\\';\\\n html+=\\'<div class=\"session-id\">\\'+truncId(s.id)+\"</div>\";\\\n html+=\\'<div class=\"session-meta\">\\'+esc(s.agent_name)+\" &middot; \"+s.message_count+\" msgs</div>\";\\\n html+=\"</div>\";\\\n });\\\n if(activeSession)html+=\\'<div id=\"session-detail\"></div>\\';\\\n body.innerHTML=html;\\\n body.querySelectorAll(\".session-item\").forEach(function(el){\\\n el.addEventListener(\"click\",function(){selectSession(el.getAttribute(\"data-id\"))});\\\n });\\\n if(activeSession)loadSessionDetail(activeSession);\\\n });\\\n}\\\n\\\nfunction selectSession(id){\\\n activeSession=activeSession===id?null:id;\\\n refreshSessions();\\\n}\\\n\\\nfunction loadSessionDetail(id){\\\n var det=document.getElementById(\"session-detail\");\\\n if(!det)return;\\\n fetch(\"/api/sessions/\"+id).then(function(r){return r.json()}).then(function(session){\\\n if(!session||session.error){det.innerHTML=\\'<div class=\"empty\">Session not found</div>\\';return}\\\n var html=\"\";\\\n (session.messages||[]).forEach(function(m){\\\n var role=m.role;\\\n var text=\"\";\\\n if(typeof m.content===\"string\")text=m.content;\\\n else if(Array.isArray(m.content)){\\\n m.content.forEach(function(b){\\\n if(b.type===\"text\")text+=b.text+\"\\\\n\";\\\n else if(b.type===\"tool_use\")text+=\"[tool_use: \"+b.name+\"]\\\\n\";\\\n else if(b.type===\"tool_result\")text+=\"[tool_result]\\\\n\";\\\n });\\\n }\\\n html+=\\'<div class=\"msg \\'+role+\\'\"><div class=\"msg-role\">\\'+role+\"</div>\"+esc(text.trim())+\"</div>\";\\\n });\\\n det.innerHTML=html;\\\n });\\\n}\\\n\\\n/* ---- send ---- */\\\nfunction sendMessage(){\\\n var agentSel=document.getElementById(\"agent-select\");\\\n var inputEl=document.getElementById(\"user-input\");\\\n var btn=document.getElementById(\"send-btn\");\\\n var agent=agentSel.value;\\\n var input=inputEl.value.trim();\\\n if(!input)return;\\\n btn.disabled=true;btn.textContent=\"Running\\\\u2026\";\\\n inputEl.value=\"\";\\\n var sid=sessionMap[agent]||undefined;\\\n fetch(\"/api/run\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify({agent:agent,input:input,session_id:sid})})\\\n .then(function(r){return r.json()})\\\n .then(function(result){\\\n if(result.session_id)sessionMap[agent]=result.session_id;\\\n if(result.output){\\\n addEvent({type:\"__output\",agent_name:agent,output:result.output});\\\n }\\\n refreshSessions();\\\n })\\\n .catch(function(err){addEvent({type:\"__error\",error:err.message||String(err)})})\\\n .finally(function(){btn.disabled=false;btn.textContent=\"Send\"});\\\n}\\\n\\\ndocument.getElementById(\"send-btn\").addEventListener(\"click\",sendMessage);\\\ndocument.getElementById(\"user-input\").addEventListener(\"keydown\",function(e){if(e.key===\"Enter\"&&!e.shiftKey){e.preventDefault();sendMessage()}});\\\n\\\n/* ---- init ---- */\\\nloadScore();\\\nconnectSSE();\\\n\\\n})();\\\n</script>\\\n</body>\\\n</html>';\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AAGvB,SAAS,gBAAAA,qBAAoB;AAa7B,SAAS,eAAe;;;AChBxB,SAAS,WAAW,eAAe,kBAAkB;AACrD,SAAS,YAAY;AACrB,OAAO,WAAW;AAClB,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAE7B,IAAM,SAAS,aAAa,WAAW;AAEvC,IAAM,EAAE,OAAO,IAAI;AAEnB,eAAsB,YAAY,aAAqC;AACrE,MAAI,CAAC,aAAa;AAChB,UAAM,WAAW,MAAM,OAAgC;AAAA,MACrD,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AACD,kBAAc,SAAS;AAAA,EACzB;AAEA,MAAI,CAAC,aAAa;AAChB,WAAO,MAAM,0BAA0B;AACvC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,KAAK,QAAQ,IAAI,GAAG,WAAW;AAE3C,MAAI,WAAW,GAAG,GAAG;AACnB,WAAO,MAAM,EAAE,KAAK,GAAG,WAAW,IAAI,GAAG,0BAA0B;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAElC,QAAM,QAAgC;AAAA,IACpC,gBAAgB,KAAK;AAAA,MACnB;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,UACP,KAAK;AAAA,UACL,OAAO;AAAA,QACT;AAAA,QACA,cAAc;AAAA,UACZ,iBAAiB;AAAA,UACjB,kBAAkB;AAAA,QACpB;AAAA,QACA,iBAAiB;AAAA,UACf,KAAK;AAAA,UACL,YAAY;AAAA,QACd;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,gBACE;AAAA,IAOF,cAAc;AAAA,IAEd,iBAAiB,KAAK;AAAA,MACpB;AAAA,QACE,iBAAiB;AAAA,UACf,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,kBAAkB;AAAA,UAClB,iBAAiB;AAAA,UACjB,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,SAAS;AAAA,QACX;AAAA,QACA,SAAS,CAAC,GAAG;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAelB,aAAa,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkB/B;AAEA,aAAW,CAAC,UAAU,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACvD,kBAAc,KAAK,KAAK,QAAQ,GAAG,OAAO;AAAA,EAC5C;AAEA,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,MAAM,oBAAe,WAAW,GAAG,CAAC;AACtD,UAAQ,IAAI;AACZ,UAAQ,IAAI,eAAe;AAC3B,UAAQ,IAAI,MAAM,KAAK,UAAU,WAAW,EAAE,CAAC;AAC/C,UAAQ,IAAI,MAAM,KAAK,0BAA0B,CAAC;AAClD,UAAQ,IAAI,MAAM,KAAK,iBAAiB,CAAC;AACzC,UAAQ,IAAI,MAAM,KAAK,iBAAiB,CAAC;AACzC,UAAQ,IAAI;AACd;;;ACrIA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAChC,OAAOC,YAAW;AAClB,OAAO,SAAS;AAChB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAAC;AAAA,OACK;AAEP,IAAMC,UAASD,cAAa,WAAW;AAEvC,eAAsB,WAAW,WAAmC;AAClE,QAAM,OAAO,QAAQ,aAAa,kBAAkB;AAEpD,MAAI,CAACF,YAAW,IAAI,GAAG;AACrB,IAAAG,QAAO,MAAM,EAAE,KAAK,GAAG,sBAAsB;AAC7C,YAAQ,MAAMF,OAAM,IAAI,8CAA8C,CAAC;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,YAAY,KAAK,IAAI;AAAA,EACrC,SAAS,KAAK;AACZ,IAAAE,QAAO;AAAA,MACL,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,MAC1D;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,iBAAsC;AAAA,IAC1C,CAAC,mBAAmB,mBAAmB;AAAA,IACvC,CAAC,gBAAgB,gBAAgB;AAAA,IACjC,CAAC,gBAAgB,gBAAgB;AAAA,EACnC;AAEA,aAAW,CAAC,eAAe,MAAM,KAAK,gBAAgB;AACpD,QAAI,MAAM,oBAAqB,eAAuD;AACpF,YAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,UAAI,CAAC,KAAK;AACR,QAAAA,QAAO,MAAM,EAAE,OAAO,GAAG,iBAAiB;AAC1C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,aAAa,KAAK;AACtC,QAAM,UAAU,IAAI,EAAE,OAAO,OAAO,CAAC;AAGrC,UAAQ,OAAO,GAAG,eAAe,CAAC,MAAM;AACtC,IAAAA,QAAO,KAAK,EAAE,OAAO,EAAE,WAAW,GAAG,eAAe;AAAA,EACtD,CAAC;AAED,UAAQ,OAAO,GAAG,eAAe,MAAM;AACrC,YAAQ,MAAM,aAAa;AAAA,EAC7B,CAAC;AAED,UAAQ,OAAO,GAAG,gBAAgB,MAAM;AACtC,YAAQ,KAAK;AAAA,EACf,CAAC;AAED,UAAQ,OAAO,GAAG,cAAc,CAAC,MAAM;AACrC,IAAAA,QAAO,KAAK,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY;AAAA,EACjD,CAAC;AAED,UAAQ,OAAO,GAAG,YAAY,CAAC,MAAM;AACnC,IAAAA,QAAO,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,WAAW;AAAA,EACjD,CAAC;AAED,UAAQ,OAAO,GAAG,cAAc,CAAC,MAAM;AACrC,IAAAA,QAAO,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY;AAAA,EAClD,CAAC;AAED,UAAQ,OAAO,GAAG,+BAA+B,CAAC,MAAM;AACtD,IAAAA,QAAO,KAAK,EAAE,MAAM,EAAE,UAAU,GAAG,qCAAqC;AAAA,EAC1E,CAAC;AAED,UAAQ,OAAO,GAAG,kBAAkB,MAAM;AACxC,IAAAA,QAAO,KAAK,gCAAgC;AAAA,EAC9C,CAAC;AAED,UAAQ,OAAO,GAAG,mBAAmB,MAAM;AACzC,IAAAA,QAAO,MAAM,uCAAkC;AAAA,EACjD,CAAC;AAGD,QAAM,KAAK,gBAAgB;AAAA,IACzB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,UAAQ,IAAIF,OAAM,IAAI,yCAAoC,CAAC;AAE3D,MAAI;AAGJ,UAAQ,GAAG,UAAU,MAAM;AACzB,YAAQ,IAAIA,OAAM,IAAI,YAAY,CAAC;AACnC,OAAG,MAAM;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAED,MAAI;AACF,WAAO,MAAM;AACX,YAAM,QAAQ,MAAM,GAAG,SAASA,OAAM,KAAK,IAAI,CAAC;AAChD,YAAM,UAAU,MAAM,KAAK;AAE3B,UAAI,CAAC,QAAS;AACd,UAAI,YAAY,UAAU,YAAY,OAAQ;AAE9C,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ,IAAI,aAAa,SAAS,SAAS;AAChE,oBAAY,OAAO;AACnB,gBAAQ,IAAI;AAAA,EAAK,OAAO,MAAM;AAAA,CAAI;AAAA,MACpC,SAAS,KAAK;AACZ,gBAAQ,KAAK;AACb,QAAAE,QAAO;AAAA,UACL,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,UAC1D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,UAAQ,IAAIF,OAAM,IAAI,UAAU,CAAC;AACjC,KAAG,MAAM;AACT,UAAQ,KAAK,CAAC;AAChB;;;AC1IA,SAAS,cAAAG,aAAY,oBAAoB;AACzC,SAAS,WAAAC,gBAAe;AACxB,SAAS,gBAAgB;AACzB,OAAOC,YAAW;AAClB,OAAOC,UAAS;AAChB,SAAS,gBAAAC,qBAAoB;AAE7B,IAAMC,UAASD,cAAa,WAAW;AAEvC,IAAM,kBAAsE;AAAA,EAC1E,YAAY;AAAA,IACV,SAAS;AAAA,IACT,OAAO;AAAA,MACLF,OAAM,KAAK,uDAAuD,CAAC;AAAA,MACnEA,OAAM,KAAK,iCAAiC,CAAC;AAAA,EACjD;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,OAAO,SAASA,OAAM,KAAK,cAAc,CAAC;AAAA,MACxCA,OAAM,KAAK,kCAAkC,CAAC;AAAA;AAAA;AAAA,MAG9CA,OAAM,KAAK,+CAA+C,CAAC;AAAA,MAC3DA,OAAM,KAAK,6BAA6B,CAAC;AAAA,EAC7C;AAAA,EACA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,OAAO;AAAA,MACLA,OAAM,KAAK,iCAAiC,CAAC;AAAA;AAAA;AAAA,MAG7CA,OAAM,KAAK,uDAAuD,CAAC;AAAA,MACnEA,OAAM,KAAK,iCAAiC,CAAC;AAAA,EACjD;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,OAAO,SAASA,OAAM,KAAK,cAAc,CAAC;AAAA,MACxCA,OAAM,KAAK,wDAAwD,CAAC;AAAA;AAAA;AAAA,MAGpEA,OAAM,KAAK,kCAAkC,CAAC;AAAA;AAAA;AAAA,MAG9CA,OAAM,KAAK,iEAAiE,CAAC;AAAA;AAAA;AAAA,MAG7EA,OAAM,KAAK,gDAAgD,CAAC;AAAA,EAChE;AACF;AAEA,SAAS,mBAAmB,OAAuB;AAEjD,MAAI,gBAAgB,KAAK,GAAG;AAC1B,WAAO,gBAAgB,KAAK,EAAE;AAAA,EAChC;AAEA,MAAI,MAAM,WAAW,GAAG,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,KAAK;AAC1B;AAEA,SAAS,mBAAmB,aAA8B;AACxD,QAAM,UAAUD,SAAQ,QAAQ,IAAI,GAAG,cAAc;AACrD,MAAI,CAACD,YAAW,OAAO,EAAG,QAAO;AAEjC,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AACrD,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,WAAO,eAAe;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,WAAW,WAAkC;AACjE,QAAM,cAAc,mBAAmB,SAAS;AAGhD,QAAM,UAAUC,SAAQ,QAAQ,IAAI,GAAG,cAAc;AACrD,MAAI,CAACD,YAAW,OAAO,GAAG;AACxB,IAAAK,QAAO,MAAM,gDAAgD;AAC7D,YAAQ,MAAMH,OAAM,IAAI,oDAAoD,CAAC;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,mBAAmB,WAAW,GAAG;AACnC,YAAQ,IAAIA,OAAM,MAAM,YAAO,WAAW,uBAAuB,CAAC;AAClE;AAAA,EACF;AAGA,QAAM,UAAUC,KAAI,cAAc,WAAW,KAAK,EAAE,MAAM;AAE1D,MAAI;AACF,aAAS,eAAe,WAAW,IAAI;AAAA,MACrC,KAAK,QAAQ,IAAI;AAAA,MACjB,OAAO;AAAA,IACT,CAAC;AACD,YAAQ,QAAQ,aAAa,WAAW,EAAE;AAAA,EAC5C,SAAS,OAAO;AACd,YAAQ,KAAK,qBAAqB,WAAW,EAAE;AAC/C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,IAAAE,QAAO,MAAM,EAAE,OAAO,SAAS,SAAS,YAAY,GAAG,qBAAqB;AAC5E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,WAAW,gBAAgB,SAAS;AAC1C,MAAI,UAAU;AACZ,YAAQ,IAAI;AACZ,YAAQ,IAAI,UAAU;AACtB,YAAQ,IAAI,SAAS,KAAK;AAC1B,YAAQ,IAAI;AAAA,EACd,OAAO;AACL,YAAQ,IAAI;AACZ,YAAQ;AAAA,MACNH,OAAM,IAAI,oDAAoD;AAAA,IAChE;AACA,YAAQ,IAAI;AAAA,EACd;AACF;;;AC3HA,SAAS,cAAAI,mBAAkB;AAC3B,SAAS,WAAAC,gBAAe;AACxB,OAAOC,YAAW;AAClB;AAAA,EACE,eAAAC;AAAA,EACA,qBAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,gBAAAC;AAAA,OACK;AAEP,IAAMC,UAASD,cAAa,WAAW;AAEvC,IAAM,KAAK,CAAC,QAAgB,QAAQ,IAAIN,OAAM,MAAM,cAAc,GAAG,CAAC;AACtE,IAAM,OAAO,CAAC,QAAgB,QAAQ,IAAIA,OAAM,IAAI,cAAc,GAAG,CAAC;AAEtE,eAAsB,aAAa,WAAmC;AACpE,QAAM,OAAOD,SAAQ,aAAa,kBAAkB;AAEpD,UAAQ,IAAIC,OAAM,KAAK;AAAA,WAAc,IAAI;AAAA,CAAO,CAAC;AAEjD,MAAI,CAACF,YAAW,IAAI,GAAG;AACrB,SAAK,2BAA2B,IAAI;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMG,aAAY,KAAK,IAAI;AACnC,OAAG,qBAAqB;AAAA,EAC1B,SAAS,KAAK;AACZ,SAAK,yBAAyB;AAC9B,IAAAM,QAAO;AAAA,MACL,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,MAC1D;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY;AAGhB,QAAM,iBAA8C;AAAA,IAClD,CAACL,oBAAmB,qBAAqB,mBAAmB;AAAA,IAC5D,CAACC,iBAAgB,kBAAkB,gBAAgB;AAAA,IACnD,CAACC,iBAAgB,kBAAkB,gBAAgB;AAAA,EACrD;AAEA,MAAI,mBAAmB;AACvB,aAAW,CAAC,eAAe,MAAM,MAAM,KAAK,gBAAgB;AAC1D,QACE,MAAM,oBACL,eACD;AACA,yBAAmB;AACnB,YAAM,MAAMC,gBAAe,SAAS,MAAM;AAC1C,UAAI,KAAK;AACP,WAAG,eAAe,OAAO,OAAO,SAAS,UAAU;AAAA,MACrD,OAAO;AACL,aAAK,eAAe,OAAO,OAAO,SAAS,cAAc;AACzD,oBAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,kBAAkB;AACrB,OAAG,8BAA8B;AAAA,EACnC;AAGA,QAAM,YAAY,OAAO,KAAK,MAAM,MAAM;AAC1C,KAAG,UAAU,SAAS,YAAY,UAAU,WAAW,IAAI,KAAK,OAAO,aAAa;AAGpF,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC5D,eAAW,SAAS,MAAM,QAAQ;AAChC,YAAM,YAAY,MAAM;AAGxB,YAAM,cAAsC;AAAA,QAC1C,QAAQ;AAAA,MACV;AAEA,YAAM,SAAS,YAAY,SAAS;AACpC,UAAI,QAAQ;AACV,cAAM,MAAMA,gBAAe,SAAS,MAAM;AAC1C,YAAI,KAAK;AACP;AAAA,YACE,YAAY,YAAY,SAAS,WAAW,OAAO,SAAS;AAAA,UAC9D;AAAA,QACF,OAAO;AACL;AAAA,YACE,YAAY,YAAY,SAAS,WAAW,OAAO,SAAS;AAAA,UAC9D;AACA,sBAAY;AAAA,QACd;AAAA,MACF,OAAO;AACL,WAAG,YAAY,YAAY,SAAS,WAAW,cAAc;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,IAAI,EAAE;AACd,MAAI,WAAW;AACb,YAAQ;AAAA,MACNL,OAAM,OAAO,sDAAsD;AAAA,IACrE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB,OAAO;AACL,YAAQ;AAAA,MACNA,OAAM,MAAM,oBAAoB,IAC9BA,OAAM,IAAI,6BAA6B;AAAA,IAC3C;AAAA,EACF;AACF;;;ACrHA,SAAS,cAAAQ,mBAAkB;AAC3B,SAAS,WAAAC,gBAAe;AACxB,SAAS,YAAY;AACrB,OAAO,aAAa;AACpB,OAAOC,YAAW;AAClB;AAAA,EACE,gBAAAC;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,OACK;AAGP,IAAMC,UAASD,cAAa,cAAc;AAC1C,IAAM,OAAO;AAEb,SAAS,cAAc,KAAsB;AAC3C,SAAO,KAAK,UAAU,KAAK,CAAC,MAAM,UAAU;AAC1C,QAAI,iBAAiB,MAAO,QAAO,EAAE,SAAS,MAAM,SAAS,MAAM,MAAM,KAAK;AAC9E,QAAI,OAAO,UAAU,WAAY,QAAO;AACxC,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,YAAY,KAAmB;AACtC,QAAM,MACJ,QAAQ,aAAa,WAAW,SAChC,QAAQ,aAAa,UAAU,UAC/B;AACF,OAAK,MAAM,MAAM,GAAG;AACtB;AAEA,eAAsB,cAAc,WAAmC;AACrE,QAAM,OAAOJ,SAAQ,aAAa,kBAAkB;AAEpD,MAAI,CAACD,YAAW,IAAI,GAAG;AACrB,IAAAM,QAAO,MAAM,EAAE,KAAK,GAAG,sBAAsB;AAC7C,YAAQ,MAAMJ,OAAM,IAAI,8CAA8C,CAAC;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAME,aAAY,KAAK,IAAI;AAAA,EACrC,SAAS,KAAK;AACZ,IAAAE,QAAO,MAAM,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,GAAG,sBAAsB;AAChG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,IAAIH,cAAa,KAAK;AAGtC,QAAM,kBAAkB,oBAAI,IAAsD;AAClF,UAAQ,OAAO,GAAG,eAAe,CAAC,MAAM;AACtC,QAAI,CAAC,gBAAgB,IAAI,EAAE,UAAU,GAAG;AACtC,sBAAgB,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,YAAY,oBAAI,KAAK,EAAE,CAAC;AAAA,IACxF;AAAA,EACF,CAAC;AAGD,QAAM,aAAa,oBAAI,IAAqB;AAC5C,UAAQ,OAAO,MAAM,CAAC,UAAU;AAC9B,UAAM,OAAO,cAAc,KAAK;AAChC,eAAW,UAAU,YAAY;AAC/B,aAAO,MAAM,yBAAyB,OAAO,MAAM;AAAA,IACrD;AAAA,EACF,CAAC;AAGD,QAAM,MAAM,QAAQ;AACpB,MAAI,IAAI,QAAQ,KAAK,CAAC;AAGtB,MAAI,IAAI,WAAW,CAAC,MAAM,QAAQ;AAChC,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AACD,QAAI,MAAM,OAAO;AACjB,eAAW,IAAI,GAAG;AAClB,SAAK,GAAG,SAAS,MAAM,WAAW,OAAO,GAAG,CAAC;AAAA,EAC/C,CAAC;AAGD,MAAI,IAAI,cAAc,CAAC,MAAM,QAAQ;AACnC,UAAM,SAAS,OAAO;AAAA,MACpB,OAAO,QAAQ,QAAQ,MAAM,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;AAAA,QACxD;AAAA,QACA;AAAA,UACE,MAAM,MAAM;AAAA,UACZ,aAAa,MAAM;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,WAAW,MAAM;AAAA,UACjB,aAAa,MAAM,OAAO;AAAA,UAC1B,QAAQ,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,QACxC;AAAA,MACF,CAAC;AAAA,IACH;AACA,QAAI,KAAK;AAAA,MACP,MAAM,QAAQ,MAAM;AAAA,MACpB,aAAa,QAAQ,MAAM;AAAA,MAC3B,eAAe,QAAQ,MAAM;AAAA,MAC7B,OAAO,QAAQ,MAAM;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,MAAI,IAAI,iBAAiB,CAAC,MAAM,QAAQ;AACtC,UAAM,WAAW,MAAM,KAAK,gBAAgB,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM;AACzE,YAAM,UAAU,QAAQ,WAAW,EAAE;AACrC,aAAO;AAAA,QACL;AAAA,QACA,YAAY,KAAK;AAAA,QACjB,eAAe,SAAS,SAAS,UAAU;AAAA,QAC3C,YAAY,KAAK;AAAA,MACnB;AAAA,IACF,CAAC;AACD,QAAI,KAAK,SAAS,QAAQ,CAAC;AAAA,EAC7B,CAAC;AAED,MAAI,IAAI,qBAAqB,CAAC,KAAK,QAAQ;AACzC,UAAM,UAAU,QAAQ,WAAW,IAAI,OAAO,EAAE;AAChD,QAAI,CAAC,SAAS;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAG;AAAA,IAAQ;AAC9E,QAAI,KAAK,OAAO;AAAA,EAClB,CAAC;AAED,MAAI,KAAK,YAAY,OAAO,KAAK,QAAQ;AACvC,UAAM,EAAE,OAAO,OAAO,WAAW,IAAI,IAAI;AACzC,QAAI,CAAC,SAAS,CAAC,OAAO;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,+BAA+B,CAAC;AAAG;AAAA,IAAQ;AACjG,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,IAAI,OAAO,OAAO,UAAU;AACzD,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,CAAC;AAAA,IAClF;AAAA,EACF,CAAC;AAGD,MAAI,IAAI,KAAK,CAAC,MAAM,QAAQ;AAC1B,QAAI,KAAK,MAAM,EAAE,KAAK,cAAc,CAAC;AAAA,EACvC,CAAC;AAED,MAAI,OAAO,MAAM,MAAM;AACrB,UAAM,MAAM,sBAAsB;AAClC,YAAQ,IAAI;AACZ,YAAQ,IAAID,OAAM,KAAK,gBAAgB,CAAC;AACxC,YAAQ,IAAIA,OAAM,IAAI,OAAO,GAAG,CAAC;AACjC,YAAQ,IAAI;AACZ,YAAQ,IAAIA,OAAM,IAAI,WAAW,KAAK,QAAQ,MAAM,QAAQ,KAAK;AACjE,YAAQ,IAAIA,OAAM,IAAI,YAAY,IAAI,OAAO,KAAK,QAAQ,MAAM,MAAM,EAAE,KAAK,IAAI,CAAC;AAClF,YAAQ,IAAI;AACZ,gBAAY,GAAG;AAAA,EACjB,CAAC;AAED,UAAQ,GAAG,UAAU,MAAM;AACzB,YAAQ,IAAIA,OAAM,IAAI,iCAAiC,CAAC;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;AAMA,SAAS,gBAAwB;AAC/B,SAAO;AAgWT;;;ALrgBA,OAAO;AAGP,IAAMK,UAASC,cAAa,WAAW;AAEvC,QAAQ,GAAG,sBAAsB,CAAC,WAAW;AAC3C,EAAAD,QAAO,MAAM,EAAE,OAAO,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,EAAE,GAAG,qBAAqB;AACxG,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,QAAQ,GAAG,qBAAqB,CAAC,QAAQ;AACvC,EAAAA,QAAO,MAAM,EAAE,OAAO,IAAI,QAAQ,GAAG,aAAa;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;AASD,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,UAAU,EACf,YAAY,mEAA8D,EAC1E,QAAQ,OAAO;AAElB,QACG,QAAQ,qBAAqB,EAC7B,YAAY,4BAA4B,EACxC,OAAO,OAAO,gBAAyB;AACtC,QAAM,YAAY,WAAW;AAC/B,CAAC;AAEH,QACG,QAAQ,aAAa,EACrB,YAAY,iCAAiC,EAC7C,OAAO,OAAO,UAAmB;AAChC,QAAM,WAAW,KAAK;AACxB,CAAC;AAEH,QACG,QAAQ,aAAa,EACrB,YAAY,6BAA6B,EACzC,OAAO,OAAO,UAAkB;AAC/B,QAAM,WAAW,KAAK;AACxB,CAAC;AAEH,QACG,QAAQ,eAAe,EACvB,YAAY,0CAA0C,EACtD,OAAO,OAAO,UAAmB;AAChC,QAAM,aAAa,KAAK;AAC1B,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,8CAAyC,EACrD,OAAO,OAAO,UAAmB;AAChC,QAAM,aAAa,KAAK;AAC1B,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,mEAA8D,EAC1E,OAAO,OAAO,UAAmB;AAChC,QAAM,cAAc,KAAK;AAC3B,CAAC;AAEH,QAAQ,MAAM;","names":["createLogger","existsSync","chalk","createLogger","logger","existsSync","resolve","chalk","ora","createLogger","logger","existsSync","resolve","chalk","ScoreLoader","AnthropicProvider","OpenAIProvider","GeminiProvider","SecretsManager","createLogger","logger","existsSync","resolve","chalk","TuttiRuntime","ScoreLoader","createLogger","logger","logger","createLogger"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/commands/run.ts","../src/commands/add.ts","../src/commands/check.ts","../src/commands/studio.ts"],"sourcesContent":["import { config } from \"dotenv\";\nconfig();\n\nimport { createLogger } from \"@tuttiai/core\";\nconst logger = createLogger(\"tutti-cli\");\n\nprocess.on(\"unhandledRejection\", (reason) => {\n logger.error({ error: reason instanceof Error ? reason.message : String(reason) }, \"Unhandled rejection\");\n process.exit(1);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n logger.error({ error: err.message }, \"Fatal error\");\n process.exit(1);\n});\n\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { runCommand } from \"./commands/run.js\";\nimport { addCommand } from \"./commands/add.js\";\nimport { checkCommand } from \"./commands/check.js\";\nimport { studioCommand } from \"./commands/studio.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"tutti-ai\")\n .description(\"Tutti — multi-agent orchestration. All agents. All together.\")\n .version(\"0.7.0\");\n\nprogram\n .command(\"init [project-name]\")\n .description(\"Create a new Tutti project\")\n .action(async (projectName?: string) => {\n await initCommand(projectName);\n });\n\nprogram\n .command(\"run [score]\")\n .description(\"Run a Tutti score interactively\")\n .action(async (score?: string) => {\n await runCommand(score);\n });\n\nprogram\n .command(\"add <voice>\")\n .description(\"Add a voice to your project\")\n .action(async (voice: string) => {\n await addCommand(voice);\n });\n\nprogram\n .command(\"check [score]\")\n .description(\"Validate a score file without running it\")\n .action(async (score?: string) => {\n await checkCommand(score);\n });\n\nprogram\n .command(\"doctor [score]\")\n .description(\"Alias for check — validate a score file\")\n .action(async (score?: string) => {\n await checkCommand(score);\n });\n\nprogram\n .command(\"studio [score]\")\n .description(\"Launch Tutti Studio — local web UI for inspecting agent runs\")\n .action(async (score?: string) => {\n await studioCommand(score);\n });\n\nprogram.parse();\n","import { mkdirSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport Enquirer from \"enquirer\";\nimport { createLogger } from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nconst { prompt } = Enquirer;\n\nexport async function initCommand(projectName?: string): Promise<void> {\n if (!projectName) {\n const response = await prompt<{ projectName: string }>({\n type: \"input\",\n name: \"projectName\",\n message: \"Project name?\",\n });\n projectName = response.projectName;\n }\n\n if (!projectName) {\n logger.error(\"Project name is required\");\n process.exit(1);\n }\n\n const dir = join(process.cwd(), projectName);\n\n if (existsSync(dir)) {\n logger.error({ dir: `${projectName}/` }, \"Directory already exists\");\n process.exit(1);\n }\n\n mkdirSync(dir, { recursive: true });\n\n const files: Record<string, string> = {\n \"package.json\": JSON.stringify(\n {\n name: projectName,\n version: \"0.0.1\",\n type: \"module\",\n scripts: {\n dev: \"tsx watch tutti.score.ts\",\n start: \"tsx tutti.score.ts\",\n },\n dependencies: {\n \"@tuttiai/core\": \"*\",\n \"@tuttiai/types\": \"*\",\n },\n devDependencies: {\n tsx: \"^4.0.0\",\n typescript: \"^5.7.0\",\n },\n },\n null,\n 2,\n ),\n\n \".env.example\":\n \"ANTHROPIC_API_KEY=your_key_here\\n\\n\" +\n \"# Log level: debug | info | warn | error (default: info)\\n\" +\n \"TUTTI_LOG_LEVEL=info\\n\\n\" +\n \"# OpenTelemetry (optional)\\n\" +\n \"# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318\\n\" +\n \"# OTEL_SERVICE_NAME=tutti\\n\",\n\n \".gitignore\": \"node_modules\\ndist\\n.env\\n\",\n\n \"tsconfig.json\": JSON.stringify(\n {\n compilerOptions: {\n strict: true,\n target: \"ES2022\",\n module: \"ES2022\",\n moduleResolution: \"bundler\",\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: \"dist\",\n rootDir: \".\",\n },\n include: [\".\"],\n },\n null,\n 2,\n ),\n\n \"tutti.score.ts\": `import { defineScore, AnthropicProvider } from \"@tuttiai/core\"\n\nexport default defineScore({\n provider: new AnthropicProvider(),\n default_model: \"claude-sonnet-4-20250514\",\n agents: {\n assistant: {\n name: \"Assistant\",\n system_prompt: \"You are a helpful assistant.\",\n voices: [],\n }\n }\n})\n`,\n\n \"README.md\": `# ${projectName}\n\nA Tutti agent project. All agents. All together.\n\n## Setup\n\n\\`\\`\\`bash\ncp .env.example .env\n# Add your ANTHROPIC_API_KEY to .env\nnpm install\n\\`\\`\\`\n\n## Run\n\n\\`\\`\\`bash\nnpm run dev\n\\`\\`\\`\n`,\n };\n\n for (const [filename, content] of Object.entries(files)) {\n writeFileSync(join(dir, filename), content);\n }\n\n console.log();\n console.log(chalk.green(` ✔ Created ${projectName}/`));\n console.log();\n console.log(\" Next steps:\");\n console.log(chalk.cyan(` cd ${projectName}`));\n console.log(chalk.cyan(\" cp .env.example .env\"));\n console.log(chalk.cyan(\" npm install\"));\n console.log(chalk.cyan(\" npm run dev\"));\n console.log();\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport {\n TuttiRuntime,\n ScoreLoader,\n AnthropicProvider,\n OpenAIProvider,\n GeminiProvider,\n SecretsManager,\n createLogger,\n} from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nexport async function runCommand(scorePath?: string): Promise<void> {\n const file = resolve(scorePath ?? \"./tutti.score.ts\");\n\n if (!existsSync(file)) {\n logger.error({ file }, \"Score file not found\");\n console.error(chalk.dim('Run \"tutti-ai init\" to create a new project.'));\n process.exit(1);\n }\n\n let score;\n try {\n score = await ScoreLoader.load(file);\n } catch (err) {\n logger.error(\n { error: err instanceof Error ? err.message : String(err) },\n \"Failed to load score\",\n );\n process.exit(1);\n }\n\n // Validate that the provider has a valid API key\n const providerKeyMap: [unknown, string][] = [\n [AnthropicProvider, \"ANTHROPIC_API_KEY\"],\n [OpenAIProvider, \"OPENAI_API_KEY\"],\n [GeminiProvider, \"GEMINI_API_KEY\"],\n ];\n\n for (const [ProviderClass, envVar] of providerKeyMap) {\n if (score.provider instanceof (ProviderClass as new (...args: unknown[]) => unknown)) {\n const key = SecretsManager.optional(envVar);\n if (!key) {\n logger.error({ envVar }, \"Missing API key\");\n process.exit(1);\n }\n }\n }\n\n // Enable streaming on all agents\n for (const agent of Object.values(score.agents)) {\n agent.streaming = true;\n }\n\n const runtime = new TuttiRuntime(score);\n const spinner = ora({ color: \"cyan\" });\n\n // Streaming state — reset per run\n let streaming = false;\n\n runtime.events.on(\"agent:start\", (e) => {\n logger.info({ agent: e.agent_name }, \"Running agent\");\n });\n\n runtime.events.on(\"llm:request\", () => {\n spinner.start(\"Thinking...\");\n });\n\n // Token-by-token streaming\n runtime.events.on(\"token:stream\", (e) => {\n if (!streaming) {\n // First token — kill spinner, switch to streaming mode\n spinner.stop();\n streaming = true;\n }\n process.stdout.write(e.text);\n });\n\n runtime.events.on(\"llm:response\", () => {\n if (streaming) {\n // End of a streamed turn — newline after the streamed text\n process.stdout.write(\"\\n\");\n } else {\n // Non-streaming fallback — just stop spinner\n spinner.stop();\n }\n });\n\n // Tool calls during streaming\n runtime.events.on(\"tool:start\", (e) => {\n if (streaming) {\n process.stdout.write(chalk.dim(\"\\n [using: \" + e.tool_name + \"]\"));\n } else {\n spinner.stop();\n console.log(chalk.dim(\" [using: \" + e.tool_name + \"]\"));\n }\n });\n\n runtime.events.on(\"tool:end\", (e) => {\n if (streaming) {\n process.stdout.write(chalk.dim(\" [done: \" + e.tool_name + \"]\\n\"));\n }\n });\n\n runtime.events.on(\"tool:error\", (e) => {\n spinner.stop();\n logger.error({ tool: e.tool_name }, \"Tool error\");\n });\n\n runtime.events.on(\"security:injection_detected\", (e) => {\n logger.warn({ tool: e.tool_name }, \"Potential prompt injection detected\");\n });\n\n runtime.events.on(\"budget:warning\", () => {\n logger.warn(\"Approaching token budget (80%)\");\n });\n\n runtime.events.on(\"budget:exceeded\", () => {\n logger.error(\"Token budget exceeded — stopping\");\n });\n\n // REPL\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(chalk.dim('Tutti REPL — type \"exit\" to quit\\n'));\n\n let sessionId: string | undefined;\n\n // Handle Ctrl+C cleanly\n process.on(\"SIGINT\", () => {\n if (streaming) process.stdout.write(\"\\n\");\n spinner.stop();\n console.log(chalk.dim(\"Goodbye!\"));\n rl.close();\n process.exit(0);\n });\n\n try {\n while (true) {\n const input = await rl.question(chalk.cyan(\"> \"));\n const trimmed = input.trim();\n\n if (!trimmed) continue;\n if (trimmed === \"exit\" || trimmed === \"quit\") break;\n\n // Reset streaming state for this run\n streaming = false;\n\n try {\n const result = await runtime.run(\"assistant\", trimmed, sessionId);\n sessionId = result.session_id;\n\n if (!streaming) {\n // Non-streaming fallback — print the full response\n console.log(\"\\n\" + result.output + \"\\n\");\n } else {\n // Streaming already printed tokens; just add a blank line\n console.log();\n }\n } catch (err) {\n if (streaming) process.stdout.write(\"\\n\");\n spinner.stop();\n logger.error(\n { error: err instanceof Error ? err.message : String(err) },\n \"Something went wrong\",\n );\n }\n }\n } catch {\n // readline closed\n }\n\n console.log(chalk.dim(\"Goodbye!\"));\n rl.close();\n process.exit(0);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { createLogger } from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nconst OFFICIAL_VOICES: Record<string, { package: string; setup: string }> = {\n filesystem: {\n package: \"@tuttiai/filesystem\",\n setup: ` Add to your score:\n ${chalk.cyan('import { FilesystemVoice } from \"@tuttiai/filesystem\"')}\n ${chalk.cyan(\"voices: [new FilesystemVoice()]\")}`,\n },\n github: {\n package: \"@tuttiai/github\",\n setup: ` Add ${chalk.bold(\"GITHUB_TOKEN\")} to your .env file:\n ${chalk.cyan(\"GITHUB_TOKEN=ghp_your_token_here\")}\n\n Add to your score:\n ${chalk.cyan('import { GitHubVoice } from \"@tuttiai/github\"')}\n ${chalk.cyan(\"voices: [new GitHubVoice()]\")}`,\n },\n playwright: {\n package: \"@tuttiai/playwright\",\n setup: ` Install the browser:\n ${chalk.cyan(\"npx playwright install chromium\")}\n\n Add to your score:\n ${chalk.cyan('import { PlaywrightVoice } from \"@tuttiai/playwright\"')}\n ${chalk.cyan(\"voices: [new PlaywrightVoice()]\")}`,\n },\n postgres: {\n package: \"pg\",\n setup: ` Add ${chalk.bold(\"DATABASE_URL\")} to your .env file:\n ${chalk.cyan(\"DATABASE_URL=postgres://user:pass@localhost:5432/tutti\")}\n\n Add to your score:\n ${chalk.cyan(\"memory: { provider: 'postgres' }\")}\n\n Or with an explicit URL:\n ${chalk.cyan(\"memory: { provider: 'postgres', url: process.env.DATABASE_URL }\")}\n\n Use the async factory for initialization:\n ${chalk.cyan(\"const tutti = await TuttiRuntime.create(score)\")}`,\n },\n};\n\nfunction resolvePackageName(input: string): string {\n // Known official voice\n if (OFFICIAL_VOICES[input]) {\n return OFFICIAL_VOICES[input].package;\n }\n // Already a scoped package\n if (input.startsWith(\"@\")) {\n return input;\n }\n // Try @tuttiai/<name> convention\n return `@tuttiai/${input}`;\n}\n\nfunction isAlreadyInstalled(packageName: string): boolean {\n const pkgPath = resolve(process.cwd(), \"package.json\");\n if (!existsSync(pkgPath)) return false;\n\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n return packageName in deps;\n } catch {\n return false;\n }\n}\n\nexport async function addCommand(voiceName: string): Promise<void> {\n const packageName = resolvePackageName(voiceName);\n\n // Check if package.json exists in cwd\n const pkgPath = resolve(process.cwd(), \"package.json\");\n if (!existsSync(pkgPath)) {\n logger.error(\"No package.json found in the current directory\");\n console.error(chalk.dim('Run \"tutti-ai init\" to create a new project first.'));\n process.exit(1);\n }\n\n // Check if already installed\n if (isAlreadyInstalled(packageName)) {\n console.log(chalk.green(` ✔ ${packageName} is already installed`));\n return;\n }\n\n // Install\n const spinner = ora(`Installing ${packageName}...`).start();\n\n try {\n execSync(`npm install ${packageName}`, {\n cwd: process.cwd(),\n stdio: \"pipe\",\n });\n spinner.succeed(`Installed ${packageName}`);\n } catch (error) {\n spinner.fail(`Failed to install ${packageName}`);\n const message = error instanceof Error ? error.message : String(error);\n logger.error({ error: message, package: packageName }, \"Installation failed\");\n process.exit(1);\n }\n\n // Print setup instructions\n const official = OFFICIAL_VOICES[voiceName];\n if (official) {\n console.log();\n console.log(\" Setup:\");\n console.log(official.setup);\n console.log();\n } else {\n console.log();\n console.log(\n chalk.dim(\" Check the package README for setup instructions.\"),\n );\n console.log();\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n ScoreLoader,\n AnthropicProvider,\n OpenAIProvider,\n GeminiProvider,\n SecretsManager,\n createLogger,\n} from \"@tuttiai/core\";\n\nconst logger = createLogger(\"tutti-cli\");\n\nconst ok = (msg: string) => console.log(chalk.green(\" \\u2714 \" + msg));\nconst fail = (msg: string) => console.log(chalk.red(\" \\u2718 \" + msg));\n\nexport async function checkCommand(scorePath?: string): Promise<void> {\n const file = resolve(scorePath ?? \"./tutti.score.ts\");\n\n console.log(chalk.cyan(`\\nChecking ${file}...\\n`));\n\n if (!existsSync(file)) {\n fail(\"Score file not found: \" + file);\n process.exit(1);\n }\n\n // 1. Load and validate\n let score;\n try {\n score = await ScoreLoader.load(file);\n ok(\"Score file is valid\");\n } catch (err) {\n fail(\"Score validation failed\");\n logger.error(\n { error: err instanceof Error ? err.message : String(err) },\n \"Score validation failed\",\n );\n process.exit(1);\n }\n\n let hasErrors = false;\n\n // 2. Check provider and API key\n const providerChecks: [unknown, string, string][] = [\n [AnthropicProvider, \"AnthropicProvider\", \"ANTHROPIC_API_KEY\"],\n [OpenAIProvider, \"OpenAIProvider\", \"OPENAI_API_KEY\"],\n [GeminiProvider, \"GeminiProvider\", \"GEMINI_API_KEY\"],\n ];\n\n let providerDetected = false;\n for (const [ProviderClass, name, envVar] of providerChecks) {\n if (\n score.provider instanceof\n (ProviderClass as new (...args: unknown[]) => unknown)\n ) {\n providerDetected = true;\n const key = SecretsManager.optional(envVar);\n if (key) {\n ok(\"Provider: \" + name + \" (\" + envVar + \" is set)\");\n } else {\n fail(\"Provider: \" + name + \" (\" + envVar + \" is NOT set)\");\n hasErrors = true;\n }\n }\n }\n\n if (!providerDetected) {\n ok(\"Provider: custom LLMProvider\");\n }\n\n // 3. Count agents\n const agentKeys = Object.keys(score.agents);\n ok(agentKeys.length + \" agent\" + (agentKeys.length === 1 ? \"\" : \"s\") + \" configured\");\n\n // 4. Check voices\n for (const [agentKey, agent] of Object.entries(score.agents)) {\n for (const voice of agent.voices) {\n const voiceName = voice.name;\n\n // Check for known voices and their env vars\n const voiceEnvMap: Record<string, string> = {\n github: \"GITHUB_TOKEN\",\n };\n\n const envVar = voiceEnvMap[voiceName];\n if (envVar) {\n const key = SecretsManager.optional(envVar);\n if (key) {\n ok(\n \"Voice: \" + voiceName + \" on \" + agentKey + \" (\" + envVar + \" is set)\",\n );\n } else {\n fail(\n \"Voice: \" + voiceName + \" on \" + agentKey + \" (\" + envVar + \" is NOT set)\",\n );\n hasErrors = true;\n }\n } else {\n ok(\"Voice: \" + voiceName + \" on \" + agentKey + \" (installed)\");\n }\n }\n }\n\n // Final summary\n console.log(\"\");\n if (hasErrors) {\n console.log(\n chalk.yellow(\"Some checks failed. Fix the issues above and re-run.\"),\n );\n process.exit(1);\n } else {\n console.log(\n chalk.green(\"All checks passed.\") +\n chalk.dim(\" Run tutti-ai run to start.\"),\n );\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { exec } from \"node:child_process\";\nimport express from \"express\";\nimport chalk from \"chalk\";\nimport {\n TuttiRuntime,\n ScoreLoader,\n createLogger,\n} from \"@tuttiai/core\";\nimport type { Response as ExpressResponse } from \"express\";\n\nconst logger = createLogger(\"tutti-studio\");\nconst PORT = 4747;\n\nfunction safeStringify(obj: unknown): string {\n return JSON.stringify(obj, (_key, value) => {\n if (value instanceof Error) return { message: value.message, name: value.name };\n if (typeof value === \"function\") return undefined;\n return value;\n });\n}\n\nfunction openBrowser(url: string): void {\n const cmd =\n process.platform === \"darwin\" ? \"open\" :\n process.platform === \"win32\" ? \"start\" :\n \"xdg-open\";\n exec(cmd + \" \" + url);\n}\n\nexport async function studioCommand(scorePath?: string): Promise<void> {\n const file = resolve(scorePath ?? \"./tutti.score.ts\");\n\n if (!existsSync(file)) {\n logger.error({ file }, \"Score file not found\");\n console.error(chalk.dim('Run \"tutti-ai init\" to create a new project.'));\n process.exit(1);\n }\n\n let score;\n try {\n score = await ScoreLoader.load(file);\n } catch (err) {\n logger.error({ error: err instanceof Error ? err.message : String(err) }, \"Failed to load score\");\n process.exit(1);\n }\n\n const runtime = new TuttiRuntime(score);\n\n // Track sessions via events\n const sessionRegistry = new Map<string, { agent_name: string; created_at: Date }>();\n runtime.events.on(\"agent:start\", (e) => {\n if (!sessionRegistry.has(e.session_id)) {\n sessionRegistry.set(e.session_id, { agent_name: e.agent_name, created_at: new Date() });\n }\n });\n\n // SSE clients\n const sseClients = new Set<ExpressResponse>();\n runtime.events.onAny((event) => {\n const data = safeStringify(event);\n for (const client of sseClients) {\n client.write(\"event: tutti\\ndata: \" + data + \"\\n\\n\");\n }\n });\n\n // Express\n const app = express();\n app.use(express.json());\n\n // SSE endpoint\n app.get(\"/events\", (_req, res) => {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n res.write(\":\\n\\n\");\n sseClients.add(res);\n _req.on(\"close\", () => sseClients.delete(res));\n });\n\n // REST API\n app.get(\"/api/score\", (_req, res) => {\n const agents = Object.fromEntries(\n Object.entries(runtime.score.agents).map(([id, agent]) => [\n id,\n {\n name: agent.name,\n description: agent.description,\n model: agent.model,\n role: agent.role,\n delegates: agent.delegates,\n voice_count: agent.voices.length,\n voices: agent.voices.map((v) => v.name),\n },\n ]),\n );\n res.json({\n name: runtime.score.name,\n description: runtime.score.description,\n default_model: runtime.score.default_model,\n entry: runtime.score.entry,\n agents,\n });\n });\n\n app.get(\"/api/sessions\", (_req, res) => {\n const sessions = Array.from(sessionRegistry.entries()).map(([id, meta]) => {\n const session = runtime.getSession(id);\n return {\n id,\n agent_name: meta.agent_name,\n message_count: session?.messages.length ?? 0,\n created_at: meta.created_at,\n };\n });\n res.json(sessions.reverse());\n });\n\n app.get(\"/api/sessions/:id\", (req, res) => {\n const session = runtime.getSession(req.params.id);\n if (!session) { res.status(404).json({ error: \"Session not found\" }); return; }\n res.json(session);\n });\n\n app.post(\"/api/run\", async (req, res) => {\n const { agent, input, session_id } = req.body as { agent: string; input: string; session_id?: string };\n if (!agent || !input) { res.status(400).json({ error: \"agent and input are required\" }); return; }\n try {\n const result = await runtime.run(agent, input, session_id);\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: err instanceof Error ? err.message : String(err) });\n }\n });\n\n // Serve UI\n app.get(\"/\", (_req, res) => {\n res.type(\"html\").send(getStudioHtml());\n });\n\n app.listen(PORT, () => {\n const url = \"http://localhost:\" + PORT;\n console.log();\n console.log(chalk.bold(\" Tutti Studio\"));\n console.log(chalk.dim(\" \" + url));\n console.log();\n console.log(chalk.dim(\" Score: \") + (runtime.score.name ?? file));\n console.log(chalk.dim(\" Agents: \") + Object.keys(runtime.score.agents).join(\", \"));\n console.log();\n openBrowser(url);\n });\n\n process.on(\"SIGINT\", () => {\n console.log(chalk.dim(\"\\nShutting down Tutti Studio...\"));\n process.exit(0);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Inline HTML UI\n// ---------------------------------------------------------------------------\n\nfunction getStudioHtml(): string {\n return '<!DOCTYPE html>\\\n<html lang=\"en\">\\\n<head>\\\n<meta charset=\"utf-8\">\\\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\\\n<title>Tutti Studio</title>\\\n<style>\\\n*{margin:0;padding:0;box-sizing:border-box}\\\n:root{\\\n--bg:#0a0a0f;--panel:#12121a;--card:#1a1a26;--input:#0f0f17;\\\n--border:#2a2a3a;--text:#e2e8f0;--muted:#64748b;\\\n--purple:#8b5cf6;--teal:#14b8a6;--blue:#3b82f6;--green:#10b981;\\\n--red:#ef4444;--orange:#f97316;--amber:#f59e0b;--indigo:#6366f1;\\\n}\\\nhtml,body{height:100%;font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);font-size:13px}\\\n#app{display:flex;flex-direction:column;height:100vh}\\\n\\\nheader{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;border-bottom:1px solid var(--border);background:var(--panel)}\\\nheader .logo{font-weight:700;font-size:15px;letter-spacing:.5px}\\\nheader .logo span{color:var(--purple)}\\\nheader .meta{color:var(--muted);font-size:12px}\\\nheader .status{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--muted)}\\\nheader .dot{width:7px;height:7px;border-radius:50%;background:var(--green)}\\\nheader .dot.off{background:var(--red)}\\\n\\\nmain{display:grid;grid-template-columns:260px 1fr 280px;flex:1;overflow:hidden;border-bottom:1px solid var(--border)}\\\n\\\n.panel{display:flex;flex-direction:column;border-right:1px solid var(--border);overflow:hidden}\\\n.panel:last-child{border-right:none}\\\n.panel-title{padding:10px 14px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);border-bottom:1px solid var(--border);background:var(--panel);flex-shrink:0}\\\n.panel-body{flex:1;overflow-y:auto;padding:10px}\\\n.panel-body::-webkit-scrollbar{width:5px}\\\n.panel-body::-webkit-scrollbar-track{background:transparent}\\\n.panel-body::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}\\\n\\\n#graph-panel .panel-body{padding:0;display:flex;align-items:center;justify-content:center}\\\n#graph-panel svg text{font-family:system-ui,-apple-system,sans-serif}\\\n\\\n#events-panel{display:flex;flex-direction:column}\\\n#event-stream{flex:1;overflow-y:auto;padding:10px}\\\n#event-stream::-webkit-scrollbar{width:5px}\\\n#event-stream::-webkit-scrollbar-track{background:transparent}\\\n#event-stream::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}\\\n\\\n.ev{padding:7px 10px;margin-bottom:6px;border-radius:6px;background:var(--card);border-left:3px solid var(--muted);font-size:12px;line-height:1.5}\\\n.ev .ev-head{display:flex;justify-content:space-between;align-items:center}\\\n.ev .ev-type{font-weight:600;font-family:\"SF Mono\",Menlo,monospace;font-size:11px}\\\n.ev .ev-time{color:var(--muted);font-size:10px;font-family:\"SF Mono\",Menlo,monospace}\\\n.ev .ev-detail{color:var(--muted);margin-top:3px;font-size:11px;word-break:break-all}\\\n.ev.agent{border-left-color:var(--purple)}.ev.agent .ev-type{color:var(--purple)}\\\n.ev.turn{border-left-color:var(--blue)}.ev.turn .ev-type{color:var(--blue)}\\\n.ev.llm{border-left-color:var(--green)}.ev.llm .ev-type{color:var(--green)}\\\n.ev.tool{border-left-color:var(--teal)}.ev.tool .ev-type{color:var(--teal)}\\\n.ev.tool-error{border-left-color:var(--red)}.ev.tool-error .ev-type{color:var(--red)}\\\n.ev.security{border-left-color:var(--orange)}.ev.security .ev-type{color:var(--orange)}\\\n.ev.budget-warn{border-left-color:var(--amber)}.ev.budget-warn .ev-type{color:var(--amber)}\\\n.ev.budget-exceed{border-left-color:var(--red)}.ev.budget-exceed .ev-type{color:var(--red)}\\\n.ev.delegate{border-left-color:var(--indigo)}.ev.delegate .ev-type{color:var(--indigo)}\\\n\\\n#input-bar{display:flex;gap:8px;padding:10px 12px;border-top:1px solid var(--border);background:var(--panel);flex-shrink:0}\\\n#agent-select{background:var(--input);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:6px 10px;font-size:12px;outline:none;cursor:pointer;min-width:110px}\\\n#user-input{flex:1;background:var(--input);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:6px 12px;font-size:13px;outline:none}\\\n#user-input:focus{border-color:var(--purple)}\\\n#send-btn{background:var(--purple);color:#fff;border:none;border-radius:6px;padding:6px 16px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap}\\\n#send-btn:hover{opacity:.9}\\\n#send-btn:disabled{opacity:.4;cursor:default}\\\n\\\n.session-item{padding:8px 10px;margin-bottom:4px;border-radius:6px;background:var(--card);cursor:pointer;transition:background .15s}\\\n.session-item:hover{background:#22223a}\\\n.session-item.active{background:#22223a;border:1px solid var(--purple)}\\\n.session-id{font-family:\"SF Mono\",Menlo,monospace;font-size:11px;color:var(--purple)}\\\n.session-meta{font-size:11px;color:var(--muted);margin-top:2px}\\\n\\\n#session-detail{margin-top:10px;border-top:1px solid var(--border);padding-top:10px}\\\n.msg{padding:6px 8px;margin-bottom:4px;border-radius:5px;font-size:12px;line-height:1.5;word-break:break-word}\\\n.msg.user{background:#1c1c3a;border-left:2px solid var(--blue)}\\\n.msg.assistant{background:#1a2a1a;border-left:2px solid var(--green)}\\\n.msg .msg-role{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;margin-bottom:2px}\\\n.msg.user .msg-role{color:var(--blue)}\\\n.msg.assistant .msg-role{color:var(--green)}\\\n\\\nfooter{display:flex;align-items:center;gap:32px;padding:8px 20px;background:var(--panel);font-size:12px}\\\n.token-item{display:flex;align-items:center;gap:6px}\\\n.token-label{color:var(--muted)}\\\n.token-val{font-family:\"SF Mono\",Menlo,monospace;font-weight:600}\\\n.token-val.input{color:var(--blue)}\\\n.token-val.output{color:var(--green)}\\\n.token-val.cost{color:var(--amber)}\\\n\\\n.empty{color:var(--muted);text-align:center;padding:30px 10px;font-size:12px}\\\n</style>\\\n</head>\\\n<body>\\\n<div id=\"app\">\\\n\\\n<header>\\\n <div class=\"logo\"><span>&#9835;</span> Tutti Studio</div>\\\n <div class=\"meta\" id=\"score-name\"></div>\\\n <div class=\"status\"><div class=\"dot\" id=\"sse-dot\"></div><span id=\"sse-label\">connecting</span></div>\\\n</header>\\\n\\\n<main>\\\n <div class=\"panel\" id=\"graph-panel\">\\\n <div class=\"panel-title\">Agent Graph</div>\\\n <div class=\"panel-body\" id=\"graph-body\"></div>\\\n </div>\\\n\\\n <div class=\"panel\" id=\"events-panel\">\\\n <div class=\"panel-title\">Live Event Stream</div>\\\n <div id=\"event-stream\"><div class=\"empty\">Waiting for events&hellip;<br>Send a message below to start an agent run.</div></div>\\\n <div id=\"input-bar\">\\\n <select id=\"agent-select\"></select>\\\n <input id=\"user-input\" placeholder=\"Type a message&hellip;\" autocomplete=\"off\">\\\n <button id=\"send-btn\">Send</button>\\\n </div>\\\n </div>\\\n\\\n <div class=\"panel\" id=\"sessions-panel\">\\\n <div class=\"panel-title\">Sessions</div>\\\n <div class=\"panel-body\" id=\"sessions-body\"><div class=\"empty\">No sessions yet</div></div>\\\n </div>\\\n</main>\\\n\\\n<footer>\\\n <div class=\"token-item\"><span class=\"token-label\">&#x2193; Input</span><span class=\"token-val input\" id=\"tok-in\">0</span></div>\\\n <div class=\"token-item\"><span class=\"token-label\">&#x2191; Output</span><span class=\"token-val output\" id=\"tok-out\">0</span></div>\\\n <div class=\"token-item\"><span class=\"token-label\">$ Est. cost</span><span class=\"token-val cost\" id=\"tok-cost\">0.0000</span></div>\\\n</footer>\\\n\\\n</div>\\\n\\\n<script>\\\n(function(){\\\n\\\nvar tokIn=0,tokOut=0;\\\nvar sessionMap={};\\\nvar activeSession=null;\\\n\\\n/* ---- helpers ---- */\\\nfunction esc(s){var d=document.createElement(\"div\");d.textContent=s;return d.innerHTML}\\\nfunction fmt(n){return n.toLocaleString()}\\\nfunction timeStr(){var d=new Date();return (\"0\"+d.getHours()).slice(-2)+\":\"+(\"0\"+d.getMinutes()).slice(-2)+\":\"+(\"0\"+d.getSeconds()).slice(-2)}\\\nfunction truncId(id){return id.slice(0,8)}\\\n\\\n/* ---- score + graph ---- */\\\nfunction loadScore(){\\\n fetch(\"/api/score\").then(function(r){return r.json()}).then(function(s){\\\n document.getElementById(\"score-name\").textContent=s.name||\"tutti.score.ts\";\\\n var sel=document.getElementById(\"agent-select\");\\\n sel.innerHTML=\"\";\\\n Object.keys(s.agents).forEach(function(id){\\\n var o=document.createElement(\"option\");o.value=id;o.textContent=s.agents[id].name;sel.appendChild(o);\\\n });\\\n renderGraph(s);\\\n });\\\n}\\\n\\\nfunction renderGraph(score){\\\n var body=document.getElementById(\"graph-body\");\\\n var W=260,ids=Object.keys(score.agents),N=ids.length;\\\n if(N===0){body.innerHTML=\"<div class=\\\\\"empty\\\\\">No agents</div>\";return}\\\n var hasDelegate=false;\\\n ids.forEach(function(id){if(score.agents[id].delegates&&score.agents[id].delegates.length)hasDelegate=true});\\\n var nodeR=26,padY=90,padTop=50;\\\n var leftIds=[],rightIds=[];\\\n if(hasDelegate){\\\n ids.forEach(function(id){var a=score.agents[id];if(a.delegates&&a.delegates.length)leftIds.push(id);else rightIds.push(id)});\\\n }else{leftIds=ids}\\\n var cols=hasDelegate?2:1;\\\n var cx1=cols===1?W/2:72,cx2=W-72;\\\n var H=Math.max(leftIds.length,rightIds.length)*padY+padTop*2;\\\n if(H<200)H=200;\\\n var pos={};\\\n var svg=\\'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"\\'+W+\\'\" height=\"\\'+H+\\'\" viewBox=\"0 0 \\'+W+\" \"+H+\\'\">\\';\\\n svg+=\\'<defs><marker id=\"ah\" viewBox=\"0 0 10 10\" refX=\"10\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#64748b\"/></marker></defs>\\';\\\n function drawNode(id,cx,cy){\\\n var a=score.agents[id];\\\n var col=a.role===\"orchestrator\"?\"#8b5cf6\":\"#14b8a6\";\\\n pos[id]={x:cx,y:cy};\\\n svg+=\\'<circle cx=\"\\'+cx+\\'\" cy=\"\\'+cy+\\'\" r=\"\\'+nodeR+\\'\" fill=\"\\'+col+\\'\" fill-opacity=\"0.15\" stroke=\"\\'+col+\\'\" stroke-width=\"2\"/>\\';\\\n svg+=\\'<text x=\"\\'+cx+\\'\" y=\"\\'+(cy+4)+\\'\" text-anchor=\"middle\" fill=\"#e2e8f0\" font-size=\"10\" font-weight=\"600\">\\'+esc(a.name)+\\'</text>\\';\\\n var model=a.model||score.default_model||\"\";\\\n if(model){var sh=model.replace(/-\\\\d{8}$/,\"\");if(sh.length>18)sh=sh.slice(0,18)+\"\\\\u2026\";svg+=\\'<text x=\"\\'+cx+\\'\" y=\"\\'+(cy+nodeR+14)+\\'\" text-anchor=\"middle\" fill=\"#64748b\" font-size=\"9\">\\'+esc(sh)+\\'</text>\\'}\\\n svg+=\\'<text x=\"\\'+cx+\\'\" y=\"\\'+(cy+nodeR+26)+\\'\" text-anchor=\"middle\" fill=\"#64748b\" font-size=\"9\">\\'+a.voice_count+\" voice\"+(a.voice_count!==1?\"s\":\"\")+\\'</text>\\';\\\n }\\\n leftIds.forEach(function(id,i){drawNode(id,cx1,padTop+i*padY)});\\\n rightIds.forEach(function(id,i){drawNode(id,cx2,padTop+i*padY)});\\\n ids.forEach(function(id){\\\n var a=score.agents[id];\\\n if(a.delegates)a.delegates.forEach(function(did){\\\n if(pos[id]&&pos[did]){\\\n var x1=pos[id].x+nodeR,y1=pos[id].y,x2=pos[did].x-nodeR,y2=pos[did].y;\\\n var mx=(x1+x2)/2;\\\n svg+=\\'<path d=\"M\\'+x1+\" \"+y1+\" C\"+mx+\" \"+y1+\" \"+mx+\" \"+y2+\" \"+x2+\" \"+y2+\\'\" fill=\"none\" stroke=\"#64748b\" stroke-width=\"1.5\" stroke-dasharray=\"4 3\" marker-end=\"url(#ah)\"/>\\';\\\n }\\\n });\\\n });\\\n svg+=\"</svg>\";\\\n body.innerHTML=svg;\\\n}\\\n\\\n/* ---- SSE ---- */\\\nfunction connectSSE(){\\\n var es=new EventSource(\"/events\");\\\n es.addEventListener(\"tutti\",function(e){\\\n var ev=JSON.parse(e.data);\\\n addEvent(ev);\\\n if(ev.type===\"llm:response\"&&ev.response&&ev.response.usage){\\\n tokIn+=ev.response.usage.input_tokens||0;\\\n tokOut+=ev.response.usage.output_tokens||0;\\\n document.getElementById(\"tok-in\").textContent=fmt(tokIn);\\\n document.getElementById(\"tok-out\").textContent=fmt(tokOut);\\\n document.getElementById(\"tok-cost\").textContent=estimateCost(tokIn,tokOut);\\\n }\\\n if(ev.type===\"agent:start\"||ev.type===\"agent:end\")refreshSessions();\\\n });\\\n es.onopen=function(){document.getElementById(\"sse-dot\").className=\"dot\";document.getElementById(\"sse-label\").textContent=\"connected\"};\\\n es.onerror=function(){document.getElementById(\"sse-dot\").className=\"dot off\";document.getElementById(\"sse-label\").textContent=\"disconnected\"};\\\n}\\\n\\\nfunction estimateCost(inp,out){\\\n var c=(inp/1e6)*3+(out/1e6)*15;\\\n return c.toFixed(4);\\\n}\\\n\\\nfunction evClass(t){\\\n if(t.indexOf(\"agent\")===0)return \"agent\";\\\n if(t.indexOf(\"turn\")===0)return \"turn\";\\\n if(t===\"llm:request\"||t===\"llm:response\")return \"llm\";\\\n if(t===\"tool:error\")return \"tool-error\";\\\n if(t.indexOf(\"tool\")===0)return \"tool\";\\\n if(t.indexOf(\"security\")===0)return \"security\";\\\n if(t===\"budget:warning\")return \"budget-warn\";\\\n if(t===\"budget:exceeded\")return \"budget-exceed\";\\\n if(t.indexOf(\"delegate\")===0)return \"delegate\";\\\n return \"\";\\\n}\\\n\\\nfunction evDetail(ev){\\\n var parts=[];\\\n if(ev.agent_name)parts.push(\"agent: \"+ev.agent_name);\\\n if(ev.session_id)parts.push(\"session: \"+truncId(ev.session_id));\\\n if(ev.turn!==undefined)parts.push(\"turn: \"+ev.turn);\\\n if(ev.tool_name)parts.push(\"tool: \"+ev.tool_name);\\\n if(ev.from)parts.push(\"from: \"+ev.from);\\\n if(ev.to)parts.push(\"to: \"+ev.to);\\\n if(ev.tokens!==undefined)parts.push(\"tokens: \"+fmt(ev.tokens));\\\n if(ev.cost_usd!==undefined)parts.push(\"cost: $\"+ev.cost_usd.toFixed(4));\\\n if(ev.response&&ev.response.usage)parts.push(\"tokens: \"+fmt(ev.response.usage.input_tokens)+\" in / \"+fmt(ev.response.usage.output_tokens)+\" out\");\\\n if(ev.error){var em=typeof ev.error===\"object\"?ev.error.message||\"\":ev.error;if(em)parts.push(\"error: \"+em)}\\\n if(ev.patterns)parts.push(\"patterns: \"+ev.patterns.join(\", \"));\\\n return parts.join(\" &middot; \");\\\n}\\\n\\\nvar firstEvent=true;\\\nfunction addEvent(ev){\\\n var stream=document.getElementById(\"event-stream\");\\\n if(firstEvent){stream.innerHTML=\"\";firstEvent=false}\\\n var div=document.createElement(\"div\");\\\n div.className=\"ev \"+evClass(ev.type);\\\n div.innerHTML=\\'<div class=\"ev-head\"><span class=\"ev-type\">\\'+esc(ev.type)+\\'</span><span class=\"ev-time\">\\'+timeStr()+\\'</span></div>\\';\\\n var det=evDetail(ev);\\\n if(det)div.innerHTML+=\\'<div class=\"ev-detail\">\\'+det+\"</div>\";\\\n stream.appendChild(div);\\\n stream.scrollTop=stream.scrollHeight;\\\n}\\\n\\\n/* ---- sessions ---- */\\\nfunction refreshSessions(){\\\n fetch(\"/api/sessions\").then(function(r){return r.json()}).then(function(list){\\\n var body=document.getElementById(\"sessions-body\");\\\n if(!list.length){body.innerHTML=\\'<div class=\"empty\">No sessions yet</div>\\';return}\\\n var html=\"\";\\\n list.forEach(function(s){\\\n var cls=\"session-item\"+(activeSession===s.id?\" active\":\"\");\\\n html+=\\'<div class=\"\\'+cls+\\'\" data-id=\"\\'+s.id+\\'\">\\';\\\n html+=\\'<div class=\"session-id\">\\'+truncId(s.id)+\"</div>\";\\\n html+=\\'<div class=\"session-meta\">\\'+esc(s.agent_name)+\" &middot; \"+s.message_count+\" msgs</div>\";\\\n html+=\"</div>\";\\\n });\\\n if(activeSession)html+=\\'<div id=\"session-detail\"></div>\\';\\\n body.innerHTML=html;\\\n body.querySelectorAll(\".session-item\").forEach(function(el){\\\n el.addEventListener(\"click\",function(){selectSession(el.getAttribute(\"data-id\"))});\\\n });\\\n if(activeSession)loadSessionDetail(activeSession);\\\n });\\\n}\\\n\\\nfunction selectSession(id){\\\n activeSession=activeSession===id?null:id;\\\n refreshSessions();\\\n}\\\n\\\nfunction loadSessionDetail(id){\\\n var det=document.getElementById(\"session-detail\");\\\n if(!det)return;\\\n fetch(\"/api/sessions/\"+id).then(function(r){return r.json()}).then(function(session){\\\n if(!session||session.error){det.innerHTML=\\'<div class=\"empty\">Session not found</div>\\';return}\\\n var html=\"\";\\\n (session.messages||[]).forEach(function(m){\\\n var role=m.role;\\\n var text=\"\";\\\n if(typeof m.content===\"string\")text=m.content;\\\n else if(Array.isArray(m.content)){\\\n m.content.forEach(function(b){\\\n if(b.type===\"text\")text+=b.text+\"\\\\n\";\\\n else if(b.type===\"tool_use\")text+=\"[tool_use: \"+b.name+\"]\\\\n\";\\\n else if(b.type===\"tool_result\")text+=\"[tool_result]\\\\n\";\\\n });\\\n }\\\n html+=\\'<div class=\"msg \\'+role+\\'\"><div class=\"msg-role\">\\'+role+\"</div>\"+esc(text.trim())+\"</div>\";\\\n });\\\n det.innerHTML=html;\\\n });\\\n}\\\n\\\n/* ---- send ---- */\\\nfunction sendMessage(){\\\n var agentSel=document.getElementById(\"agent-select\");\\\n var inputEl=document.getElementById(\"user-input\");\\\n var btn=document.getElementById(\"send-btn\");\\\n var agent=agentSel.value;\\\n var input=inputEl.value.trim();\\\n if(!input)return;\\\n btn.disabled=true;btn.textContent=\"Running\\\\u2026\";\\\n inputEl.value=\"\";\\\n var sid=sessionMap[agent]||undefined;\\\n fetch(\"/api/run\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify({agent:agent,input:input,session_id:sid})})\\\n .then(function(r){return r.json()})\\\n .then(function(result){\\\n if(result.session_id)sessionMap[agent]=result.session_id;\\\n if(result.output){\\\n addEvent({type:\"__output\",agent_name:agent,output:result.output});\\\n }\\\n refreshSessions();\\\n })\\\n .catch(function(err){addEvent({type:\"__error\",error:err.message||String(err)})})\\\n .finally(function(){btn.disabled=false;btn.textContent=\"Send\"});\\\n}\\\n\\\ndocument.getElementById(\"send-btn\").addEventListener(\"click\",sendMessage);\\\ndocument.getElementById(\"user-input\").addEventListener(\"keydown\",function(e){if(e.key===\"Enter\"&&!e.shiftKey){e.preventDefault();sendMessage()}});\\\n\\\n/* ---- init ---- */\\\nloadScore();\\\nconnectSSE();\\\n\\\n})();\\\n</script>\\\n</body>\\\n</html>';\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AAGvB,SAAS,gBAAAA,qBAAoB;AAa7B,SAAS,eAAe;;;AChBxB,SAAS,WAAW,eAAe,kBAAkB;AACrD,SAAS,YAAY;AACrB,OAAO,WAAW;AAClB,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAE7B,IAAM,SAAS,aAAa,WAAW;AAEvC,IAAM,EAAE,OAAO,IAAI;AAEnB,eAAsB,YAAY,aAAqC;AACrE,MAAI,CAAC,aAAa;AAChB,UAAM,WAAW,MAAM,OAAgC;AAAA,MACrD,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AACD,kBAAc,SAAS;AAAA,EACzB;AAEA,MAAI,CAAC,aAAa;AAChB,WAAO,MAAM,0BAA0B;AACvC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,KAAK,QAAQ,IAAI,GAAG,WAAW;AAE3C,MAAI,WAAW,GAAG,GAAG;AACnB,WAAO,MAAM,EAAE,KAAK,GAAG,WAAW,IAAI,GAAG,0BAA0B;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAElC,QAAM,QAAgC;AAAA,IACpC,gBAAgB,KAAK;AAAA,MACnB;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,UACP,KAAK;AAAA,UACL,OAAO;AAAA,QACT;AAAA,QACA,cAAc;AAAA,UACZ,iBAAiB;AAAA,UACjB,kBAAkB;AAAA,QACpB;AAAA,QACA,iBAAiB;AAAA,UACf,KAAK;AAAA,UACL,YAAY;AAAA,QACd;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,gBACE;AAAA,IAOF,cAAc;AAAA,IAEd,iBAAiB,KAAK;AAAA,MACpB;AAAA,QACE,iBAAiB;AAAA,UACf,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,kBAAkB;AAAA,UAClB,iBAAiB;AAAA,UACjB,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,SAAS;AAAA,QACX;AAAA,QACA,SAAS,CAAC,GAAG;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAelB,aAAa,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkB/B;AAEA,aAAW,CAAC,UAAU,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACvD,kBAAc,KAAK,KAAK,QAAQ,GAAG,OAAO;AAAA,EAC5C;AAEA,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,MAAM,oBAAe,WAAW,GAAG,CAAC;AACtD,UAAQ,IAAI;AACZ,UAAQ,IAAI,eAAe;AAC3B,UAAQ,IAAI,MAAM,KAAK,UAAU,WAAW,EAAE,CAAC;AAC/C,UAAQ,IAAI,MAAM,KAAK,0BAA0B,CAAC;AAClD,UAAQ,IAAI,MAAM,KAAK,iBAAiB,CAAC;AACzC,UAAQ,IAAI,MAAM,KAAK,iBAAiB,CAAC;AACzC,UAAQ,IAAI;AACd;;;ACrIA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAChC,OAAOC,YAAW;AAClB,OAAO,SAAS;AAChB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAAC;AAAA,OACK;AAEP,IAAMC,UAASD,cAAa,WAAW;AAEvC,eAAsB,WAAW,WAAmC;AAClE,QAAM,OAAO,QAAQ,aAAa,kBAAkB;AAEpD,MAAI,CAACF,YAAW,IAAI,GAAG;AACrB,IAAAG,QAAO,MAAM,EAAE,KAAK,GAAG,sBAAsB;AAC7C,YAAQ,MAAMF,OAAM,IAAI,8CAA8C,CAAC;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,YAAY,KAAK,IAAI;AAAA,EACrC,SAAS,KAAK;AACZ,IAAAE,QAAO;AAAA,MACL,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,MAC1D;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,iBAAsC;AAAA,IAC1C,CAAC,mBAAmB,mBAAmB;AAAA,IACvC,CAAC,gBAAgB,gBAAgB;AAAA,IACjC,CAAC,gBAAgB,gBAAgB;AAAA,EACnC;AAEA,aAAW,CAAC,eAAe,MAAM,KAAK,gBAAgB;AACpD,QAAI,MAAM,oBAAqB,eAAuD;AACpF,YAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,UAAI,CAAC,KAAK;AACR,QAAAA,QAAO,MAAM,EAAE,OAAO,GAAG,iBAAiB;AAC1C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,aAAW,SAAS,OAAO,OAAO,MAAM,MAAM,GAAG;AAC/C,UAAM,YAAY;AAAA,EACpB;AAEA,QAAM,UAAU,IAAI,aAAa,KAAK;AACtC,QAAM,UAAU,IAAI,EAAE,OAAO,OAAO,CAAC;AAGrC,MAAI,YAAY;AAEhB,UAAQ,OAAO,GAAG,eAAe,CAAC,MAAM;AACtC,IAAAA,QAAO,KAAK,EAAE,OAAO,EAAE,WAAW,GAAG,eAAe;AAAA,EACtD,CAAC;AAED,UAAQ,OAAO,GAAG,eAAe,MAAM;AACrC,YAAQ,MAAM,aAAa;AAAA,EAC7B,CAAC;AAGD,UAAQ,OAAO,GAAG,gBAAgB,CAAC,MAAM;AACvC,QAAI,CAAC,WAAW;AAEd,cAAQ,KAAK;AACb,kBAAY;AAAA,IACd;AACA,YAAQ,OAAO,MAAM,EAAE,IAAI;AAAA,EAC7B,CAAC;AAED,UAAQ,OAAO,GAAG,gBAAgB,MAAM;AACtC,QAAI,WAAW;AAEb,cAAQ,OAAO,MAAM,IAAI;AAAA,IAC3B,OAAO;AAEL,cAAQ,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AAGD,UAAQ,OAAO,GAAG,cAAc,CAAC,MAAM;AACrC,QAAI,WAAW;AACb,cAAQ,OAAO,MAAMF,OAAM,IAAI,iBAAiB,EAAE,YAAY,GAAG,CAAC;AAAA,IACpE,OAAO;AACL,cAAQ,KAAK;AACb,cAAQ,IAAIA,OAAM,IAAI,eAAe,EAAE,YAAY,GAAG,CAAC;AAAA,IACzD;AAAA,EACF,CAAC;AAED,UAAQ,OAAO,GAAG,YAAY,CAAC,MAAM;AACnC,QAAI,WAAW;AACb,cAAQ,OAAO,MAAMA,OAAM,IAAI,cAAc,EAAE,YAAY,KAAK,CAAC;AAAA,IACnE;AAAA,EACF,CAAC;AAED,UAAQ,OAAO,GAAG,cAAc,CAAC,MAAM;AACrC,YAAQ,KAAK;AACb,IAAAE,QAAO,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY;AAAA,EAClD,CAAC;AAED,UAAQ,OAAO,GAAG,+BAA+B,CAAC,MAAM;AACtD,IAAAA,QAAO,KAAK,EAAE,MAAM,EAAE,UAAU,GAAG,qCAAqC;AAAA,EAC1E,CAAC;AAED,UAAQ,OAAO,GAAG,kBAAkB,MAAM;AACxC,IAAAA,QAAO,KAAK,gCAAgC;AAAA,EAC9C,CAAC;AAED,UAAQ,OAAO,GAAG,mBAAmB,MAAM;AACzC,IAAAA,QAAO,MAAM,uCAAkC;AAAA,EACjD,CAAC;AAGD,QAAM,KAAK,gBAAgB;AAAA,IACzB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,UAAQ,IAAIF,OAAM,IAAI,yCAAoC,CAAC;AAE3D,MAAI;AAGJ,UAAQ,GAAG,UAAU,MAAM;AACzB,QAAI,UAAW,SAAQ,OAAO,MAAM,IAAI;AACxC,YAAQ,KAAK;AACb,YAAQ,IAAIA,OAAM,IAAI,UAAU,CAAC;AACjC,OAAG,MAAM;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAED,MAAI;AACF,WAAO,MAAM;AACX,YAAM,QAAQ,MAAM,GAAG,SAASA,OAAM,KAAK,IAAI,CAAC;AAChD,YAAM,UAAU,MAAM,KAAK;AAE3B,UAAI,CAAC,QAAS;AACd,UAAI,YAAY,UAAU,YAAY,OAAQ;AAG9C,kBAAY;AAEZ,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ,IAAI,aAAa,SAAS,SAAS;AAChE,oBAAY,OAAO;AAEnB,YAAI,CAAC,WAAW;AAEd,kBAAQ,IAAI,OAAO,OAAO,SAAS,IAAI;AAAA,QACzC,OAAO;AAEL,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,UAAW,SAAQ,OAAO,MAAM,IAAI;AACxC,gBAAQ,KAAK;AACb,QAAAE,QAAO;AAAA,UACL,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,UAC1D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,UAAQ,IAAIF,OAAM,IAAI,UAAU,CAAC;AACjC,KAAG,MAAM;AACT,UAAQ,KAAK,CAAC;AAChB;;;ACvLA,SAAS,cAAAG,aAAY,oBAAoB;AACzC,SAAS,WAAAC,gBAAe;AACxB,SAAS,gBAAgB;AACzB,OAAOC,YAAW;AAClB,OAAOC,UAAS;AAChB,SAAS,gBAAAC,qBAAoB;AAE7B,IAAMC,UAASD,cAAa,WAAW;AAEvC,IAAM,kBAAsE;AAAA,EAC1E,YAAY;AAAA,IACV,SAAS;AAAA,IACT,OAAO;AAAA,MACLF,OAAM,KAAK,uDAAuD,CAAC;AAAA,MACnEA,OAAM,KAAK,iCAAiC,CAAC;AAAA,EACjD;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,OAAO,SAASA,OAAM,KAAK,cAAc,CAAC;AAAA,MACxCA,OAAM,KAAK,kCAAkC,CAAC;AAAA;AAAA;AAAA,MAG9CA,OAAM,KAAK,+CAA+C,CAAC;AAAA,MAC3DA,OAAM,KAAK,6BAA6B,CAAC;AAAA,EAC7C;AAAA,EACA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,OAAO;AAAA,MACLA,OAAM,KAAK,iCAAiC,CAAC;AAAA;AAAA;AAAA,MAG7CA,OAAM,KAAK,uDAAuD,CAAC;AAAA,MACnEA,OAAM,KAAK,iCAAiC,CAAC;AAAA,EACjD;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,OAAO,SAASA,OAAM,KAAK,cAAc,CAAC;AAAA,MACxCA,OAAM,KAAK,wDAAwD,CAAC;AAAA;AAAA;AAAA,MAGpEA,OAAM,KAAK,kCAAkC,CAAC;AAAA;AAAA;AAAA,MAG9CA,OAAM,KAAK,iEAAiE,CAAC;AAAA;AAAA;AAAA,MAG7EA,OAAM,KAAK,gDAAgD,CAAC;AAAA,EAChE;AACF;AAEA,SAAS,mBAAmB,OAAuB;AAEjD,MAAI,gBAAgB,KAAK,GAAG;AAC1B,WAAO,gBAAgB,KAAK,EAAE;AAAA,EAChC;AAEA,MAAI,MAAM,WAAW,GAAG,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,KAAK;AAC1B;AAEA,SAAS,mBAAmB,aAA8B;AACxD,QAAM,UAAUD,SAAQ,QAAQ,IAAI,GAAG,cAAc;AACrD,MAAI,CAACD,YAAW,OAAO,EAAG,QAAO;AAEjC,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AACrD,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,WAAO,eAAe;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,WAAW,WAAkC;AACjE,QAAM,cAAc,mBAAmB,SAAS;AAGhD,QAAM,UAAUC,SAAQ,QAAQ,IAAI,GAAG,cAAc;AACrD,MAAI,CAACD,YAAW,OAAO,GAAG;AACxB,IAAAK,QAAO,MAAM,gDAAgD;AAC7D,YAAQ,MAAMH,OAAM,IAAI,oDAAoD,CAAC;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,mBAAmB,WAAW,GAAG;AACnC,YAAQ,IAAIA,OAAM,MAAM,YAAO,WAAW,uBAAuB,CAAC;AAClE;AAAA,EACF;AAGA,QAAM,UAAUC,KAAI,cAAc,WAAW,KAAK,EAAE,MAAM;AAE1D,MAAI;AACF,aAAS,eAAe,WAAW,IAAI;AAAA,MACrC,KAAK,QAAQ,IAAI;AAAA,MACjB,OAAO;AAAA,IACT,CAAC;AACD,YAAQ,QAAQ,aAAa,WAAW,EAAE;AAAA,EAC5C,SAAS,OAAO;AACd,YAAQ,KAAK,qBAAqB,WAAW,EAAE;AAC/C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,IAAAE,QAAO,MAAM,EAAE,OAAO,SAAS,SAAS,YAAY,GAAG,qBAAqB;AAC5E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,WAAW,gBAAgB,SAAS;AAC1C,MAAI,UAAU;AACZ,YAAQ,IAAI;AACZ,YAAQ,IAAI,UAAU;AACtB,YAAQ,IAAI,SAAS,KAAK;AAC1B,YAAQ,IAAI;AAAA,EACd,OAAO;AACL,YAAQ,IAAI;AACZ,YAAQ;AAAA,MACNH,OAAM,IAAI,oDAAoD;AAAA,IAChE;AACA,YAAQ,IAAI;AAAA,EACd;AACF;;;AC3HA,SAAS,cAAAI,mBAAkB;AAC3B,SAAS,WAAAC,gBAAe;AACxB,OAAOC,YAAW;AAClB;AAAA,EACE,eAAAC;AAAA,EACA,qBAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,gBAAAC;AAAA,OACK;AAEP,IAAMC,UAASD,cAAa,WAAW;AAEvC,IAAM,KAAK,CAAC,QAAgB,QAAQ,IAAIN,OAAM,MAAM,cAAc,GAAG,CAAC;AACtE,IAAM,OAAO,CAAC,QAAgB,QAAQ,IAAIA,OAAM,IAAI,cAAc,GAAG,CAAC;AAEtE,eAAsB,aAAa,WAAmC;AACpE,QAAM,OAAOD,SAAQ,aAAa,kBAAkB;AAEpD,UAAQ,IAAIC,OAAM,KAAK;AAAA,WAAc,IAAI;AAAA,CAAO,CAAC;AAEjD,MAAI,CAACF,YAAW,IAAI,GAAG;AACrB,SAAK,2BAA2B,IAAI;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMG,aAAY,KAAK,IAAI;AACnC,OAAG,qBAAqB;AAAA,EAC1B,SAAS,KAAK;AACZ,SAAK,yBAAyB;AAC9B,IAAAM,QAAO;AAAA,MACL,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,MAC1D;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY;AAGhB,QAAM,iBAA8C;AAAA,IAClD,CAACL,oBAAmB,qBAAqB,mBAAmB;AAAA,IAC5D,CAACC,iBAAgB,kBAAkB,gBAAgB;AAAA,IACnD,CAACC,iBAAgB,kBAAkB,gBAAgB;AAAA,EACrD;AAEA,MAAI,mBAAmB;AACvB,aAAW,CAAC,eAAe,MAAM,MAAM,KAAK,gBAAgB;AAC1D,QACE,MAAM,oBACL,eACD;AACA,yBAAmB;AACnB,YAAM,MAAMC,gBAAe,SAAS,MAAM;AAC1C,UAAI,KAAK;AACP,WAAG,eAAe,OAAO,OAAO,SAAS,UAAU;AAAA,MACrD,OAAO;AACL,aAAK,eAAe,OAAO,OAAO,SAAS,cAAc;AACzD,oBAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,kBAAkB;AACrB,OAAG,8BAA8B;AAAA,EACnC;AAGA,QAAM,YAAY,OAAO,KAAK,MAAM,MAAM;AAC1C,KAAG,UAAU,SAAS,YAAY,UAAU,WAAW,IAAI,KAAK,OAAO,aAAa;AAGpF,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC5D,eAAW,SAAS,MAAM,QAAQ;AAChC,YAAM,YAAY,MAAM;AAGxB,YAAM,cAAsC;AAAA,QAC1C,QAAQ;AAAA,MACV;AAEA,YAAM,SAAS,YAAY,SAAS;AACpC,UAAI,QAAQ;AACV,cAAM,MAAMA,gBAAe,SAAS,MAAM;AAC1C,YAAI,KAAK;AACP;AAAA,YACE,YAAY,YAAY,SAAS,WAAW,OAAO,SAAS;AAAA,UAC9D;AAAA,QACF,OAAO;AACL;AAAA,YACE,YAAY,YAAY,SAAS,WAAW,OAAO,SAAS;AAAA,UAC9D;AACA,sBAAY;AAAA,QACd;AAAA,MACF,OAAO;AACL,WAAG,YAAY,YAAY,SAAS,WAAW,cAAc;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,IAAI,EAAE;AACd,MAAI,WAAW;AACb,YAAQ;AAAA,MACNL,OAAM,OAAO,sDAAsD;AAAA,IACrE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB,OAAO;AACL,YAAQ;AAAA,MACNA,OAAM,MAAM,oBAAoB,IAC9BA,OAAM,IAAI,6BAA6B;AAAA,IAC3C;AAAA,EACF;AACF;;;ACrHA,SAAS,cAAAQ,mBAAkB;AAC3B,SAAS,WAAAC,gBAAe;AACxB,SAAS,YAAY;AACrB,OAAO,aAAa;AACpB,OAAOC,YAAW;AAClB;AAAA,EACE,gBAAAC;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,OACK;AAGP,IAAMC,UAASD,cAAa,cAAc;AAC1C,IAAM,OAAO;AAEb,SAAS,cAAc,KAAsB;AAC3C,SAAO,KAAK,UAAU,KAAK,CAAC,MAAM,UAAU;AAC1C,QAAI,iBAAiB,MAAO,QAAO,EAAE,SAAS,MAAM,SAAS,MAAM,MAAM,KAAK;AAC9E,QAAI,OAAO,UAAU,WAAY,QAAO;AACxC,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,YAAY,KAAmB;AACtC,QAAM,MACJ,QAAQ,aAAa,WAAW,SAChC,QAAQ,aAAa,UAAU,UAC/B;AACF,OAAK,MAAM,MAAM,GAAG;AACtB;AAEA,eAAsB,cAAc,WAAmC;AACrE,QAAM,OAAOJ,SAAQ,aAAa,kBAAkB;AAEpD,MAAI,CAACD,YAAW,IAAI,GAAG;AACrB,IAAAM,QAAO,MAAM,EAAE,KAAK,GAAG,sBAAsB;AAC7C,YAAQ,MAAMJ,OAAM,IAAI,8CAA8C,CAAC;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAME,aAAY,KAAK,IAAI;AAAA,EACrC,SAAS,KAAK;AACZ,IAAAE,QAAO,MAAM,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,GAAG,sBAAsB;AAChG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,IAAIH,cAAa,KAAK;AAGtC,QAAM,kBAAkB,oBAAI,IAAsD;AAClF,UAAQ,OAAO,GAAG,eAAe,CAAC,MAAM;AACtC,QAAI,CAAC,gBAAgB,IAAI,EAAE,UAAU,GAAG;AACtC,sBAAgB,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,YAAY,oBAAI,KAAK,EAAE,CAAC;AAAA,IACxF;AAAA,EACF,CAAC;AAGD,QAAM,aAAa,oBAAI,IAAqB;AAC5C,UAAQ,OAAO,MAAM,CAAC,UAAU;AAC9B,UAAM,OAAO,cAAc,KAAK;AAChC,eAAW,UAAU,YAAY;AAC/B,aAAO,MAAM,yBAAyB,OAAO,MAAM;AAAA,IACrD;AAAA,EACF,CAAC;AAGD,QAAM,MAAM,QAAQ;AACpB,MAAI,IAAI,QAAQ,KAAK,CAAC;AAGtB,MAAI,IAAI,WAAW,CAAC,MAAM,QAAQ;AAChC,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AACD,QAAI,MAAM,OAAO;AACjB,eAAW,IAAI,GAAG;AAClB,SAAK,GAAG,SAAS,MAAM,WAAW,OAAO,GAAG,CAAC;AAAA,EAC/C,CAAC;AAGD,MAAI,IAAI,cAAc,CAAC,MAAM,QAAQ;AACnC,UAAM,SAAS,OAAO;AAAA,MACpB,OAAO,QAAQ,QAAQ,MAAM,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;AAAA,QACxD;AAAA,QACA;AAAA,UACE,MAAM,MAAM;AAAA,UACZ,aAAa,MAAM;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,WAAW,MAAM;AAAA,UACjB,aAAa,MAAM,OAAO;AAAA,UAC1B,QAAQ,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,QACxC;AAAA,MACF,CAAC;AAAA,IACH;AACA,QAAI,KAAK;AAAA,MACP,MAAM,QAAQ,MAAM;AAAA,MACpB,aAAa,QAAQ,MAAM;AAAA,MAC3B,eAAe,QAAQ,MAAM;AAAA,MAC7B,OAAO,QAAQ,MAAM;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,MAAI,IAAI,iBAAiB,CAAC,MAAM,QAAQ;AACtC,UAAM,WAAW,MAAM,KAAK,gBAAgB,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM;AACzE,YAAM,UAAU,QAAQ,WAAW,EAAE;AACrC,aAAO;AAAA,QACL;AAAA,QACA,YAAY,KAAK;AAAA,QACjB,eAAe,SAAS,SAAS,UAAU;AAAA,QAC3C,YAAY,KAAK;AAAA,MACnB;AAAA,IACF,CAAC;AACD,QAAI,KAAK,SAAS,QAAQ,CAAC;AAAA,EAC7B,CAAC;AAED,MAAI,IAAI,qBAAqB,CAAC,KAAK,QAAQ;AACzC,UAAM,UAAU,QAAQ,WAAW,IAAI,OAAO,EAAE;AAChD,QAAI,CAAC,SAAS;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAG;AAAA,IAAQ;AAC9E,QAAI,KAAK,OAAO;AAAA,EAClB,CAAC;AAED,MAAI,KAAK,YAAY,OAAO,KAAK,QAAQ;AACvC,UAAM,EAAE,OAAO,OAAO,WAAW,IAAI,IAAI;AACzC,QAAI,CAAC,SAAS,CAAC,OAAO;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,+BAA+B,CAAC;AAAG;AAAA,IAAQ;AACjG,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,IAAI,OAAO,OAAO,UAAU;AACzD,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,CAAC;AAAA,IAClF;AAAA,EACF,CAAC;AAGD,MAAI,IAAI,KAAK,CAAC,MAAM,QAAQ;AAC1B,QAAI,KAAK,MAAM,EAAE,KAAK,cAAc,CAAC;AAAA,EACvC,CAAC;AAED,MAAI,OAAO,MAAM,MAAM;AACrB,UAAM,MAAM,sBAAsB;AAClC,YAAQ,IAAI;AACZ,YAAQ,IAAID,OAAM,KAAK,gBAAgB,CAAC;AACxC,YAAQ,IAAIA,OAAM,IAAI,OAAO,GAAG,CAAC;AACjC,YAAQ,IAAI;AACZ,YAAQ,IAAIA,OAAM,IAAI,WAAW,KAAK,QAAQ,MAAM,QAAQ,KAAK;AACjE,YAAQ,IAAIA,OAAM,IAAI,YAAY,IAAI,OAAO,KAAK,QAAQ,MAAM,MAAM,EAAE,KAAK,IAAI,CAAC;AAClF,YAAQ,IAAI;AACZ,gBAAY,GAAG;AAAA,EACjB,CAAC;AAED,UAAQ,GAAG,UAAU,MAAM;AACzB,YAAQ,IAAIA,OAAM,IAAI,iCAAiC,CAAC;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;AAMA,SAAS,gBAAwB;AAC/B,SAAO;AAgWT;;;ALrgBA,OAAO;AAGP,IAAMK,UAASC,cAAa,WAAW;AAEvC,QAAQ,GAAG,sBAAsB,CAAC,WAAW;AAC3C,EAAAD,QAAO,MAAM,EAAE,OAAO,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,EAAE,GAAG,qBAAqB;AACxG,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,QAAQ,GAAG,qBAAqB,CAAC,QAAQ;AACvC,EAAAA,QAAO,MAAM,EAAE,OAAO,IAAI,QAAQ,GAAG,aAAa;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;AASD,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,UAAU,EACf,YAAY,mEAA8D,EAC1E,QAAQ,OAAO;AAElB,QACG,QAAQ,qBAAqB,EAC7B,YAAY,4BAA4B,EACxC,OAAO,OAAO,gBAAyB;AACtC,QAAM,YAAY,WAAW;AAC/B,CAAC;AAEH,QACG,QAAQ,aAAa,EACrB,YAAY,iCAAiC,EAC7C,OAAO,OAAO,UAAmB;AAChC,QAAM,WAAW,KAAK;AACxB,CAAC;AAEH,QACG,QAAQ,aAAa,EACrB,YAAY,6BAA6B,EACzC,OAAO,OAAO,UAAkB;AAC/B,QAAM,WAAW,KAAK;AACxB,CAAC;AAEH,QACG,QAAQ,eAAe,EACvB,YAAY,0CAA0C,EACtD,OAAO,OAAO,UAAmB;AAChC,QAAM,aAAa,KAAK;AAC1B,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,8CAAyC,EACrD,OAAO,OAAO,UAAmB;AAChC,QAAM,aAAa,KAAK;AAC1B,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,mEAA8D,EAC1E,OAAO,OAAO,UAAmB;AAChC,QAAM,cAAc,KAAK;AAC3B,CAAC;AAEH,QAAQ,MAAM;","names":["createLogger","existsSync","chalk","createLogger","logger","existsSync","resolve","chalk","ora","createLogger","logger","existsSync","resolve","chalk","ScoreLoader","AnthropicProvider","OpenAIProvider","GeminiProvider","SecretsManager","createLogger","logger","existsSync","resolve","chalk","TuttiRuntime","ScoreLoader","createLogger","logger","logger","createLogger"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuttiai/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Tutti CLI — scaffold and run multi-agent projects",
5
5
  "type": "module",
6
6
  "bin": {