openmates 0.16.0-alpha.0 → 0.16.0-alpha.10

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.
@@ -21607,6 +21607,13 @@ var OpenMatesPlans = class {
21607
21607
  return response.entries ?? [];
21608
21608
  }
21609
21609
  async getRawPlan(planId) {
21610
+ try {
21611
+ const response = await this.client.get(`/v1/user-plans/${encodeURIComponent(planId)}`);
21612
+ if (!response.plan) throw new OpenMatesApiError(500, { detail: "User plan response missing plan" });
21613
+ return response.plan;
21614
+ } catch (error) {
21615
+ if (!(error instanceof OpenMatesApiError) || error.status !== 404) throw error;
21616
+ }
21610
21617
  const plans = await this.listRaw({ activeOnly: false });
21611
21618
  const plan = plans.find((candidate) => candidate.plan_id === planId || candidate.plan_id?.startsWith(planId));
21612
21619
  if (!plan) throw new OpenMatesApiError(404, { detail: "Plan not found" });
@@ -24640,6 +24647,7 @@ var CORE_WORKER_SERVICES = [
24640
24647
  "task-worker",
24641
24648
  "user-init-worker",
24642
24649
  "core-worker",
24650
+ "reminder-worker",
24643
24651
  "task-scheduler",
24644
24652
  "app-ai-worker",
24645
24653
  "app-images-worker",
@@ -24652,6 +24660,7 @@ var CORE_WORKER_SERVICES = [
24652
24660
  ];
24653
24661
  var OPENMATESCLOUD_OVERLAY_DIR = "OpenMatesCloud";
24654
24662
  var OPENMATESCLOUD_COMPOSE_FILE = "docker-compose.openmatescloud.yml";
24663
+ var OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE = "backend/core/docker-compose.no-webapp.yml";
24655
24664
  var SOURCE_COMPOSE_FILES = {
24656
24665
  core: "backend/core/docker-compose.yml",
24657
24666
  upload: "backend/upload/docker-compose.yml",
@@ -24724,6 +24733,7 @@ var INTEGRATION_SECRET_PREFIXES = [
24724
24733
  "SECRET__REVOLUT_BUSINESS__",
24725
24734
  "SECRET__INVOICE_"
24726
24735
  ];
24736
+ var CELERY_PROBE_CHECK_TIMEOUT_SECONDS = 15;
24727
24737
  var RUNTIME_CHECKS = {
24728
24738
  core: [
24729
24739
  { id: "compose.required_services", required: true, timeoutSeconds: 15 },
@@ -24731,8 +24741,8 @@ var RUNTIME_CHECKS = {
24731
24741
  { id: "core.database", required: true, timeoutSeconds: 10 },
24732
24742
  { id: "core.cache", required: true, timeoutSeconds: 10 },
24733
24743
  { id: "core.vault", required: true, timeoutSeconds: 10 },
24734
- { id: "core.worker_queue", required: true, timeoutSeconds: 15 },
24735
- { id: "core.scheduler_freshness", required: true, timeoutSeconds: 5 },
24744
+ { id: "core.worker_queue", required: true, timeoutSeconds: CELERY_PROBE_CHECK_TIMEOUT_SECONDS },
24745
+ { id: "core.scheduler_freshness", required: true, timeoutSeconds: CELERY_PROBE_CHECK_TIMEOUT_SECONDS },
24736
24746
  { id: "core.chat_plumbing", required: true, timeoutSeconds: 20 }
24737
24747
  ],
24738
24748
  upload: [
@@ -24867,7 +24877,7 @@ function planOpenMatesCloudOverlay(input) {
24867
24877
  deploymentMode,
24868
24878
  enabled: true,
24869
24879
  overlayPath,
24870
- composeFiles: [overlayComposeFile],
24880
+ composeFiles: [overlayComposeFile, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE],
24871
24881
  env: {
24872
24882
  OPENMATES_CLOUD_OVERLAY_ENABLED: "true",
24873
24883
  OPENMATES_CLOUD_OVERLAY_PATH: overlayPath
@@ -24937,6 +24947,7 @@ function appendSelectedServices(args, selectedServices, filterRequested) {
24937
24947
  function shouldCheckWebHealth(input) {
24938
24948
  const role = parseServerRole(input.role);
24939
24949
  if (role !== "core") return false;
24950
+ if (input.deploymentMode === "official_cloud") return false;
24940
24951
  return input.filterRequested !== true || (input.selectedServices ?? []).includes("webapp");
24941
24952
  }
24942
24953
  function planUpdate(input) {
@@ -25198,6 +25209,9 @@ function planRuntimeMonitoringServices(input) {
25198
25209
  ].join("\n")
25199
25210
  };
25200
25211
  }
25212
+ function shouldAutoInstallRuntimeMonitoringServices(env) {
25213
+ return env.OPENMATES_SKIP_RUNTIME_MONITORING !== "1";
25214
+ }
25201
25215
 
25202
25216
  // src/serverHealth.ts
25203
25217
  import dnsModule, { promises as dns } from "dns";
@@ -25472,6 +25486,7 @@ var ROLE_TEMPLATE_FILES = {
25472
25486
  upload: join6("upload", "docker-compose.yml"),
25473
25487
  preview: join6("preview", "docker-compose.preview.yml")
25474
25488
  };
25489
+ var CORE_NO_WEBAPP_TEMPLATE_FILE = join6("core", "docker-compose.no-webapp.yml");
25475
25490
  var CORE_PROMTAIL_CONFIG_FILE = join6("backend", "core", "monitoring", "promtail", "promtail-config.yaml");
25476
25491
  var COMPOSE_OVERRIDE2 = join6("backend", "core", "docker-compose.override.yml");
25477
25492
  var DEFAULT_INSTALL_PATH = join6(homedir6(), "openmates");
@@ -25723,6 +25738,12 @@ function getServerDeploymentMode(flags, config) {
25723
25738
  if (typeof flags["deployment-mode"] === "string") return normalizeServerDeploymentMode(flags["deployment-mode"]);
25724
25739
  return normalizeServerDeploymentMode(config?.deploymentMode);
25725
25740
  }
25741
+ function getInstallDeploymentMode(installPath, config) {
25742
+ const env = readEnvMap(installPath);
25743
+ if (env.OPENMATES_CLOUD_OVERLAY_ENABLED === "true") return "official_cloud";
25744
+ if (env.OPENMATES_CLOUD_OVERLAY_ENABLED === "false") return "self_host";
25745
+ return normalizeServerDeploymentMode(config?.deploymentMode);
25746
+ }
25726
25747
  function getOpenMatesCloudOverlayPath(flags, config) {
25727
25748
  const explicit = flags["openmatescloud-path"] ?? flags["openmates-cloud-path"];
25728
25749
  if (explicit === true) throw new Error("Provide an OpenMatesCloud path: --openmatescloud-path <dir>.");
@@ -25801,9 +25822,10 @@ function featureKind(featureId) {
25801
25822
  return featureId.split(":", 1)[0] || "unknown";
25802
25823
  }
25803
25824
  function composeArgs(installPath, withOverrides, installMode = getInstallMode(installPath), role = "core") {
25804
- const env = readEnvMap(installPath);
25805
25825
  const config = loadConfigForInstallPath(installPath);
25806
- const deploymentMode = env.OPENMATES_CLOUD_OVERLAY_ENABLED === "true" ? "official_cloud" : env.OPENMATES_CLOUD_OVERLAY_ENABLED === "false" ? "self_host" : normalizeServerDeploymentMode(config?.deploymentMode);
25826
+ const deploymentMode = getInstallDeploymentMode(installPath, config);
25827
+ ensureOfficialCloudNoWebappComposeFile(installPath, deploymentMode, role);
25828
+ const env = readEnvMap(installPath);
25807
25829
  const overlayPath = env.OPENMATES_CLOUD_OVERLAY_PATH || config?.openMatesCloudOverlayPath || void 0;
25808
25830
  const resolvedOverlayPath = overlayPath ?? defaultOpenMatesCloudOverlayPath(installPath);
25809
25831
  const overlayComposeFile = defaultOpenMatesCloudComposeFile(resolvedOverlayPath);
@@ -25947,9 +25969,34 @@ async function fetchText(url) {
25947
25969
  function packagedTemplatePath(role) {
25948
25970
  return join6(dirname5(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
25949
25971
  }
25972
+ function packagedNoWebappTemplatePath() {
25973
+ return join6(dirname5(new URL(import.meta.url).pathname), "..", "templates", CORE_NO_WEBAPP_TEMPLATE_FILE);
25974
+ }
25950
25975
  function packagedCaddyTemplatePath(role) {
25951
25976
  return join6(dirname5(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
25952
25977
  }
25978
+ function readOfficialCloudNoWebappComposeTemplate() {
25979
+ const packaged = packagedNoWebappTemplatePath();
25980
+ if (existsSync8(packaged)) return readFileSync6(packaged, "utf-8");
25981
+ return [
25982
+ "# backend/core/docker-compose.no-webapp.yml",
25983
+ "#",
25984
+ "# Keeps official OpenMatesCloud core runtimes backend-only while regular",
25985
+ "# self-host installs continue to start the webapp from the base compose file.",
25986
+ "",
25987
+ "services:",
25988
+ " webapp:",
25989
+ ' profiles: ["webapp"]',
25990
+ ""
25991
+ ].join("\n");
25992
+ }
25993
+ function ensureOfficialCloudNoWebappComposeFile(installPath, deploymentMode, role) {
25994
+ if (deploymentMode !== "official_cloud" || role !== "core") return;
25995
+ const filePath = join6(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE);
25996
+ if (existsSync8(filePath)) return;
25997
+ mkdirSync5(dirname5(filePath), { recursive: true });
25998
+ writeFileSync4(filePath, readOfficialCloudNoWebappComposeTemplate());
25999
+ }
25953
26000
  function fileHash(path2) {
25954
26001
  if (!existsSync8(path2)) return null;
25955
26002
  return createHash12("sha256").update(readFileSync6(path2)).digest("hex");
@@ -25973,6 +26020,9 @@ async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
25973
26020
  mkdirSync5(vaultConfigDir, { recursive: true });
25974
26021
  mkdirSync5(join6(installPath, "config", "providers"), { recursive: true });
25975
26022
  writeFileSync4(join6(installPath, ROLE_IMAGE_COMPOSE_FILES[role]), await loadSelfHostComposeTemplate(templateRefForImageTag(imageTag, getPackageVersion()), role));
26023
+ if (role === "core") {
26024
+ writeFileSync4(join6(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE), readOfficialCloudNoWebappComposeTemplate());
26025
+ }
25976
26026
  if (role === "core") {
25977
26027
  const promtailConfigPath = join6(installPath, CORE_PROMTAIL_CONFIG_FILE);
25978
26028
  mkdirSync5(dirname5(promtailConfigPath), { recursive: true });
@@ -26708,6 +26758,7 @@ async function serverStart(flags) {
26708
26758
  const config = loadConfigForInstallPath(installPath);
26709
26759
  const role = getServerRole(flags, config);
26710
26760
  const installMode = getInstallMode(installPath, config);
26761
+ const deploymentMode = getInstallDeploymentMode(installPath, config);
26711
26762
  const pullArgs = [...composeArgs(installPath, withOverrides, installMode, role), "pull"];
26712
26763
  const args = [...composeArgs(installPath, withOverrides, installMode, role), "up", "-d"];
26713
26764
  if (hasServiceFilter(flags)) {
@@ -26730,8 +26781,12 @@ async function serverStart(flags) {
26730
26781
  printJson({ command: "start", status: "success", path: installPath });
26731
26782
  } else {
26732
26783
  console.log("\nServer started.");
26733
- console.log("Web app: http://localhost:5173");
26734
26784
  console.log("API: http://localhost:8000");
26785
+ if (deploymentMode === "official_cloud") {
26786
+ console.log("Web app: deployed separately for official cloud");
26787
+ } else {
26788
+ console.log("Web app: http://localhost:5173");
26789
+ }
26735
26790
  if (withOverrides) {
26736
26791
  console.log("Directus CMS: http://localhost:8055");
26737
26792
  console.log("Grafana: http://localhost:3000");
@@ -26880,7 +26935,7 @@ async function serverInstall(flags) {
26880
26935
  ...cliUrls2
26881
26936
  });
26882
26937
  try {
26883
- installRuntimeMonitoringServices(installPath, role);
26938
+ autoInstallRuntimeMonitoringServices(installPath, role);
26884
26939
  } catch (error) {
26885
26940
  throw new Error(`Server files were installed but runtime monitoring service installation failed: ${error instanceof Error ? error.message : String(error)}`);
26886
26941
  }
@@ -26964,7 +27019,7 @@ CLI default API: ${cliUrls2.apiUrl}`);
26964
27019
  ...cliUrls
26965
27020
  });
26966
27021
  try {
26967
- installRuntimeMonitoringServices(installPath, role);
27022
+ autoInstallRuntimeMonitoringServices(installPath, role);
26968
27023
  } catch (error) {
26969
27024
  throw new Error(`Server files were installed but runtime monitoring service installation failed: ${error instanceof Error ? error.message : String(error)}`);
26970
27025
  }
@@ -27111,6 +27166,14 @@ function installRuntimeMonitoringServices(installPath, role) {
27111
27166
  execSync(`systemctl enable --now ${timers.map(shellQuote).join(" ")}`, { stdio: "pipe" });
27112
27167
  for (const timer of timers) execFileSync3("systemctl", ["is-active", "--quiet", timer], { stdio: "pipe" });
27113
27168
  }
27169
+ function autoInstallRuntimeMonitoringServices(installPath, role) {
27170
+ if (!shouldAutoInstallRuntimeMonitoringServices(process.env)) {
27171
+ console.error("Skipping runtime monitoring service installation because OPENMATES_SKIP_RUNTIME_MONITORING=1.");
27172
+ return "skipped";
27173
+ }
27174
+ installRuntimeMonitoringServices(installPath, role);
27175
+ return "installed";
27176
+ }
27114
27177
  async function serverUpdate(rest, flags) {
27115
27178
  if (rest[0] === "status") {
27116
27179
  const installPath2 = resolveServerPath(flags);
@@ -27149,6 +27212,7 @@ async function serverUpdate(rest, flags) {
27149
27212
  const role = getServerRole(flags, config);
27150
27213
  const withOverrides = config?.composeProfile === "full";
27151
27214
  const installMode = getInstallMode(installPath, config);
27215
+ const deploymentMode = getInstallDeploymentMode(installPath, config);
27152
27216
  const filterRequested = hasServiceFilter(flags);
27153
27217
  const selectedServices = filterRequested ? selectedComposeServices(role, flags) : [];
27154
27218
  const missingEnvKeys = missingRequiredEnvKeys(installPath, role);
@@ -27261,7 +27325,7 @@ async function serverUpdate(rest, flags) {
27261
27325
  try {
27262
27326
  writeUpdateStatus(installPath, role, { status: "in_progress", targetImageTag: target.tag, sourceLinks, providerKeyReminders: secretPreflight.emptySecretEnvKeys, step: "health-check" });
27263
27327
  await waitForServerHealth(installPath, role, {
27264
- checkWebApp: shouldCheckWebHealth({ role, selectedServices, filterRequested })
27328
+ checkWebApp: shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested })
27265
27329
  });
27266
27330
  const runtimeOutput = runRuntimeVerification(installPath, role, config);
27267
27331
  await persistRuntimeResult(installPath, role, runtimeOutput);
@@ -27269,7 +27333,7 @@ async function serverUpdate(rest, flags) {
27269
27333
  const notificationDelivery = await dispatchRuntimeEvent(installPath, role, "post_update_failed", runtimeOutput.checks, (/* @__PURE__ */ new Date()).toISOString());
27270
27334
  let monitoringProvision = "installed";
27271
27335
  try {
27272
- installRuntimeMonitoringServices(installPath, role);
27336
+ monitoringProvision = autoInstallRuntimeMonitoringServices(installPath, role);
27273
27337
  } catch {
27274
27338
  monitoringProvision = "failed";
27275
27339
  }
@@ -27295,7 +27359,7 @@ async function serverUpdate(rest, flags) {
27295
27359
  saveServerConfig({ ...config, imageTag: target.tag, imageChannel: target.channel });
27296
27360
  }
27297
27361
  try {
27298
- installRuntimeMonitoringServices(installPath, role);
27362
+ autoInstallRuntimeMonitoringServices(installPath, role);
27299
27363
  } catch (error) {
27300
27364
  const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${join6(roleBackupDir(installPath, role), safetyPlan.backupName)}` : null;
27301
27365
  writeUpdateStatus(installPath, role, {
@@ -27331,14 +27395,14 @@ async function serverUpdate(rest, flags) {
27331
27395
  path: installPath,
27332
27396
  mode: "source",
27333
27397
  selectedServices: filterRequested ? selectedServices : "all",
27334
- checkWebApp: shouldCheckWebHealth({ role, selectedServices, filterRequested }),
27398
+ checkWebApp: shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested }),
27335
27399
  dryRun: true
27336
27400
  });
27337
27401
  } else {
27338
27402
  console.log("Update plan:");
27339
27403
  console.log(" Mode: source");
27340
27404
  console.log(` Services: ${filterRequested ? selectedServices.join(", ") : "all"}`);
27341
- console.log(` Web health: ${shouldCheckWebHealth({ role, selectedServices, filterRequested }) ? "enabled" : "skipped"}`);
27405
+ console.log(` Web health: ${shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested }) ? "enabled" : "skipped"}`);
27342
27406
  console.log(" Commands: git pull --ff-only, docker compose build, docker compose up -d, health checks");
27343
27407
  }
27344
27408
  return;
@@ -27386,7 +27450,7 @@ async function serverUpdate(rest, flags) {
27386
27450
  );
27387
27451
  code = await runInteractive("docker", upArgs, installPath);
27388
27452
  if (code !== 0) process.exit(code);
27389
- const checkWebApp = shouldCheckWebHealth({ role, selectedServices, filterRequested });
27453
+ const checkWebApp = shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested });
27390
27454
  console.error(checkWebApp ? "Waiting for API and web health checks..." : "Waiting for API health checks...");
27391
27455
  let successfulRuntimeOutput = null;
27392
27456
  try {
@@ -27397,7 +27461,7 @@ async function serverUpdate(rest, flags) {
27397
27461
  const notificationDelivery = await dispatchRuntimeEvent(installPath, role, "post_update_failed", runtimeOutput.checks, (/* @__PURE__ */ new Date()).toISOString());
27398
27462
  let monitoringProvision = "installed";
27399
27463
  try {
27400
- installRuntimeMonitoringServices(installPath, role);
27464
+ monitoringProvision = autoInstallRuntimeMonitoringServices(installPath, role);
27401
27465
  } catch {
27402
27466
  monitoringProvision = "failed";
27403
27467
  }
@@ -27419,7 +27483,7 @@ async function serverUpdate(rest, flags) {
27419
27483
  throw error;
27420
27484
  }
27421
27485
  try {
27422
- installRuntimeMonitoringServices(installPath, role);
27486
+ autoInstallRuntimeMonitoringServices(installPath, role);
27423
27487
  } catch (error) {
27424
27488
  writeUpdateStatus(installPath, role, { status: "degraded", step: "monitoring-service", restoreStatus: "restore_unavailable", restoreCommand: null });
27425
27489
  throw new Error(`Runtime checks passed but monitoring service installation failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -45157,6 +45221,172 @@ notebook:
45157
45221
  }
45158
45222
  };
45159
45223
 
45224
+ // ../ui/src/demo_chats/data/example_chats/audio-generate-product-success-chime.ts
45225
+ var audioGenerateProductSuccessChimeChat = {
45226
+ chat_id: "example-audio-generate-product-success-chime",
45227
+ slug: "audio-generate-product-success-chime",
45228
+ title: "example_chats.audio_generate_product_success_chime.title",
45229
+ summary: "example_chats.audio_generate_product_success_chime.summary",
45230
+ icon: "volume2",
45231
+ category: "design",
45232
+ keywords: ["audio generation", "sound effects", "ElevenLabs", "product sound", "OpenMates apps"],
45233
+ follow_up_suggestions: [],
45234
+ messages: [
45235
+ {
45236
+ id: "0fe80324-42f5-4b87-9e11-0b0d8f36c9d5",
45237
+ role: "user",
45238
+ content: "example_chats.audio_generate_product_success_chime.message_1",
45239
+ created_at: 1786382134
45240
+ },
45241
+ {
45242
+ id: "87bb2a45-d06b-4afa-8a4d-d2f4173647b8",
45243
+ role: "assistant",
45244
+ content: "example_chats.audio_generate_product_success_chime.message_2",
45245
+ created_at: 1786382154,
45246
+ user_message_id: "0fe80324-42f5-4b87-9e11-0b0d8f36c9d5",
45247
+ response_credits: 31,
45248
+ category: "general_knowledge",
45249
+ model_name: "Gemini 3.5 Flash-Lite"
45250
+ }
45251
+ ],
45252
+ embeds: [
45253
+ {
45254
+ embed_id: "8d96310e-a05a-41e8-a687-08ce2ce1ce9a",
45255
+ type: "app_skill_use",
45256
+ content: 'app_id: audio\nskill_id: generate\ntype: audio\nstatus: finished\nprompt: "Create a short, friendly success chime for an OpenMates workflow finishing"\ngeneration_type: sound_effect\nprovider: ElevenLabs\nmodel: eleven_text_to_sound_v2\nmime_type: audio/mpeg\nduration_seconds: 0.679\nbyte_length: 3531\npreviewAudioUrl: /store-examples/audio-generate-product-success-chime.mp3\nfiles:\n original:\n size_bytes: 3531\n format: mp3\n mime_type: audio/mpeg\n duration_seconds: 0.679\ngenerated_at: "2026-08-11T02:30:00.000000+00:00"\nwatermarking: Static public fixture',
45257
+ parent_embed_id: null,
45258
+ embed_ids: null
45259
+ }
45260
+ ],
45261
+ usage_entries: [
45262
+ {
45263
+ id: "example-audio-generate-product-success-chime-usage-1",
45264
+ type: "skill_execution",
45265
+ source: "chat",
45266
+ app_id: "ai",
45267
+ skill_id: "ask",
45268
+ model_used: "google/gemini-3.5-flash-lite",
45269
+ credits: 17,
45270
+ input_tokens: 18477,
45271
+ output_tokens: 107,
45272
+ user_input_tokens: 185,
45273
+ system_prompt_tokens: 8516,
45274
+ server_provider: "Google AI Studio",
45275
+ server_region: "US",
45276
+ chat_id: "example-audio-generate-product-success-chime",
45277
+ message_id: "0fe80324-42f5-4b87-9e11-0b0d8f36c9d5",
45278
+ created_at: 1786382147,
45279
+ updated_at: 1786382147,
45280
+ tool_inference_iterations: 1
45281
+ },
45282
+ {
45283
+ id: "example-audio-generate-product-success-chime-usage-2",
45284
+ type: "skill_execution",
45285
+ source: "chat",
45286
+ app_id: "audio",
45287
+ skill_id: "generate",
45288
+ model_used: "elevenlabs/eleven_text_to_sound_v2",
45289
+ credits: 14,
45290
+ server_provider: "ElevenLabs",
45291
+ server_region: "US",
45292
+ chat_id: "example-audio-generate-product-success-chime",
45293
+ message_id: "0fe80324-42f5-4b87-9e11-0b0d8f36c9d5",
45294
+ created_at: 1786382144,
45295
+ updated_at: 1786382144,
45296
+ tool_inference_iterations: 0,
45297
+ code_run_duration_seconds: 0.679
45298
+ }
45299
+ ],
45300
+ metadata: {
45301
+ featured: true,
45302
+ order: 123,
45303
+ app_skill_examples: ["audio.generate"]
45304
+ }
45305
+ };
45306
+
45307
+ // ../ui/src/demo_chats/data/example_chats/audio-speak-friendly-welcome-message.ts
45308
+ var audioSpeakFriendlyWelcomeMessageChat = {
45309
+ chat_id: "example-audio-speak-friendly-welcome-message",
45310
+ slug: "audio-speak-friendly-welcome-message",
45311
+ title: "example_chats.audio_speak_friendly_welcome_message.title",
45312
+ summary: "example_chats.audio_speak_friendly_welcome_message.summary",
45313
+ icon: "volume2",
45314
+ category: "design",
45315
+ keywords: ["text to speech", "ElevenLabs", "speech generation", "voice narration", "OpenMates apps"],
45316
+ follow_up_suggestions: [],
45317
+ messages: [
45318
+ {
45319
+ id: "b39cb42f-ec3e-45ec-9339-801b54c7cb03",
45320
+ role: "user",
45321
+ content: "example_chats.audio_speak_friendly_welcome_message.message_1",
45322
+ created_at: 1786382241
45323
+ },
45324
+ {
45325
+ id: "892c94f0-2c44-4d72-9c92-f27db186a103",
45326
+ role: "assistant",
45327
+ content: "example_chats.audio_speak_friendly_welcome_message.message_2",
45328
+ created_at: 1786382255,
45329
+ user_message_id: "b39cb42f-ec3e-45ec-9339-801b54c7cb03",
45330
+ response_credits: 28,
45331
+ category: "onboarding_support",
45332
+ model_name: "Gemini 3.5 Flash-Lite"
45333
+ }
45334
+ ],
45335
+ embeds: [
45336
+ {
45337
+ embed_id: "463ace0f-02f9-43c2-94ee-cf385162bb75",
45338
+ type: "app_skill_use",
45339
+ content: 'app_id: audio\nskill_id: speak\ntype: audio\nstatus: finished\nprompt: "Say this as a warm, natural welcome message: Welcome back to OpenMates. Your workspace is ready whenever you are."\ntext_preview: "Welcome back to OpenMates. Your workspace is ready whenever you are."\ntext: "Welcome back to OpenMates. Your workspace is ready whenever you are."\ngeneration_type: speech\nvoice: warm_neutral\nprovider: ElevenLabs\nmodel: eleven_flash_v2_5\nmime_type: audio/mpeg\nduration_seconds: 4.968\nbyte_length: 28413\npreviewAudioUrl: /store-examples/audio-speak-friendly-welcome-message.mp3\nfiles:\n original:\n size_bytes: 28413\n format: mp3\n mime_type: audio/mpeg\n duration_seconds: 4.968\ngenerated_at: "2026-08-11T02:30:00.000000+00:00"\nwatermarking: Static public fixture',
45340
+ parent_embed_id: null,
45341
+ embed_ids: null
45342
+ }
45343
+ ],
45344
+ usage_entries: [
45345
+ {
45346
+ id: "example-audio-speak-friendly-welcome-message-usage-1",
45347
+ type: "skill_execution",
45348
+ source: "chat",
45349
+ app_id: "ai",
45350
+ skill_id: "ask",
45351
+ model_used: "google/gemini-3.5-flash-lite",
45352
+ credits: 18,
45353
+ input_tokens: 19599,
45354
+ output_tokens: 69,
45355
+ user_input_tokens: 181,
45356
+ system_prompt_tokens: 9066,
45357
+ server_provider: "Google AI Studio",
45358
+ server_region: "US",
45359
+ chat_id: "example-audio-speak-friendly-welcome-message",
45360
+ message_id: "b39cb42f-ec3e-45ec-9339-801b54c7cb03",
45361
+ created_at: 1786382249,
45362
+ updated_at: 1786382249,
45363
+ tool_inference_iterations: 1
45364
+ },
45365
+ {
45366
+ id: "example-audio-speak-friendly-welcome-message-usage-2",
45367
+ type: "skill_execution",
45368
+ source: "chat",
45369
+ app_id: "audio",
45370
+ skill_id: "speak",
45371
+ model_used: "elevenlabs/eleven_flash_v2_5",
45372
+ credits: 10,
45373
+ server_provider: "ElevenLabs",
45374
+ server_region: "US",
45375
+ chat_id: "example-audio-speak-friendly-welcome-message",
45376
+ message_id: "b39cb42f-ec3e-45ec-9339-801b54c7cb03",
45377
+ created_at: 1786382246,
45378
+ updated_at: 1786382246,
45379
+ tool_inference_iterations: 0,
45380
+ code_run_duration_seconds: 4.968
45381
+ }
45382
+ ],
45383
+ metadata: {
45384
+ featured: true,
45385
+ order: 124,
45386
+ app_skill_examples: ["audio.speak"]
45387
+ }
45388
+ };
45389
+
45160
45390
  // ../ui/src/demo_chats/exampleChatData.ts
45161
45391
  var ALL_EXAMPLE_CHATS = [
45162
45392
  giganticAirplanesChat,
@@ -45260,9 +45490,425 @@ var ALL_EXAMPLE_CHATS = [
45260
45490
  berlinAiFounderMeetupsMapChat,
45261
45491
  deutschlandticketTrainFareBreakdownChat,
45262
45492
  berlinMitteWorkFriendlyRestaurantsChat,
45263
- openMeteoWeatherNotebookChat
45493
+ openMeteoWeatherNotebookChat,
45494
+ audioGenerateProductSuccessChimeChat,
45495
+ audioSpeakFriendlyWelcomeMessageChat
45264
45496
  ].sort((a, b) => a.metadata.order - b.metadata.order);
45265
45497
 
45498
+ // ../ui/src/demo_chats/exampleChatFiles.ts
45499
+ var FILE_LIKE_TYPES = /* @__PURE__ */ new Set([
45500
+ "audio",
45501
+ "audio-recording",
45502
+ "code",
45503
+ "code-code",
45504
+ "design",
45505
+ "diagram",
45506
+ "docs",
45507
+ "docs-doc",
45508
+ "document",
45509
+ "image",
45510
+ "images-image",
45511
+ "mind-map",
45512
+ "mind_map",
45513
+ "model3d",
45514
+ "music",
45515
+ "notebook",
45516
+ "pcb_schematic",
45517
+ "pdf",
45518
+ "recording",
45519
+ "remotion-video",
45520
+ "sheet",
45521
+ "sheets",
45522
+ "sheets-sheet",
45523
+ "spreadsheet",
45524
+ "svg",
45525
+ "video"
45526
+ ]);
45527
+ var URL_FIELDS = [
45528
+ "downloadUrl",
45529
+ "download_url",
45530
+ "file_url",
45531
+ "previewAudioUrl",
45532
+ "preview_audio_url",
45533
+ "previewImageUrl",
45534
+ "preview_image_url",
45535
+ "previewVideoUrl",
45536
+ "preview_video_url",
45537
+ "src",
45538
+ "url",
45539
+ "video_url"
45540
+ ];
45541
+ var AUDIO_URL_FIELDS = [
45542
+ "downloadUrl",
45543
+ "download_url",
45544
+ "file_url",
45545
+ "previewAudioUrl",
45546
+ "preview_audio_url",
45547
+ "src",
45548
+ "url"
45549
+ ];
45550
+ var VIDEO_URL_FIELDS = [
45551
+ "downloadUrl",
45552
+ "download_url",
45553
+ "file_url",
45554
+ "previewVideoUrl",
45555
+ "preview_video_url",
45556
+ "video_url",
45557
+ "src",
45558
+ "url",
45559
+ "previewImageUrl",
45560
+ "preview_image_url"
45561
+ ];
45562
+ var IMAGE_URL_FIELDS = [
45563
+ "downloadUrl",
45564
+ "download_url",
45565
+ "file_url",
45566
+ "previewImageUrl",
45567
+ "preview_image_url",
45568
+ "src",
45569
+ "url"
45570
+ ];
45571
+ var FILENAME_FIELDS = [
45572
+ "filename",
45573
+ "file_name",
45574
+ "file_path",
45575
+ "original_filename"
45576
+ ];
45577
+ var MIME_EXTENSION = {
45578
+ "application/pdf": "pdf",
45579
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
45580
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
45581
+ "audio/mpeg": "mp3",
45582
+ "audio/wav": "wav",
45583
+ "image/jpeg": "jpg",
45584
+ "image/png": "png",
45585
+ "image/svg+xml": "svg",
45586
+ "image/webp": "webp",
45587
+ "model/gltf-binary": "glb",
45588
+ "text/html": "html",
45589
+ "text/markdown": "md",
45590
+ "text/plain": "txt",
45591
+ "video/mp4": "mp4"
45592
+ };
45593
+ function isRecord2(value) {
45594
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45595
+ }
45596
+ function normalizeEmbedId(embedId) {
45597
+ return embedId.startsWith("embed:") ? embedId.slice("embed:".length) : embedId;
45598
+ }
45599
+ function cleanString(value) {
45600
+ if (typeof value !== "string") return "";
45601
+ const trimmed = stripQuotes(value.trim());
45602
+ if (!trimmed || trimmed === "null" || trimmed === "undefined") return "";
45603
+ return trimmed;
45604
+ }
45605
+ function stripQuotes(value) {
45606
+ const first = value[0];
45607
+ if ((first === '"' || first === "'") && value.endsWith(first)) {
45608
+ return value.slice(1, -1).replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\'/g, "'");
45609
+ }
45610
+ return value;
45611
+ }
45612
+ function numberValue2(value) {
45613
+ if (typeof value === "number" && Number.isFinite(value)) return value;
45614
+ if (typeof value !== "string" || value.trim() === "") return null;
45615
+ const parsed = Number(value);
45616
+ return Number.isFinite(parsed) ? parsed : null;
45617
+ }
45618
+ function parseFlatToonFields(content) {
45619
+ const fields = {};
45620
+ for (const line of content.split("\n")) {
45621
+ if (!line || /^\s/.test(line)) continue;
45622
+ const separatorIndex = line.indexOf(":");
45623
+ if (separatorIndex <= 0) continue;
45624
+ const key = line.slice(0, separatorIndex).trim();
45625
+ const value = line.slice(separatorIndex + 1).trim();
45626
+ if (!key || value === "") continue;
45627
+ fields[key] = stripQuotes(value);
45628
+ }
45629
+ return fields;
45630
+ }
45631
+ function decodeEmbedContent(content) {
45632
+ if (isRecord2(content)) return { ...content };
45633
+ if (typeof content !== "string" || !content.trim()) return {};
45634
+ const trimmed = content.trim();
45635
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
45636
+ try {
45637
+ const parsed = JSON.parse(trimmed);
45638
+ return isRecord2(parsed) ? parsed : {};
45639
+ } catch {
45640
+ return parseFlatToonFields(content);
45641
+ }
45642
+ }
45643
+ return parseFlatToonFields(content);
45644
+ }
45645
+ function stringField2(record, keys) {
45646
+ for (const key of keys) {
45647
+ const value = cleanString(record[key]);
45648
+ if (value) return value;
45649
+ }
45650
+ return "";
45651
+ }
45652
+ function basenameFromUrl(url) {
45653
+ if (url.startsWith("data:")) return "";
45654
+ const withoutQuery = url.split(/[?#]/)[0] || "";
45655
+ return withoutQuery.split("/").pop()?.replace(/[^a-zA-Z0-9._-]/g, "_") ?? "";
45656
+ }
45657
+ function extensionFromUrl(url) {
45658
+ const basename4 = basenameFromUrl(url);
45659
+ const dotIndex = basename4.lastIndexOf(".");
45660
+ return dotIndex > 0 ? basename4.slice(dotIndex + 1).toLowerCase() : "";
45661
+ }
45662
+ function extensionFromLanguage(language) {
45663
+ const normalized = language.toLowerCase();
45664
+ const map = {
45665
+ bash: "sh",
45666
+ css: "css",
45667
+ dockerfile: "Dockerfile",
45668
+ html: "html",
45669
+ javascript: "js",
45670
+ json: "json",
45671
+ markdown: "md",
45672
+ python: "py",
45673
+ rust: "rs",
45674
+ svelte: "svelte",
45675
+ text: "txt",
45676
+ typescript: "ts",
45677
+ yaml: "yml"
45678
+ };
45679
+ return (map[normalized] ?? normalized.replace(/[^a-z0-9]/g, "")) || "txt";
45680
+ }
45681
+ function nestedFormatFromToon(content) {
45682
+ if (typeof content !== "string") return "";
45683
+ const match = content.match(/\n\s+format:\s*([^\n]+)/);
45684
+ return cleanString(match?.[1]);
45685
+ }
45686
+ function urlForType(type, record) {
45687
+ if (type.includes("video") || type === "remotion-video") return stringField2(record, VIDEO_URL_FIELDS);
45688
+ if (type.includes("audio") || type === "music" || type === "recording") return stringField2(record, AUDIO_URL_FIELDS);
45689
+ if (type.includes("image") || type === "svg") return stringField2(record, IMAGE_URL_FIELDS);
45690
+ return stringField2(record, URL_FIELDS);
45691
+ }
45692
+ function inferFallbackExtension(type, record, content) {
45693
+ const mimeType = stringField2(record, ["mime_type", "file_type", "mimeType"]);
45694
+ if (mimeType && MIME_EXTENSION[mimeType]) return MIME_EXTENSION[mimeType];
45695
+ const urlExtension = extensionFromUrl(urlForType(type, record));
45696
+ if (urlExtension) return urlExtension;
45697
+ const nestedFormat = nestedFormatFromToon(content);
45698
+ if (nestedFormat) return nestedFormat;
45699
+ if (type.includes("video")) return "mp4";
45700
+ if (type.includes("audio") || type === "music" || type === "recording") return "mp3";
45701
+ if (type.includes("image")) return "png";
45702
+ if (type.includes("pdf")) return "pdf";
45703
+ if (type.includes("doc") || type === "document") return "docx";
45704
+ if (type.includes("sheet") || type === "spreadsheet") return "xlsx";
45705
+ if (type === "model3d") return "glb";
45706
+ if (type.includes("code") || type === "notebook" || type === "pcb_schematic") {
45707
+ return extensionFromLanguage(stringField2(record, ["language"]));
45708
+ }
45709
+ return "txt";
45710
+ }
45711
+ function inferTitle(embedId, type, record, content) {
45712
+ const url = urlForType(type, record);
45713
+ const urlBasename = basenameFromUrl(url);
45714
+ const filename = stringField2(record, FILENAME_FIELDS);
45715
+ if ((type.includes("video") || type === "music" || type === "audio") && urlBasename) {
45716
+ return urlBasename;
45717
+ }
45718
+ if (filename) return filename.replace(/[/\\]/g, "_");
45719
+ if (urlBasename) return urlBasename;
45720
+ const title = stringField2(record, ["title"]);
45721
+ if (title) {
45722
+ const extension2 = inferFallbackExtension(type, record, content);
45723
+ return `${title.replace(/[^a-zA-Z0-9._ -]/g, "_").trim()}.${extension2}`;
45724
+ }
45725
+ const extension = inferFallbackExtension(type, record, content);
45726
+ return `${type || "file"}-${embedId.slice(0, 8)}.${extension}`;
45727
+ }
45728
+ function inferNodeType(title, type) {
45729
+ const lowerTitle = title.toLowerCase();
45730
+ if (type.includes("pdf") || lowerTitle.endsWith(".pdf")) return "pdf";
45731
+ if (type.includes("image") || /\.(png|jpe?g|gif|webp|svg|avif|heic|heif)$/.test(lowerTitle)) return "image";
45732
+ if (type.includes("video") || /\.(mp4|mov|webm|m4v)$/.test(lowerTitle)) return "video";
45733
+ if (type.includes("audio") || type === "music" || type === "recording" || /\.(mp3|wav|m4a|ogg|webm)$/.test(lowerTitle)) return "recording";
45734
+ if (type.includes("sheet") || type === "spreadsheet" || /\.(csv|xlsx?|ods)$/.test(lowerTitle)) return "sheets-sheet";
45735
+ if (type.includes("doc") || type === "document" || /\.(docx?|odt|rtf)$/.test(lowerTitle)) return "docs-doc";
45736
+ if (type === "model3d" || /\.(glb|gltf|obj|stl)$/.test(lowerTitle)) return "model3d";
45737
+ if (type.includes("code") || type === "notebook" || type === "pcb_schematic") return "code-code";
45738
+ return "file";
45739
+ }
45740
+ function iconNameForNodeType(nodeType) {
45741
+ if (nodeType === "pdf") return "pdf";
45742
+ if (nodeType === "image") return "image";
45743
+ if (nodeType === "video") return "video";
45744
+ if (nodeType === "recording") return "audio";
45745
+ if (nodeType === "sheets-sheet") return "sheets";
45746
+ if (nodeType === "docs-doc") return "document";
45747
+ if (nodeType === "code-code") return "coding";
45748
+ if (nodeType === "model3d") return "3dmodels";
45749
+ return "files";
45750
+ }
45751
+ function labelForNodeType(nodeType, type) {
45752
+ if (nodeType === "pdf") return "PDF";
45753
+ if (nodeType === "image") return "Image";
45754
+ if (nodeType === "video") return "Video";
45755
+ if (nodeType === "recording") return type === "music" ? "Music" : "Audio";
45756
+ if (nodeType === "sheets-sheet") return "Sheet";
45757
+ if (nodeType === "docs-doc") return "Document";
45758
+ if (nodeType === "code-code") return type === "notebook" ? "Notebook" : "Code file";
45759
+ if (nodeType === "model3d") return "3D model";
45760
+ return "File";
45761
+ }
45762
+ function formatBytes(bytes) {
45763
+ if (!bytes || bytes <= 0) return "";
45764
+ if (bytes < 1024) return `${bytes} B`;
45765
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(bytes < 10 * 1024 ? 1 : 0)} KB`;
45766
+ return `${(bytes / (1024 * 1024)).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)} MB`;
45767
+ }
45768
+ function plural(value, singular, pluralLabel = `${singular}s`) {
45769
+ return `${value} ${value === 1 ? singular : pluralLabel}`;
45770
+ }
45771
+ function buildMetadata(nodeType, type, record) {
45772
+ const parts = [labelForNodeType(nodeType, type)];
45773
+ const lineCount = numberValue2(record.line_count ?? record.lineCount);
45774
+ const pageCount = numberValue2(record.page_count ?? record.pageCount);
45775
+ const wordCount = numberValue2(record.word_count ?? record.wordCount);
45776
+ const duration = numberValue2(record.duration_seconds ?? record.durationSeconds);
45777
+ const size = numberValue2(record.byte_length ?? record.size_bytes ?? record.sizeBytes);
45778
+ if (lineCount) parts.push(plural(lineCount, "line"));
45779
+ if (pageCount) parts.push(plural(pageCount, "page"));
45780
+ if (wordCount) parts.push(plural(wordCount, "word"));
45781
+ if (duration) parts.push(`${duration.toFixed(duration < 10 ? 1 : 0)}s`);
45782
+ const sizeLabel = formatBytes(size);
45783
+ if (sizeLabel) parts.push(sizeLabel);
45784
+ return parts.join(" | ");
45785
+ }
45786
+ function isSupportedPublicUrl(url) {
45787
+ return url.startsWith("/") || url.startsWith("data:") || /^https?:\/\//i.test(url);
45788
+ }
45789
+ function extractEmbedIdsFromContent(content) {
45790
+ const decoded = decodeEmbedContent(content);
45791
+ const rawIds = decoded.embed_ids ?? decoded.embedIds;
45792
+ const ids = typeof rawIds === "string" ? rawIds.split(/[|,]/) : rawIds;
45793
+ if (!Array.isArray(ids)) return [];
45794
+ return ids.map((id) => cleanString(id)).filter(Boolean).map(normalizeEmbedId);
45795
+ }
45796
+ function buildEmbedLookup(embeds) {
45797
+ const lookup = /* @__PURE__ */ new Map();
45798
+ for (const embed of embeds) {
45799
+ if (!embed.embed_id) continue;
45800
+ lookup.set(normalizeEmbedId(embed.embed_id), embed);
45801
+ }
45802
+ return lookup;
45803
+ }
45804
+ function buildEmbedRefLookup(embeds) {
45805
+ const lookup = /* @__PURE__ */ new Map();
45806
+ for (const embed of embeds) {
45807
+ const embedId = normalizeEmbedId(embed.embed_id);
45808
+ lookup.set(embedId, embedId);
45809
+ lookup.set(`embed:${embedId}`, embedId);
45810
+ const decoded = decodeEmbedContent(embed.content);
45811
+ const embedRef = stringField2(decoded, ["embed_ref", "embedRef"]);
45812
+ if (embedRef) {
45813
+ lookup.set(embedRef, embedId);
45814
+ lookup.set(`embed:${embedRef}`, embedId);
45815
+ }
45816
+ }
45817
+ return lookup;
45818
+ }
45819
+ function extractReferencedExampleEmbedIds(messages, embeds) {
45820
+ const refLookup = buildEmbedRefLookup(embeds);
45821
+ const ids = /* @__PURE__ */ new Set();
45822
+ const jsonBlockRe = /```(?:json_embed|json)\s*([\s\S]*?)\s*```/g;
45823
+ const inlineEmbedRe = /embed:([a-zA-Z0-9_.:-]+)/g;
45824
+ for (const message of messages) {
45825
+ const content = message.content ?? "";
45826
+ for (const match of Array.from(content.matchAll(jsonBlockRe))) {
45827
+ try {
45828
+ const parsed = JSON.parse(match[1].trim());
45829
+ const embedId = cleanString(parsed.embed_id ?? parsed.embedId);
45830
+ const resolved = refLookup.get(embedId) ?? refLookup.get(`embed:${embedId}`) ?? embedId;
45831
+ if (resolved) ids.add(normalizeEmbedId(resolved));
45832
+ } catch {
45833
+ }
45834
+ }
45835
+ for (const match of Array.from(content.matchAll(inlineEmbedRe))) {
45836
+ const rawRef = match[1];
45837
+ const resolved = refLookup.get(rawRef) ?? refLookup.get(`embed:${rawRef}`) ?? rawRef;
45838
+ if (resolved) ids.add(normalizeEmbedId(resolved));
45839
+ }
45840
+ }
45841
+ return Array.from(ids);
45842
+ }
45843
+ function expandEmbedIds(embedIds, embeds) {
45844
+ const lookup = buildEmbedLookup(embeds);
45845
+ const seen = /* @__PURE__ */ new Set();
45846
+ const queue = [...embedIds.map(normalizeEmbedId)];
45847
+ while (queue.length > 0) {
45848
+ const embedId = queue.shift();
45849
+ if (!embedId || seen.has(embedId)) continue;
45850
+ seen.add(embedId);
45851
+ const embed = lookup.get(embedId);
45852
+ if (!embed) continue;
45853
+ const childIds = [
45854
+ ...embed.embed_ids ?? [],
45855
+ ...extractEmbedIdsFromContent(embed.content)
45856
+ ];
45857
+ for (const childId of childIds) {
45858
+ const normalizedChildId = normalizeEmbedId(childId);
45859
+ if (normalizedChildId && !seen.has(normalizedChildId)) queue.push(normalizedChildId);
45860
+ }
45861
+ }
45862
+ return Array.from(seen);
45863
+ }
45864
+ function getEmbedFileReferences(embed) {
45865
+ const embedId = cleanString(embed.embed_id ?? embed.embedId ?? embed.id);
45866
+ if (!embedId) return [];
45867
+ const decoded = decodeEmbedContent(embed.content);
45868
+ const type = (stringField2(decoded, ["type", "embed_type", "embedType"]) || cleanString(embed.type) || "file").toLowerCase();
45869
+ if (!FILE_LIKE_TYPES.has(type)) return [];
45870
+ const title = inferTitle(embedId, type, decoded, embed.content);
45871
+ const nodeType = inferNodeType(title, type);
45872
+ const url = urlForType(type, decoded);
45873
+ const mimeType = stringField2(decoded, ["mime_type", "file_type", "mimeType"]);
45874
+ const createdAt = numberValue2(embed.created_at ?? embed.createdAt) ?? 0;
45875
+ return [{
45876
+ embedId: normalizeEmbedId(embedId),
45877
+ contentRef: `embed:${normalizeEmbedId(embedId)}`,
45878
+ title,
45879
+ subtitle: labelForNodeType(nodeType, type),
45880
+ type,
45881
+ nodeType,
45882
+ iconName: iconNameForNodeType(nodeType),
45883
+ createdAt,
45884
+ updatedAt: createdAt,
45885
+ metadata: buildMetadata(nodeType, type, decoded),
45886
+ appId: stringField2(decoded, ["app_id", "appId"]) || cleanString(embed.app_id ?? embed.appId) || null,
45887
+ skillId: stringField2(decoded, ["skill_id", "skillId"]) || cleanString(embed.skill_id ?? embed.skillId) || null,
45888
+ url: url && isSupportedPublicUrl(url) ? url : null,
45889
+ mimeType: mimeType || null
45890
+ }];
45891
+ }
45892
+ function collectExampleChatFileReferences(embeds, messages = []) {
45893
+ if (embeds.length === 0) return [];
45894
+ const lookup = buildEmbedLookup(embeds);
45895
+ const referencedIds = extractReferencedExampleEmbedIds(messages, embeds);
45896
+ const candidateIds = referencedIds.length > 0 ? expandEmbedIds(referencedIds, embeds) : embeds.map((embed) => normalizeEmbedId(embed.embed_id));
45897
+ const rows = [];
45898
+ const seen = /* @__PURE__ */ new Set();
45899
+ for (const embedId of candidateIds) {
45900
+ const embed = lookup.get(embedId);
45901
+ if (!embed) continue;
45902
+ for (const row of getEmbedFileReferences(embed)) {
45903
+ const key = `${row.embedId}:${row.title}`.toLowerCase();
45904
+ if (seen.has(key)) continue;
45905
+ rows.push(row);
45906
+ seen.add(key);
45907
+ }
45908
+ }
45909
+ return rows;
45910
+ }
45911
+
45266
45912
  // ../ui/src/i18n/locales/en.json
45267
45913
  var en_default = {
45268
45914
  activity: {
@@ -53200,6 +53846,34 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
53200
53846
  text: "[images-search] Show me more photos of the Orion spacecraft"
53201
53847
  }
53202
53848
  },
53849
+ audio_generate_product_success_chime: {
53850
+ title: {
53851
+ text: "Audio Generate Product Success Chime"
53852
+ },
53853
+ summary: {
53854
+ text: "Create a short product success chime with the Audio Generate app skill."
53855
+ },
53856
+ message_1: {
53857
+ text: "Create a short, friendly success chime for an OpenMates workflow finishing. Keep it clean, modern, and subtle enough for a product notification."
53858
+ },
53859
+ message_2: {
53860
+ text: '```json\n{"type":"app_skill_use","embed_id":"8d96310e-a05a-41e8-a687-08ce2ce1ce9a","app_id":"audio","skill_id":"generate"}\n```\n\nI created a short, friendly product success chime you can use as a subtle workflow-complete notification.'
53861
+ }
53862
+ },
53863
+ audio_speak_friendly_welcome_message: {
53864
+ title: {
53865
+ text: "Audio Speak Friendly Welcome Message"
53866
+ },
53867
+ summary: {
53868
+ text: "Turn a short welcome line into a friendly spoken message with the Audio Speak app skill."
53869
+ },
53870
+ message_1: {
53871
+ text: "Say this as a warm, natural welcome message: Welcome back to OpenMates. Your workspace is ready whenever you are."
53872
+ },
53873
+ message_2: {
53874
+ text: '```json\n{"type":"app_skill_use","embed_id":"463ace0f-02f9-43c2-94ee-cf385162bb75","app_id":"audio","skill_id":"speak"}\n```\n\nI generated the welcome line as a friendly spoken audio message.'
53875
+ }
53876
+ },
53203
53877
  audio_transcribe_voice_note: {
53204
53878
  title: {
53205
53879
  text: "Transcribe a voice note"
@@ -66102,8 +66776,10 @@ function getExampleChatConversation(query) {
66102
66776
  category: message.category ?? chat.category,
66103
66777
  modelName: message.model_name ?? null,
66104
66778
  createdAt: message.created_at,
66105
- embedIds: []
66779
+ embedIds: extractReferencedExampleEmbedIds([message], chat.embeds)
66106
66780
  })),
66781
+ embeds: chat.embeds,
66782
+ files: collectExampleChatFileReferences(chat.embeds, chat.messages),
66107
66783
  followUpSuggestions: chat.follow_up_suggestions.map(translate)
66108
66784
  };
66109
66785
  }
@@ -69996,7 +70672,7 @@ async function requestProjectRemoteOperation(options) {
69996
70672
  throw new ProjectRequesterError("source_offline", "The selected Project source is offline.");
69997
70673
  }
69998
70674
  const timeoutMs = options.timeoutMs ?? REMOTE_PROTOCOL_TIMEOUT_MS;
69999
- let sourceSessionId = stringField2(options.source, "source_session_id");
70675
+ let sourceSessionId = stringField3(options.source, "source_session_id");
70000
70676
  let keyEpoch = numberField(options.source, "key_epoch");
70001
70677
  let routingIdentity = null;
70002
70678
  if (!sourceSessionId || !keyEpoch) {
@@ -70148,7 +70824,7 @@ function parseJsonObject(value) {
70148
70824
  }
70149
70825
  throw new ProjectRequesterError("invalid_remote_payload", "The Project source returned an invalid payload.");
70150
70826
  }
70151
- function stringField2(value, key) {
70827
+ function stringField3(value, key) {
70152
70828
  return typeof value[key] === "string" && value[key] ? value[key] : null;
70153
70829
  }
70154
70830
  function numberField(value, key) {
@@ -71081,7 +71757,7 @@ function splitTaskAskInstruction(instruction) {
71081
71757
  const suffix = instruction.includes(":") ? instruction.split(":").slice(1).join(":") : instruction;
71082
71758
  return suffix.split(/[,;]\s*/).map((title) => title.trim()).filter(Boolean).map((title) => ({ title }));
71083
71759
  }
71084
- function isRecord2(value) {
71760
+ function isRecord3(value) {
71085
71761
  return typeof value === "object" && value !== null && !Array.isArray(value);
71086
71762
  }
71087
71763
  function isPrivateCliOutputKey(key) {
@@ -71089,7 +71765,7 @@ function isPrivateCliOutputKey(key) {
71089
71765
  }
71090
71766
  function sanitizeWorkspaceAskOutput(value) {
71091
71767
  if (Array.isArray(value)) return value.map((item) => sanitizeWorkspaceAskOutput(item));
71092
- if (!isRecord2(value)) return value;
71768
+ if (!isRecord3(value)) return value;
71093
71769
  const sanitized = {};
71094
71770
  for (const [key, nested] of Object.entries(value)) {
71095
71771
  if (isPrivateCliOutputKey(key)) continue;
@@ -71099,20 +71775,20 @@ function sanitizeWorkspaceAskOutput(value) {
71099
71775
  }
71100
71776
  async function prepareTaskAskOutput(result, masterKey) {
71101
71777
  const output = sanitizeWorkspaceAskOutput(result);
71102
- if (isRecord2(result.task)) {
71778
+ if (isRecord3(result.task)) {
71103
71779
  output.task = taskToJson(await decryptUserTask(result.task, masterKey));
71104
71780
  }
71105
- if (Array.isArray(result.tasks) && result.tasks.every(isRecord2)) {
71781
+ if (Array.isArray(result.tasks) && result.tasks.every(isRecord3)) {
71106
71782
  output.tasks = (await decryptUserTasks(result.tasks, masterKey)).map(taskToJson);
71107
71783
  }
71108
71784
  return output;
71109
71785
  }
71110
71786
  async function preparePlanAskOutput(result, masterKey) {
71111
71787
  const output = sanitizeWorkspaceAskOutput(result);
71112
- if (isRecord2(result.plan)) {
71788
+ if (isRecord3(result.plan)) {
71113
71789
  output.plan = planToJson(await decryptUserPlan(result.plan, masterKey));
71114
71790
  }
71115
- if (Array.isArray(result.plans) && result.plans.every(isRecord2)) {
71791
+ if (Array.isArray(result.plans) && result.plans.every(isRecord3)) {
71116
71792
  output.plans = (await decryptUserPlans(result.plans, masterKey)).map(planToJson);
71117
71793
  }
71118
71794
  return output;
@@ -73426,6 +74102,28 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
73426
74102
  }
73427
74103
  return;
73428
74104
  }
74105
+ if (subcommand === "files") {
74106
+ const chatId = rest[0];
74107
+ if (!chatId) {
74108
+ throw new Error("Missing chat ID. Usage: openmates chats files <chat-id> [--json]");
74109
+ }
74110
+ const resolvedId = chatId.toLowerCase() === "last" ? "__last__" : chatId;
74111
+ if (apiKey) {
74112
+ throw new Error("Chat file listing through --api-key is not supported; use an authenticated CLI session so encrypted chat embeds can be decrypted locally.");
74113
+ }
74114
+ if (!client.hasSession()) {
74115
+ const example = resolveExampleChatForOpen(resolvedId === "__last__" ? "1" : resolvedId);
74116
+ if (!example) {
74117
+ throw new Error(`Example chat '${chatId}' not found. Run 'openmates chats list' to see available examples.`);
74118
+ }
74119
+ printChatFiles(example.chat, example.files, flags, { example: true });
74120
+ return;
74121
+ }
74122
+ const { chat, messages } = await client.getChatMessages(resolvedId, teamContext);
74123
+ const result = await loadChatFileReferencesForCli(client, messages);
74124
+ printChatFiles(chat, result.files, flags, { errors: result.errors });
74125
+ return;
74126
+ }
73429
74127
  if (subcommand === "messages") {
73430
74128
  const chatId = rest[0];
73431
74129
  if (!chatId) {
@@ -74402,6 +75100,113 @@ function resolveExampleChatForOpen(target) {
74402
75100
  }
74403
75101
  return getExampleChatConversation(target);
74404
75102
  }
75103
+ function collectMessageEmbedIdsForFiles(messages) {
75104
+ const ids = /* @__PURE__ */ new Set();
75105
+ const inlineEmbedRefRe = /embed:([a-zA-Z0-9_.:-]+)/g;
75106
+ for (const message of messages) {
75107
+ for (const embedId of message.embedIds ?? []) {
75108
+ if (embedId) ids.add(embedId);
75109
+ }
75110
+ for (const match of Array.from((message.content ?? "").matchAll(inlineEmbedRefRe))) {
75111
+ if (match[1]) ids.add(match[1]);
75112
+ }
75113
+ for (const segment of parseMessageSegments(message.content ?? "")) {
75114
+ if (segment.type === "embed" && segment.value) ids.add(segment.value);
75115
+ }
75116
+ }
75117
+ return Array.from(ids);
75118
+ }
75119
+ function fileReferenceKey(file) {
75120
+ return `${file.embedId}:${file.title}`.toLowerCase();
75121
+ }
75122
+ function chatFileReferenceFromEmbed(embed) {
75123
+ return getEmbedFileReferences({
75124
+ embedId: embed.embedId,
75125
+ type: embed.type,
75126
+ content: embed.content,
75127
+ appId: embed.appId,
75128
+ skillId: embed.skillId,
75129
+ createdAt: embed.createdAt
75130
+ });
75131
+ }
75132
+ async function loadChatFileReferencesForCli(client, messages) {
75133
+ const queue = collectMessageEmbedIdsForFiles(messages);
75134
+ const visitedEmbeds = /* @__PURE__ */ new Set();
75135
+ const seenFiles = /* @__PURE__ */ new Set();
75136
+ const files = [];
75137
+ const errors = [];
75138
+ while (queue.length > 0) {
75139
+ const embedId = queue.shift();
75140
+ if (!embedId || visitedEmbeds.has(embedId)) continue;
75141
+ visitedEmbeds.add(embedId);
75142
+ try {
75143
+ const embed = await client.getEmbed(embedId);
75144
+ for (const file of chatFileReferenceFromEmbed(embed)) {
75145
+ const key = fileReferenceKey(file);
75146
+ if (seenFiles.has(key)) continue;
75147
+ files.push(file);
75148
+ seenFiles.add(key);
75149
+ }
75150
+ for (const childId of extractEmbedIdsFromContent(embed.content)) {
75151
+ if (childId && !visitedEmbeds.has(childId)) queue.push(childId);
75152
+ }
75153
+ } catch (error) {
75154
+ errors.push({
75155
+ embed_id: embedId,
75156
+ message: error instanceof Error ? error.message : String(error)
75157
+ });
75158
+ }
75159
+ }
75160
+ return { files, errors };
75161
+ }
75162
+ function chatFileToJson(file) {
75163
+ return {
75164
+ embed_id: file.embedId,
75165
+ content_ref: file.contentRef,
75166
+ title: file.title,
75167
+ type: file.type,
75168
+ node_type: file.nodeType,
75169
+ metadata: file.metadata,
75170
+ app_id: file.appId,
75171
+ skill_id: file.skillId,
75172
+ url: file.url,
75173
+ mime_type: file.mimeType
75174
+ };
75175
+ }
75176
+ function printChatFiles(chat, files, flags, options = {}) {
75177
+ if (flags.json === true) {
75178
+ printJson2({
75179
+ chat_id: chat.id,
75180
+ title: chat.title,
75181
+ source: options.example ? "example" : "chat",
75182
+ count: files.length,
75183
+ files: files.map(chatFileToJson),
75184
+ errors: options.errors ?? []
75185
+ });
75186
+ return;
75187
+ }
75188
+ const title = chat.title ?? chat.shortId ?? chat.id;
75189
+ header(`Files for ${title}`);
75190
+ if (options.example) {
75191
+ process.stdout.write("\x1B[2mPublic example chat files are resolved from bundled static embed metadata.\x1B[0m\n");
75192
+ }
75193
+ if (files.length === 0) {
75194
+ process.stdout.write("\x1B[2mNo files found for this chat.\x1B[0m\n");
75195
+ } else {
75196
+ for (const file of files) {
75197
+ const appSkill = [file.appId, file.skillId].filter(Boolean).join("/");
75198
+ const suffix = appSkill ? ` \x1B[2m${appSkill}\x1B[0m` : "";
75199
+ process.stdout.write(` \x1B[1m${file.title}\x1B[0m \x1B[2m${file.metadata}\x1B[0m${suffix}
75200
+ `);
75201
+ process.stdout.write(` \x1B[2m${file.contentRef}${file.url ? ` ${file.url}` : ""}\x1B[0m
75202
+ `);
75203
+ }
75204
+ }
75205
+ for (const error of options.errors ?? []) {
75206
+ process.stderr.write(`Warning: could not inspect embed ${error.embed_id}: ${error.message}
75207
+ `);
75208
+ }
75209
+ }
74405
75210
  async function openUrl(url) {
74406
75211
  const { exec: exec2 } = await import("child_process");
74407
75212
  const platform4 = process.platform;
@@ -80436,6 +81241,7 @@ function printChatsHelp() {
80436
81241
  openmates chats list [--limit <n>] [--page <n>] [--json]
80437
81242
  openmates chats show <chat-id> [--raw] [--json] [--all]
80438
81243
  openmates chats messages <chat-id> [--json]
81244
+ openmates chats files <chat-id> [--json]
80439
81245
  openmates chats <chat-id> add-to-project <project-id> [--folder <folder-id>] [--openmates-only|--repo-copy|--remote-cache-copy|--remote-copy] [--json]
80440
81246
  openmates chats <chat-id> remove-from-project <project-id> [--json]
80441
81247
  openmates chats fork <chat-id> --from-message <message-id> [--title <title>] [--json]
@@ -80463,6 +81269,11 @@ Options for 'show':
80463
81269
  --raw Show raw decrypted message content without rendering embeds
80464
81270
  or cleaning embed references. Useful for debugging.
80465
81271
 
81272
+ Options for 'files':
81273
+ Lists uploaded and generated files connected to a saved or public example chat.
81274
+ Saved chats require an authenticated CLI session so encrypted embeds can be
81275
+ decrypted locally. Public examples also work while logged out.
81276
+
80466
81277
  Options for 'fork', 'rewind', and 'retry':
80467
81278
  --from-message <id> Message boundary for non-destructive fork.
80468
81279
  --to-message <id> Message boundary to keep when rewinding.
@@ -80540,6 +81351,8 @@ Examples:
80540
81351
  openmates chats open gigantic-airplanes-transporting-rocket-parts
80541
81352
  openmates chats show d262cb68
80542
81353
  openmates chats messages d262cb68
81354
+ openmates chats files d262cb68
81355
+ openmates chats files example-audio-speak-friendly-welcome-message --json
80543
81356
  openmates chats fork d262cb68 --from-message 8f4e2a1c --title "Clean branch"
80544
81357
  openmates chats rewind d262cb68 --to-message 8f4e2a1c --send "Continue from here" --yes
80545
81358
  openmates chats retry d262cb68 --yes
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  requireExactConfirmation,
9
9
  resolveProjectContext,
10
10
  serializeToYaml
11
- } from "./chunk-SVL7BFYD.js";
11
+ } from "./chunk-FMYIOUSI.js";
12
12
  import "./chunk-IWKP55ZB.js";
13
13
  import "./chunk-MLWBMJEN.js";
14
14
  export {
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  renderPlanList,
40
40
  renderSupportInfo,
41
41
  serializeToYaml
42
- } from "./chunk-SVL7BFYD.js";
42
+ } from "./chunk-FMYIOUSI.js";
43
43
  import "./chunk-IWKP55ZB.js";
44
44
  import "./chunk-MLWBMJEN.js";
45
45
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmates",
3
- "version": "0.16.0-alpha.0",
3
+ "version": "0.16.0-alpha.10",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,8 @@
1
+ # backend/core/docker-compose.no-webapp.yml
2
+ #
3
+ # Keeps official OpenMatesCloud core runtimes backend-only while regular
4
+ # self-host installs continue to start the webapp from the base compose file.
5
+
6
+ services:
7
+ webapp:
8
+ profiles: ["webapp"]
@@ -113,7 +113,8 @@ services:
113
113
  test: ["CMD", "/usr/local/bin/openmates-directus-health"]
114
114
  interval: 10s
115
115
  timeout: 8s
116
- start_period: 60s
116
+ # cms-setup creates the indexes validated by this health script; cold CI runners can take several minutes.
117
+ start_period: 10m
117
118
  retries: 12
118
119
 
119
120
  cms-database:
@@ -225,12 +226,25 @@ services:
225
226
  mem_limit: ${CORE_WORKER_MEMORY_LIMIT:-3g}
226
227
  environment:
227
228
  <<: *openmates-worker-env
228
- CELERY_QUEUES: persistence,health_check,server_stats,demo,e2e_tests,reminder,push
229
+ CELERY_QUEUES: persistence,health_check,server_stats,demo,e2e_tests,push
229
230
  CELERY_AUTOSCALE_MAX: ${CORE_WORKER_CONCURRENCY:-${CELERY_AUTOSCALE_MAX:-3}}
230
231
  CELERY_AUTOSCALE_MIN: ${CELERY_AUTOSCALE_MIN:-1}
231
232
  CELERY_METRICS_PORT: "9109"
232
233
  command: >
233
- sh -c "chown -R celeryuser:celeryuser /vault-data && gosu celeryuser python -m celery -A backend.core.api.app.tasks.celery_config worker --loglevel=info --queues=persistence,health_check,server_stats,demo,e2e_tests,reminder,push --concurrency=$${CELERY_AUTOSCALE_MAX} --max-tasks-per-child=50 --max-memory-per-child=600000 --prefetch-multiplier=1"
234
+ sh -c "chown -R celeryuser:celeryuser /vault-data && gosu celeryuser python -m celery -A backend.core.api.app.tasks.celery_config worker --loglevel=info --queues=persistence,health_check,server_stats,demo,e2e_tests,push --concurrency=$${CELERY_AUTOSCALE_MAX} --max-tasks-per-child=50 --max-memory-per-child=600000 --prefetch-multiplier=1"
235
+
236
+ reminder-worker:
237
+ <<: *openmates-worker-base
238
+ container_name: reminder-worker
239
+ mem_limit: ${REMINDER_WORKER_MEMORY_LIMIT:-1536m}
240
+ environment:
241
+ <<: *openmates-worker-env
242
+ CELERY_QUEUES: reminder
243
+ CELERY_AUTOSCALE_MAX: ${REMINDER_WORKER_CONCURRENCY:-2}
244
+ CELERY_AUTOSCALE_MIN: ${CELERY_AUTOSCALE_MIN:-1}
245
+ CELERY_METRICS_PORT: "9111"
246
+ command: >
247
+ sh -c "chown -R celeryuser:celeryuser /vault-data && gosu celeryuser python -m celery -A backend.core.api.app.tasks.celery_config worker --loglevel=info --queues=reminder --concurrency=$${CELERY_AUTOSCALE_MAX} --max-tasks-per-child=50 --max-memory-per-child=600000 --prefetch-multiplier=1"
234
248
 
235
249
  workflow-worker:
236
250
  <<: *openmates-worker-base