openmates 0.16.0-alpha.1 → 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) {
@@ -25475,6 +25486,7 @@ var ROLE_TEMPLATE_FILES = {
25475
25486
  upload: join6("upload", "docker-compose.yml"),
25476
25487
  preview: join6("preview", "docker-compose.preview.yml")
25477
25488
  };
25489
+ var CORE_NO_WEBAPP_TEMPLATE_FILE = join6("core", "docker-compose.no-webapp.yml");
25478
25490
  var CORE_PROMTAIL_CONFIG_FILE = join6("backend", "core", "monitoring", "promtail", "promtail-config.yaml");
25479
25491
  var COMPOSE_OVERRIDE2 = join6("backend", "core", "docker-compose.override.yml");
25480
25492
  var DEFAULT_INSTALL_PATH = join6(homedir6(), "openmates");
@@ -25726,6 +25738,12 @@ function getServerDeploymentMode(flags, config) {
25726
25738
  if (typeof flags["deployment-mode"] === "string") return normalizeServerDeploymentMode(flags["deployment-mode"]);
25727
25739
  return normalizeServerDeploymentMode(config?.deploymentMode);
25728
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
+ }
25729
25747
  function getOpenMatesCloudOverlayPath(flags, config) {
25730
25748
  const explicit = flags["openmatescloud-path"] ?? flags["openmates-cloud-path"];
25731
25749
  if (explicit === true) throw new Error("Provide an OpenMatesCloud path: --openmatescloud-path <dir>.");
@@ -25804,9 +25822,10 @@ function featureKind(featureId) {
25804
25822
  return featureId.split(":", 1)[0] || "unknown";
25805
25823
  }
25806
25824
  function composeArgs(installPath, withOverrides, installMode = getInstallMode(installPath), role = "core") {
25807
- const env = readEnvMap(installPath);
25808
25825
  const config = loadConfigForInstallPath(installPath);
25809
- 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);
25810
25829
  const overlayPath = env.OPENMATES_CLOUD_OVERLAY_PATH || config?.openMatesCloudOverlayPath || void 0;
25811
25830
  const resolvedOverlayPath = overlayPath ?? defaultOpenMatesCloudOverlayPath(installPath);
25812
25831
  const overlayComposeFile = defaultOpenMatesCloudComposeFile(resolvedOverlayPath);
@@ -25950,9 +25969,34 @@ async function fetchText(url) {
25950
25969
  function packagedTemplatePath(role) {
25951
25970
  return join6(dirname5(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
25952
25971
  }
25972
+ function packagedNoWebappTemplatePath() {
25973
+ return join6(dirname5(new URL(import.meta.url).pathname), "..", "templates", CORE_NO_WEBAPP_TEMPLATE_FILE);
25974
+ }
25953
25975
  function packagedCaddyTemplatePath(role) {
25954
25976
  return join6(dirname5(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
25955
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
+ }
25956
26000
  function fileHash(path2) {
25957
26001
  if (!existsSync8(path2)) return null;
25958
26002
  return createHash12("sha256").update(readFileSync6(path2)).digest("hex");
@@ -25976,6 +26020,9 @@ async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
25976
26020
  mkdirSync5(vaultConfigDir, { recursive: true });
25977
26021
  mkdirSync5(join6(installPath, "config", "providers"), { recursive: true });
25978
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
+ }
25979
26026
  if (role === "core") {
25980
26027
  const promtailConfigPath = join6(installPath, CORE_PROMTAIL_CONFIG_FILE);
25981
26028
  mkdirSync5(dirname5(promtailConfigPath), { recursive: true });
@@ -26711,6 +26758,7 @@ async function serverStart(flags) {
26711
26758
  const config = loadConfigForInstallPath(installPath);
26712
26759
  const role = getServerRole(flags, config);
26713
26760
  const installMode = getInstallMode(installPath, config);
26761
+ const deploymentMode = getInstallDeploymentMode(installPath, config);
26714
26762
  const pullArgs = [...composeArgs(installPath, withOverrides, installMode, role), "pull"];
26715
26763
  const args = [...composeArgs(installPath, withOverrides, installMode, role), "up", "-d"];
26716
26764
  if (hasServiceFilter(flags)) {
@@ -26733,8 +26781,12 @@ async function serverStart(flags) {
26733
26781
  printJson({ command: "start", status: "success", path: installPath });
26734
26782
  } else {
26735
26783
  console.log("\nServer started.");
26736
- console.log("Web app: http://localhost:5173");
26737
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
+ }
26738
26790
  if (withOverrides) {
26739
26791
  console.log("Directus CMS: http://localhost:8055");
26740
26792
  console.log("Grafana: http://localhost:3000");
@@ -27160,6 +27212,7 @@ async function serverUpdate(rest, flags) {
27160
27212
  const role = getServerRole(flags, config);
27161
27213
  const withOverrides = config?.composeProfile === "full";
27162
27214
  const installMode = getInstallMode(installPath, config);
27215
+ const deploymentMode = getInstallDeploymentMode(installPath, config);
27163
27216
  const filterRequested = hasServiceFilter(flags);
27164
27217
  const selectedServices = filterRequested ? selectedComposeServices(role, flags) : [];
27165
27218
  const missingEnvKeys = missingRequiredEnvKeys(installPath, role);
@@ -27272,7 +27325,7 @@ async function serverUpdate(rest, flags) {
27272
27325
  try {
27273
27326
  writeUpdateStatus(installPath, role, { status: "in_progress", targetImageTag: target.tag, sourceLinks, providerKeyReminders: secretPreflight.emptySecretEnvKeys, step: "health-check" });
27274
27327
  await waitForServerHealth(installPath, role, {
27275
- checkWebApp: shouldCheckWebHealth({ role, selectedServices, filterRequested })
27328
+ checkWebApp: shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested })
27276
27329
  });
27277
27330
  const runtimeOutput = runRuntimeVerification(installPath, role, config);
27278
27331
  await persistRuntimeResult(installPath, role, runtimeOutput);
@@ -27342,14 +27395,14 @@ async function serverUpdate(rest, flags) {
27342
27395
  path: installPath,
27343
27396
  mode: "source",
27344
27397
  selectedServices: filterRequested ? selectedServices : "all",
27345
- checkWebApp: shouldCheckWebHealth({ role, selectedServices, filterRequested }),
27398
+ checkWebApp: shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested }),
27346
27399
  dryRun: true
27347
27400
  });
27348
27401
  } else {
27349
27402
  console.log("Update plan:");
27350
27403
  console.log(" Mode: source");
27351
27404
  console.log(` Services: ${filterRequested ? selectedServices.join(", ") : "all"}`);
27352
- console.log(` Web health: ${shouldCheckWebHealth({ role, selectedServices, filterRequested }) ? "enabled" : "skipped"}`);
27405
+ console.log(` Web health: ${shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested }) ? "enabled" : "skipped"}`);
27353
27406
  console.log(" Commands: git pull --ff-only, docker compose build, docker compose up -d, health checks");
27354
27407
  }
27355
27408
  return;
@@ -27397,7 +27450,7 @@ async function serverUpdate(rest, flags) {
27397
27450
  );
27398
27451
  code = await runInteractive("docker", upArgs, installPath);
27399
27452
  if (code !== 0) process.exit(code);
27400
- const checkWebApp = shouldCheckWebHealth({ role, selectedServices, filterRequested });
27453
+ const checkWebApp = shouldCheckWebHealth({ role, deploymentMode, selectedServices, filterRequested });
27401
27454
  console.error(checkWebApp ? "Waiting for API and web health checks..." : "Waiting for API health checks...");
27402
27455
  let successfulRuntimeOutput = null;
27403
27456
  try {
@@ -45168,6 +45221,172 @@ notebook:
45168
45221
  }
45169
45222
  };
45170
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
+
45171
45390
  // ../ui/src/demo_chats/exampleChatData.ts
45172
45391
  var ALL_EXAMPLE_CHATS = [
45173
45392
  giganticAirplanesChat,
@@ -45271,9 +45490,425 @@ var ALL_EXAMPLE_CHATS = [
45271
45490
  berlinAiFounderMeetupsMapChat,
45272
45491
  deutschlandticketTrainFareBreakdownChat,
45273
45492
  berlinMitteWorkFriendlyRestaurantsChat,
45274
- openMeteoWeatherNotebookChat
45493
+ openMeteoWeatherNotebookChat,
45494
+ audioGenerateProductSuccessChimeChat,
45495
+ audioSpeakFriendlyWelcomeMessageChat
45275
45496
  ].sort((a, b) => a.metadata.order - b.metadata.order);
45276
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
+
45277
45912
  // ../ui/src/i18n/locales/en.json
45278
45913
  var en_default = {
45279
45914
  activity: {
@@ -53211,6 +53846,34 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
53211
53846
  text: "[images-search] Show me more photos of the Orion spacecraft"
53212
53847
  }
53213
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
+ },
53214
53877
  audio_transcribe_voice_note: {
53215
53878
  title: {
53216
53879
  text: "Transcribe a voice note"
@@ -66113,8 +66776,10 @@ function getExampleChatConversation(query) {
66113
66776
  category: message.category ?? chat.category,
66114
66777
  modelName: message.model_name ?? null,
66115
66778
  createdAt: message.created_at,
66116
- embedIds: []
66779
+ embedIds: extractReferencedExampleEmbedIds([message], chat.embeds)
66117
66780
  })),
66781
+ embeds: chat.embeds,
66782
+ files: collectExampleChatFileReferences(chat.embeds, chat.messages),
66118
66783
  followUpSuggestions: chat.follow_up_suggestions.map(translate)
66119
66784
  };
66120
66785
  }
@@ -70007,7 +70672,7 @@ async function requestProjectRemoteOperation(options) {
70007
70672
  throw new ProjectRequesterError("source_offline", "The selected Project source is offline.");
70008
70673
  }
70009
70674
  const timeoutMs = options.timeoutMs ?? REMOTE_PROTOCOL_TIMEOUT_MS;
70010
- let sourceSessionId = stringField2(options.source, "source_session_id");
70675
+ let sourceSessionId = stringField3(options.source, "source_session_id");
70011
70676
  let keyEpoch = numberField(options.source, "key_epoch");
70012
70677
  let routingIdentity = null;
70013
70678
  if (!sourceSessionId || !keyEpoch) {
@@ -70159,7 +70824,7 @@ function parseJsonObject(value) {
70159
70824
  }
70160
70825
  throw new ProjectRequesterError("invalid_remote_payload", "The Project source returned an invalid payload.");
70161
70826
  }
70162
- function stringField2(value, key) {
70827
+ function stringField3(value, key) {
70163
70828
  return typeof value[key] === "string" && value[key] ? value[key] : null;
70164
70829
  }
70165
70830
  function numberField(value, key) {
@@ -71092,7 +71757,7 @@ function splitTaskAskInstruction(instruction) {
71092
71757
  const suffix = instruction.includes(":") ? instruction.split(":").slice(1).join(":") : instruction;
71093
71758
  return suffix.split(/[,;]\s*/).map((title) => title.trim()).filter(Boolean).map((title) => ({ title }));
71094
71759
  }
71095
- function isRecord2(value) {
71760
+ function isRecord3(value) {
71096
71761
  return typeof value === "object" && value !== null && !Array.isArray(value);
71097
71762
  }
71098
71763
  function isPrivateCliOutputKey(key) {
@@ -71100,7 +71765,7 @@ function isPrivateCliOutputKey(key) {
71100
71765
  }
71101
71766
  function sanitizeWorkspaceAskOutput(value) {
71102
71767
  if (Array.isArray(value)) return value.map((item) => sanitizeWorkspaceAskOutput(item));
71103
- if (!isRecord2(value)) return value;
71768
+ if (!isRecord3(value)) return value;
71104
71769
  const sanitized = {};
71105
71770
  for (const [key, nested] of Object.entries(value)) {
71106
71771
  if (isPrivateCliOutputKey(key)) continue;
@@ -71110,20 +71775,20 @@ function sanitizeWorkspaceAskOutput(value) {
71110
71775
  }
71111
71776
  async function prepareTaskAskOutput(result, masterKey) {
71112
71777
  const output = sanitizeWorkspaceAskOutput(result);
71113
- if (isRecord2(result.task)) {
71778
+ if (isRecord3(result.task)) {
71114
71779
  output.task = taskToJson(await decryptUserTask(result.task, masterKey));
71115
71780
  }
71116
- if (Array.isArray(result.tasks) && result.tasks.every(isRecord2)) {
71781
+ if (Array.isArray(result.tasks) && result.tasks.every(isRecord3)) {
71117
71782
  output.tasks = (await decryptUserTasks(result.tasks, masterKey)).map(taskToJson);
71118
71783
  }
71119
71784
  return output;
71120
71785
  }
71121
71786
  async function preparePlanAskOutput(result, masterKey) {
71122
71787
  const output = sanitizeWorkspaceAskOutput(result);
71123
- if (isRecord2(result.plan)) {
71788
+ if (isRecord3(result.plan)) {
71124
71789
  output.plan = planToJson(await decryptUserPlan(result.plan, masterKey));
71125
71790
  }
71126
- if (Array.isArray(result.plans) && result.plans.every(isRecord2)) {
71791
+ if (Array.isArray(result.plans) && result.plans.every(isRecord3)) {
71127
71792
  output.plans = (await decryptUserPlans(result.plans, masterKey)).map(planToJson);
71128
71793
  }
71129
71794
  return output;
@@ -73437,6 +74102,28 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
73437
74102
  }
73438
74103
  return;
73439
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
+ }
73440
74127
  if (subcommand === "messages") {
73441
74128
  const chatId = rest[0];
73442
74129
  if (!chatId) {
@@ -74413,6 +75100,113 @@ function resolveExampleChatForOpen(target) {
74413
75100
  }
74414
75101
  return getExampleChatConversation(target);
74415
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
+ }
74416
75210
  async function openUrl(url) {
74417
75211
  const { exec: exec2 } = await import("child_process");
74418
75212
  const platform4 = process.platform;
@@ -80447,6 +81241,7 @@ function printChatsHelp() {
80447
81241
  openmates chats list [--limit <n>] [--page <n>] [--json]
80448
81242
  openmates chats show <chat-id> [--raw] [--json] [--all]
80449
81243
  openmates chats messages <chat-id> [--json]
81244
+ openmates chats files <chat-id> [--json]
80450
81245
  openmates chats <chat-id> add-to-project <project-id> [--folder <folder-id>] [--openmates-only|--repo-copy|--remote-cache-copy|--remote-copy] [--json]
80451
81246
  openmates chats <chat-id> remove-from-project <project-id> [--json]
80452
81247
  openmates chats fork <chat-id> --from-message <message-id> [--title <title>] [--json]
@@ -80474,6 +81269,11 @@ Options for 'show':
80474
81269
  --raw Show raw decrypted message content without rendering embeds
80475
81270
  or cleaning embed references. Useful for debugging.
80476
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
+
80477
81277
  Options for 'fork', 'rewind', and 'retry':
80478
81278
  --from-message <id> Message boundary for non-destructive fork.
80479
81279
  --to-message <id> Message boundary to keep when rewinding.
@@ -80551,6 +81351,8 @@ Examples:
80551
81351
  openmates chats open gigantic-airplanes-transporting-rocket-parts
80552
81352
  openmates chats show d262cb68
80553
81353
  openmates chats messages d262cb68
81354
+ openmates chats files d262cb68
81355
+ openmates chats files example-audio-speak-friendly-welcome-message --json
80554
81356
  openmates chats fork d262cb68 --from-message 8f4e2a1c --title "Clean branch"
80555
81357
  openmates chats rewind d262cb68 --to-message 8f4e2a1c --send "Continue from here" --yes
80556
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-QQA6E4KG.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-QQA6E4KG.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.1",
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