openmates 0.27.0-alpha.6 → 0.27.0-alpha.8

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.
@@ -12586,7 +12586,7 @@ var OpenMatesClient = class _OpenMatesClient {
12586
12586
  if (!userId) {
12587
12587
  throw new Error("Could not resolve current user id for local connected-account connector.");
12588
12588
  }
12589
- const { createHash: createHash31 } = await import("crypto");
12589
+ const { createHash: createHash32 } = await import("crypto");
12590
12590
  const accountId = randomUUID10();
12591
12591
  const masterKey = this.getMasterKeyBytes();
12592
12592
  const encryptLocalConnectorValue = async (value) => {
@@ -12595,9 +12595,9 @@ var OpenMatesClient = class _OpenMatesClient {
12595
12595
  };
12596
12596
  const payload = {
12597
12597
  id: accountId,
12598
- hashed_user_id: createHash31("sha256").update(userId).digest("hex"),
12598
+ hashed_user_id: createHash32("sha256").update(userId).digest("hex"),
12599
12599
  encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
12600
- provider_type_hash: createHash31("sha256").update(input.provider_id).digest("hex"),
12600
+ provider_type_hash: createHash32("sha256").update(input.provider_id).digest("hex"),
12601
12601
  encrypted_account_label: await encryptLocalConnectorValue(input.label),
12602
12602
  encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
12603
12603
  encrypted_app_permissions: await encryptLocalConnectorValue({
@@ -13388,7 +13388,61 @@ var OpenMatesClient = class _OpenMatesClient {
13388
13388
  createdAt: typeof d.created_at === "number" ? d.created_at : null,
13389
13389
  updatedAt: typeof d.last_edited_overall_timestamp === "number" ? d.last_edited_overall_timestamp : null,
13390
13390
  category,
13391
- mateName: category ? MATE_NAMES[category] ?? null : null
13391
+ mateName: category ? MATE_NAMES[category] ?? null : null,
13392
+ parentId: typeof d.parent_id === "string" ? d.parent_id : null,
13393
+ isSubChat: d.is_sub_chat === true,
13394
+ isHiddenCandidate: !chatKeyBytes
13395
+ };
13396
+ }
13397
+ /** Global activity snapshot; decrypt only running chats and their ancestors. */
13398
+ async getChatActivity(options = {}) {
13399
+ this.requireSession();
13400
+ const teamId = this.resolveTeamContext(options), master = Buffer.from(this.getMasterKeyBytes());
13401
+ const response = await this.http.get(
13402
+ this.appendTeamQuery("/v1/chats/activity", options),
13403
+ this.getCliRequestHeaders()
13404
+ );
13405
+ if (!response.ok) throw new Error(`Chat activity unavailable: HTTP ${response.status}`);
13406
+ if (!this.hasSession() || teamId !== this.resolveTeamContext(options) || !master.equals(Buffer.from(this.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
13407
+ const ids = response.data.active_tasks.map((task) => task.chat_id);
13408
+ if (!ids.length) return { ids, chats: [] };
13409
+ const chats = await this.getSidebarChats(response.data.chats.map((chat) => chat.chat_id), options);
13410
+ if (teamId !== this.resolveTeamContext(options)) throw new Error("Chat workspace changed");
13411
+ return { ids, chats };
13412
+ }
13413
+ /** Minimal encrypted metadata; unreadable keys stay excluded from normal surfaces. */
13414
+ async getSidebarChats(ids, options = {}) {
13415
+ this.requireSession();
13416
+ const teamId = this.resolveTeamContext(options), master = Buffer.from(this.getMasterKeyBytes());
13417
+ const key = await this.getChatWrappingKey(teamId, master), result = [];
13418
+ const current = () => this.hasSession() && teamId === this.resolveTeamContext(options) && master.equals(Buffer.from(this.getMasterKeyBytes()));
13419
+ for (let start = 0; start < ids.length; start += 100) {
13420
+ if (!current()) throw new Error("Chat workspace changed");
13421
+ const response = await this.http.post(
13422
+ this.appendTeamQuery("/v1/chats/metadata/batch", options),
13423
+ { chat_ids: ids.slice(start, start + 100) },
13424
+ this.getCliRequestHeaders()
13425
+ );
13426
+ if (!response.ok) throw new Error(`Sidebar metadata unavailable: HTTP ${response.status}`);
13427
+ for (const details of response.data.chats) {
13428
+ const timestamp = Number(details.updated_at);
13429
+ result.push(await this.decryptChatListItem({ details: {
13430
+ ...details,
13431
+ last_edited_overall_timestamp: Number.isFinite(timestamp) ? timestamp : Date.parse(String(details.updated_at)) / 1e3
13432
+ }, messages: [] }, key, null, teamId));
13433
+ }
13434
+ }
13435
+ if (!current()) throw new Error("Chat workspace changed");
13436
+ return result;
13437
+ }
13438
+ /** Observe lifecycle metadata without claiming or collecting AI responses. */
13439
+ async observeChatActivity(onChange) {
13440
+ const { ws } = await this.openWsClient({ taskUpdateJobs: false });
13441
+ const types = ["ai_task_initiated", "ai_typing_started", "ai_typing_ended", "ai_background_response_completed", "ai_task_cancel_requested", "ai_task_error", "pending_ai_response"];
13442
+ const cleanup = types.map((type) => ws.onMessageType(type, onChange));
13443
+ return () => {
13444
+ cleanup.forEach((remove) => remove());
13445
+ ws.close();
13392
13446
  };
13393
13447
  }
13394
13448
  async listChats(limit = 10, page = 1, options = {}) {
@@ -14391,8 +14445,8 @@ var OpenMatesClient = class _OpenMatesClient {
14391
14445
  );
14392
14446
  }
14393
14447
  const embedId = String(embed.embed_id ?? embed.id ?? "");
14394
- const { createHash: createHash31 } = await import("crypto");
14395
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
14448
+ const { createHash: createHash32 } = await import("crypto");
14449
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
14396
14450
  const embedKeyBytes = await this.resolveEmbedKey(
14397
14451
  cache,
14398
14452
  masterKey,
@@ -14494,7 +14548,7 @@ var OpenMatesClient = class _OpenMatesClient {
14494
14548
  async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
14495
14549
  if (visited.has(embedId)) return null;
14496
14550
  visited.add(embedId);
14497
- const { createHash: createHash31 } = await import("crypto");
14551
+ const { createHash: createHash32 } = await import("crypto");
14498
14552
  const masterKeyEntry = cache.embedKeys.find(
14499
14553
  (ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
14500
14554
  );
@@ -14511,7 +14565,7 @@ var OpenMatesClient = class _OpenMatesClient {
14511
14565
  if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
14512
14566
  const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
14513
14567
  const owningChat = cache.chats.find((c) => {
14514
- const chatHash = createHash31("sha256").update(String(c.details.id ?? "")).digest("hex");
14568
+ const chatHash = createHash32("sha256").update(String(c.details.id ?? "")).digest("hex");
14515
14569
  return chatHash === hashedChatId;
14516
14570
  });
14517
14571
  if (owningChat) {
@@ -14540,7 +14594,7 @@ var OpenMatesClient = class _OpenMatesClient {
14540
14594
  const parentFullId = String(
14541
14595
  parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
14542
14596
  );
14543
- const parentHashedId = createHash31("sha256").update(parentFullId).digest("hex");
14597
+ const parentHashedId = createHash32("sha256").update(parentFullId).digest("hex");
14544
14598
  const parentKey = await this.resolveEmbedKey(
14545
14599
  cache,
14546
14600
  masterKey,
@@ -16737,6 +16791,7 @@ var OpenMatesClient = class _OpenMatesClient {
16737
16791
  );
16738
16792
  }
16739
16793
  async resolveWorkflowId(query, options = {}) {
16794
+ if (CANONICAL_UUID_PATTERN.test(query)) return query;
16740
16795
  const workflows = await this.listWorkflows(options);
16741
16796
  const exactId = workflows.find((workflow) => workflow.id === query);
16742
16797
  if (exactId) return exactId.id;
@@ -16835,6 +16890,39 @@ var OpenMatesClient = class _OpenMatesClient {
16835
16890
  }
16836
16891
  return this.decryptWorkflowSlug(response.data.workflow, { personal: true });
16837
16892
  }
16893
+ async saveWorkflowAsReusable(workflowId, options) {
16894
+ this.requireSession();
16895
+ if (!options.idempotencyKey.trim()) throw new Error("Workflow save-as-reusable requires a stable idempotencyKey");
16896
+ const response = await this.http.post(
16897
+ `/v1/workflows/${encodeURIComponent(workflowId)}/save-as-reusable`,
16898
+ {},
16899
+ { ...this.getCliRequestHeaders(), "Idempotency-Key": options.idempotencyKey }
16900
+ );
16901
+ if (!response.ok || !response.data.workflow) throw new Error(`Workflow save-as-reusable failed with HTTP ${response.status}`);
16902
+ return this.decryptWorkflowSlug(response.data.workflow, { personal: true });
16903
+ }
16904
+ async workflowRunRouting(params) {
16905
+ const routing = {};
16906
+ if (params.sourceChatId !== void 0) routing.source_chat_id = await this.resolveRequiredChatId(params.sourceChatId, { personal: true });
16907
+ if (params.messageDestinationOverrides !== void 0) {
16908
+ routing.message_destination_overrides = Object.fromEntries(await Promise.all(
16909
+ Object.entries(params.messageDestinationOverrides).map(async ([nodeId, chatId]) => [nodeId, await this.resolveRequiredChatId(chatId, { personal: true })])
16910
+ ));
16911
+ }
16912
+ if (params.returnOutputs !== void 0) routing.return_outputs = params.returnOutputs;
16913
+ return routing;
16914
+ }
16915
+ async runWorkflowOnce(params) {
16916
+ this.requireSession();
16917
+ if (!params.idempotencyKey.trim()) throw new Error("Workflow run-once requires a stable idempotencyKey");
16918
+ const response = await this.http.post(
16919
+ "/v1/workflows/run-once",
16920
+ { title: params.title, graph: params.graph, input: params.input ?? {}, ...await this.workflowRunRouting(params) },
16921
+ { ...this.getCliRequestHeaders(), "Idempotency-Key": params.idempotencyKey }
16922
+ );
16923
+ if (!response.ok || !response.data.workflow || !response.data.run) throw new Error(`Workflow run-once failed with HTTP ${response.status}`);
16924
+ return { workflow: await this.decryptWorkflowSlug(response.data.workflow, { personal: true }), run: response.data.run };
16925
+ }
16838
16926
  async enableWorkflow(workflowId) {
16839
16927
  return this.setWorkflowEnabled(workflowId, true);
16840
16928
  }
@@ -16848,7 +16936,7 @@ var OpenMatesClient = class _OpenMatesClient {
16848
16936
  }
16849
16937
  const response = await this.http.post(
16850
16938
  `/v1/workflows/${encodeURIComponent(workflowId)}/run`,
16851
- { mode: params.mode ?? "manual", input: params.input ?? {} },
16939
+ { mode: params.mode ?? "manual", input: params.input ?? {}, ...await this.workflowRunRouting(params) },
16852
16940
  { ...this.getCliRequestHeaders(), "Idempotency-Key": params.idempotencyKey }
16853
16941
  );
16854
16942
  if (!response.ok || !response.data.run) {
@@ -17357,7 +17445,7 @@ var OpenMatesClient = class _OpenMatesClient {
17357
17445
  this.requireSession();
17358
17446
  const response = await this.http.post(
17359
17447
  "/v1/projects/ask/plan",
17360
- { instruction: input.instruction },
17448
+ { instruction: input.instruction, ...input.chatTitles !== void 0 ? { chat_titles: input.chatTitles } : {} },
17361
17449
  this.getCliRequestHeaders()
17362
17450
  );
17363
17451
  if (!response.ok) throw new Error(`Project ask planning failed with HTTP ${response.status}`);
@@ -17366,7 +17454,7 @@ var OpenMatesClient = class _OpenMatesClient {
17366
17454
  async listProjectItems(projectId, options = {}) {
17367
17455
  this.requireSession();
17368
17456
  const response = await this.http.get(
17369
- this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
17457
+ this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items${options.chatOnly ? "?chat_only=true" : ""}`, options),
17370
17458
  this.getCliRequestHeaders()
17371
17459
  );
17372
17460
  if (!response.ok) throw this.projectRequestError("item list", response);
@@ -17503,10 +17591,10 @@ var OpenMatesClient = class _OpenMatesClient {
17503
17591
  }
17504
17592
  return { ws: opened.ws, ownerId: opened.ownerId };
17505
17593
  }
17506
- async createProjectItem(projectId, input) {
17594
+ async createProjectItem(projectId, input, options = {}) {
17507
17595
  this.requireSession();
17508
17596
  const response = await this.http.post(
17509
- `/v1/projects/${encodeURIComponent(projectId)}/items`,
17597
+ this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
17510
17598
  input,
17511
17599
  this.getCliRequestHeaders()
17512
17600
  );
@@ -17515,6 +17603,20 @@ var OpenMatesClient = class _OpenMatesClient {
17515
17603
  }
17516
17604
  return response.data.item;
17517
17605
  }
17606
+ async moveProjectItemToFolder(projectId, itemId, folderId, options = {}) {
17607
+ this.requireSession();
17608
+ const response = await this.http.patch(
17609
+ this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items/${encodeURIComponent(itemId)}`, options),
17610
+ { folder_id: folderId, updated_at: Math.floor(Date.now() / 1e3) },
17611
+ this.getCliRequestHeaders()
17612
+ );
17613
+ if (!response.ok) throw this.projectRequestError("folder move", response);
17614
+ }
17615
+ async createProjectFolder(projectId, input, options = {}) {
17616
+ this.requireSession();
17617
+ const response = await this.http.post(this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/folders`, options), input, this.getCliRequestHeaders());
17618
+ if (!response.ok) throw this.projectRequestError("folder create", response);
17619
+ }
17518
17620
  async deleteProjectItemByTarget(projectId, itemType, targetId, options = {}) {
17519
17621
  this.requireSession();
17520
17622
  const params = new URLSearchParams({ item_type: itemType, target_id: targetId });
@@ -19150,7 +19252,7 @@ Required: ${schema.required.join(", ")}`
19150
19252
  const session = this.requireSession();
19151
19253
  const masterKey = base64ToBytes(session.masterKeyExportedB64);
19152
19254
  const cache = await this.ensureSynced();
19153
- const { createHash: createHash31 } = await import("crypto");
19255
+ const { createHash: createHash32 } = await import("crypto");
19154
19256
  const embed = cache.embeds.find(
19155
19257
  (e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
19156
19258
  );
@@ -19158,7 +19260,7 @@ Required: ${schema.required.join(", ")}`
19158
19260
  throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
19159
19261
  }
19160
19262
  const embedId = String(embed.embed_id ?? embed.id ?? "");
19161
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
19263
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
19162
19264
  const embedKeyBytes = await this.resolveEmbedKey(
19163
19265
  cache,
19164
19266
  masterKey,
@@ -19266,8 +19368,8 @@ Required: ${schema.required.join(", ")}`
19266
19368
  if (!embed) {
19267
19369
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
19268
19370
  }
19269
- const { createHash: createHash31 } = await import("crypto");
19270
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
19371
+ const { createHash: createHash32 } = await import("crypto");
19372
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
19271
19373
  const embedKey = await this.resolveEmbedKey(
19272
19374
  cache,
19273
19375
  masterKey,
@@ -19383,8 +19485,8 @@ Required: ${schema.required.join(", ")}`
19383
19485
  if (!embed) {
19384
19486
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
19385
19487
  }
19386
- const { createHash: createHash31 } = await import("crypto");
19387
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
19488
+ const { createHash: createHash32 } = await import("crypto");
19489
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
19388
19490
  const embedKey = await this.resolveEmbedKey(
19389
19491
  cache,
19390
19492
  masterKey,
@@ -19916,9 +20018,9 @@ Required: ${schema.required.join(", ")}`
19916
20018
  chats.splice(0, chats.length, ...reconciledChats);
19917
20019
  if (totalChatCount === 0) totalChatCount = chats.length;
19918
20020
  if (reconciliation.deleted_chat_ids?.length) {
19919
- const { createHash: createHash31 } = await import("crypto");
20021
+ const { createHash: createHash32 } = await import("crypto");
19920
20022
  const deletedHashes = new Set(
19921
- reconciliation.deleted_chat_ids.map((id) => createHash31("sha256").update(id).digest("hex"))
20023
+ reconciliation.deleted_chat_ids.map((id) => createHash32("sha256").update(id).digest("hex"))
19922
20024
  );
19923
20025
  const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
19924
20026
  const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
@@ -30556,12 +30658,47 @@ var OpenMatesWorkflows = class {
30556
30658
  if (!response.workflow) throw new OpenMatesApiError(500, { detail: "Workflow response missing workflow" });
30557
30659
  return this.decryptWorkflowSlug(response.workflow);
30558
30660
  }
30661
+ async saveAsReusable(workflowId, options) {
30662
+ if (!options.idempotencyKey.trim()) throw new OpenMatesConfigError("Workflow save-as-reusable requires a stable idempotencyKey");
30663
+ const resolvedWorkflowId = await this.resolveId(workflowId);
30664
+ const response = await this.client.request(
30665
+ `/v1/workflows/${encodeURIComponent(resolvedWorkflowId)}/save-as-reusable`,
30666
+ {},
30667
+ void 0,
30668
+ { "Idempotency-Key": options.idempotencyKey }
30669
+ );
30670
+ if (!response.workflow) throw new OpenMatesApiError(500, { detail: "Workflow response missing workflow" });
30671
+ return this.decryptWorkflowSlug(response.workflow);
30672
+ }
30673
+ async runRouting(params) {
30674
+ const routing = {};
30675
+ if (params.sourceChatId !== void 0) routing.source_chat_id = await resolveSdkChatId(this.client, params.sourceChatId);
30676
+ if (params.messageDestinationOverrides !== void 0) {
30677
+ routing.message_destination_overrides = Object.fromEntries(await Promise.all(
30678
+ Object.entries(params.messageDestinationOverrides).map(async ([nodeId, chatId]) => [nodeId, await resolveSdkChatId(this.client, chatId)])
30679
+ ));
30680
+ }
30681
+ if (params.returnOutputs !== void 0) routing.return_outputs = params.returnOutputs;
30682
+ return routing;
30683
+ }
30684
+ async runOnce(params) {
30685
+ if (!params.idempotencyKey.trim()) throw new OpenMatesConfigError("Workflow run-once requires a stable idempotencyKey");
30686
+ const response = await this.client.request(
30687
+ "/v1/workflows/run-once",
30688
+ { title: params.title, graph: params.graph, input: params.input ?? {}, ...await this.runRouting(params) },
30689
+ void 0,
30690
+ { "Idempotency-Key": params.idempotencyKey }
30691
+ );
30692
+ if (!response.workflow || !response.run) throw new OpenMatesApiError(500, { detail: "Workflow run-once response missing workflow or run" });
30693
+ return { workflow: await this.decryptWorkflowSlug(response.workflow), run: response.run };
30694
+ }
30559
30695
  async run(workflowId, params) {
30560
30696
  if (!params.idempotencyKey.trim()) throw new OpenMatesConfigError("Workflow run requires a stable idempotencyKey");
30561
30697
  const resolvedWorkflowId = await this.resolveId(workflowId);
30562
30698
  const response = await this.client.request(`/v1/workflows/${encodeURIComponent(resolvedWorkflowId)}/run`, {
30563
30699
  mode: params.mode ?? "manual",
30564
- input: params.input ?? {}
30700
+ input: params.input ?? {},
30701
+ ...await this.runRouting(params)
30565
30702
  }, void 0, { "Idempotency-Key": params.idempotencyKey });
30566
30703
  if (!response.run) throw new OpenMatesApiError(500, { detail: "Workflow response missing run" });
30567
30704
  return response.run;
@@ -31158,7 +31295,7 @@ async function flushPendingTaskActivities(client2, teamId, stateDir2 = resolveSt
31158
31295
  import { existsSync as existsSync26, readFileSync as readFileSync24, realpathSync as realpathSync12, readdirSync as readdirSync14, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
31159
31296
  import { fileURLToPath as fileURLToPath4 } from "url";
31160
31297
  import { basename as basename9, dirname as dirname16, join as join29 } from "path";
31161
- import { createHash as createHash30, randomBytes as randomBytes14, randomUUID as randomUUID23 } from "crypto";
31298
+ import { createHash as createHash31, randomBytes as randomBytes14, randomUUID as randomUUID24 } from "crypto";
31162
31299
  import { arch as arch2, platform as platform3 } from "os";
31163
31300
  import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
31164
31301
 
@@ -31270,7 +31407,7 @@ function assertPortable(value) {
31270
31407
  }
31271
31408
  function remapGraph(graph, ids) {
31272
31409
  const escaped = Array.from(ids.keys()).sort((a, b) => b.length - a.length).map((id) => id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
31273
- const references = escaped.length ? new RegExp(`(\\$nodes\\.|\\{\\{\\s*steps\\.)(${escaped.join("|")})(?=[.}\\s]|$)`, "g") : null;
31410
+ const references = escaped.length ? new RegExp(`(\\$nodes\\.|\\{\\{\\s*steps\\.|\\$items\\.|\\{\\{\\s*items\\.)(${escaped.join("|")})(?=[.}\\s]|$)`, "g") : null;
31274
31411
  function visit(value) {
31275
31412
  if (typeof value === "string") return references ? value.replace(references, (_match, prefix, id) => prefix + ids.get(id)) : value;
31276
31413
  if (Array.isArray(value)) return value.map((item) => visit(item));
@@ -60704,6 +60841,15 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
60704
60841
  manual_intro: {
60705
60842
  text: "Use this skill directly by entering your request below."
60706
60843
  },
60844
+ requirements: {
60845
+ text: "Requirements"
60846
+ },
60847
+ optional: {
60848
+ text: "(optional)"
60849
+ },
60850
+ requirements_placeholder: {
60851
+ text: "Describe what the results should match."
60852
+ },
60707
60853
  via: {
60708
60854
  text: "via"
60709
60855
  },
@@ -60905,6 +61051,21 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
60905
61051
  request_error: {
60906
61052
  text: "The request could not finish. You can try again."
60907
61053
  },
61054
+ saving: {
61055
+ text: "Saving results\u2026"
61056
+ },
61057
+ saved: {
61058
+ text: "Results saved"
61059
+ },
61060
+ save_failed: {
61061
+ text: "Results are available. Saving failed."
61062
+ },
61063
+ retry_save: {
61064
+ text: "Retry saving"
61065
+ },
61066
+ saving_results: {
61067
+ text: "Results awaiting save"
61068
+ },
60908
61069
  viewer_read_only: {
60909
61070
  text: "Your team role allows viewing results. Ask a team member to run this skill."
60910
61071
  },
@@ -62135,6 +62296,64 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
62135
62296
  }
62136
62297
  },
62137
62298
  chats: {
62299
+ projects: {
62300
+ move: {
62301
+ text: "Move to project"
62302
+ },
62303
+ move_here: {
62304
+ text: "Move here"
62305
+ },
62306
+ add: {
62307
+ text: "Add to project"
62308
+ },
62309
+ create: {
62310
+ text: "Create new project"
62311
+ },
62312
+ add_here: {
62313
+ text: "Add here"
62314
+ },
62315
+ new_folder: {
62316
+ text: "New subfolder"
62317
+ },
62318
+ folder_name: {
62319
+ text: "Folder name"
62320
+ },
62321
+ path: {
62322
+ text: "Project path"
62323
+ },
62324
+ up: {
62325
+ text: "Go up one level"
62326
+ },
62327
+ ancestors: {
62328
+ text: "Show full path"
62329
+ },
62330
+ open: {
62331
+ text: "Open project"
62332
+ },
62333
+ organizing: {
62334
+ text: "Organizing chats\u2026"
62335
+ },
62336
+ error: {
62337
+ text: "Could not organize chats. Please try again."
62338
+ }
62339
+ },
62340
+ activity: {
62341
+ heading: {
62342
+ text: "Active chats"
62343
+ },
62344
+ count: {
62345
+ text: "{count} chats active\u2026"
62346
+ },
62347
+ count_single: {
62348
+ text: "1 chat active\u2026"
62349
+ },
62350
+ subchats: {
62351
+ text: "{count} subchats running"
62352
+ },
62353
+ subchats_single: {
62354
+ text: "1 subchat running"
62355
+ }
62356
+ },
62138
62357
  chat: {
62139
62358
  welcome: {
62140
62359
  back_to_recent: {
@@ -81647,6 +81866,108 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
81647
81866
  if_unsure: {
81648
81867
  text: "If unsure"
81649
81868
  },
81869
+ check_result: {
81870
+ text: "Result"
81871
+ },
81872
+ check_result_boolean: {
81873
+ text: "True or False"
81874
+ },
81875
+ check_result_options: {
81876
+ text: "Multiple choice"
81877
+ },
81878
+ check_selection_mode: {
81879
+ text: "Select"
81880
+ },
81881
+ check_select_one: {
81882
+ text: "Exactly one"
81883
+ },
81884
+ check_select_multiple: {
81885
+ text: "All that apply"
81886
+ },
81887
+ check_option_name: {
81888
+ text: "Option name"
81889
+ },
81890
+ check_option_description: {
81891
+ text: "Select when (optional)"
81892
+ },
81893
+ add_option: {
81894
+ text: "Add option"
81895
+ },
81896
+ remove_option: {
81897
+ text: "Remove option"
81898
+ },
81899
+ if_no_match: {
81900
+ text: "If none match"
81901
+ },
81902
+ selected: {
81903
+ text: "Selected"
81904
+ },
81905
+ no_match: {
81906
+ text: "No match"
81907
+ },
81908
+ ai_check_options_guidance: {
81909
+ text: "Choose from the named options using earlier results. A variable alone is enough; an instruction is optional. Unsure is used when evidence is missing."
81910
+ },
81911
+ branch_mapping_required: {
81912
+ text: "This branch contains steps. Move or remove those steps before changing its result or deleting it."
81913
+ },
81914
+ for_each: {
81915
+ text: "For each"
81916
+ },
81917
+ for_each_items: {
81918
+ text: "List to process"
81919
+ },
81920
+ for_each_max_items: {
81921
+ text: "Maximum items"
81922
+ },
81923
+ for_each_advanced_limits: {
81924
+ text: "Advanced limits"
81925
+ },
81926
+ for_each_duration: {
81927
+ text: "Total time limit (seconds)"
81928
+ },
81929
+ for_each_credits: {
81930
+ text: "Credit limit"
81931
+ },
81932
+ for_each_timeout: {
81933
+ text: "Time limit per item (seconds)"
81934
+ },
81935
+ for_each_limits: {
81936
+ text: "Processes items one at a time. Defaults: 100 items, 300 seconds, 100 credits and 60 seconds per item."
81937
+ },
81938
+ for_each_array_required: {
81939
+ text: "Choose an earlier list and a maximum of 1 to 100 items."
81940
+ },
81941
+ for_each_body: {
81942
+ text: "For each item"
81943
+ },
81944
+ this_item: {
81945
+ text: "This item"
81946
+ },
81947
+ start_input: {
81948
+ text: "Start input"
81949
+ },
81950
+ item_number: {
81951
+ text: "Item"
81952
+ },
81953
+ chat_actions: {
81954
+ text: "Workflow actions"
81955
+ },
81956
+ chat_open_workflow: {
81957
+ text: "Open workflow"
81958
+ },
81959
+ chat_save_reusable: {
81960
+ text: "Save as reusable"
81961
+ },
81962
+ chat_open_saved_copy: {
81963
+ text: "Open saved copy"
81964
+ },
81965
+ chat_snapshot_note: {
81966
+ text: "Showing the workflow saved with this chat. Open the Workflows workspace to edit it."
81967
+ },
81968
+ chat_copy_failed: {
81969
+ text: "Could not save this workflow."
81970
+ },
81650
81971
  if: {
81651
81972
  text: "If"
81652
81973
  },
@@ -83994,7 +84315,7 @@ function formatMetricDelta(delta, suffix) {
83994
84315
  // src/tui.ts
83995
84316
  import { createInterface as createInterface4 } from "readline/promises";
83996
84317
  import { stdin as nodeStdin, stdout as nodeStdout } from "process";
83997
- import { randomUUID as randomUUID20 } from "crypto";
84318
+ import { randomUUID as randomUUID21 } from "crypto";
83998
84319
 
83999
84320
  // src/tuiExampleContinuation.ts
84000
84321
  function buildExampleContinuationHistory(conversation) {
@@ -84687,13 +85008,13 @@ function storedPath(metadata, displayName) {
84687
85008
  return null;
84688
85009
  }
84689
85010
  }
84690
- async function loadTuiProject(client2, id) {
85011
+ async function loadTuiProject(client2, id, chatOnly = false) {
84691
85012
  const context = contextFor(client2);
84692
85013
  const detail = await client2.getProject(id, context);
84693
85014
  const project = await projectFromRecord(client2, detail.project, context);
84694
85015
  const [itemsResponse, sourceRecords] = await Promise.all([
84695
- client2.listProjectItems(id, context),
84696
- client2.listProjectSources(id, context)
85016
+ client2.listProjectItems(id, { ...context, chatOnly }),
85017
+ chatOnly ? Promise.resolve([]) : client2.listProjectSources(id, context)
84697
85018
  ]);
84698
85019
  const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
84699
85020
  const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
@@ -84927,7 +85248,7 @@ function buildProjectForm() {
84927
85248
  ]
84928
85249
  };
84929
85250
  }
84930
- async function submitProjectForm(client2, form) {
85251
+ async function submitProjectForm(client2, form, chatOrganizationOnly = false) {
84931
85252
  const name = formValue(form, "name").trim();
84932
85253
  if (!name) throw new Error("Project name is required.");
84933
85254
  const writeMode = formValue(form, "write_mode");
@@ -84951,7 +85272,7 @@ async function submitProjectForm(client2, form) {
84951
85272
  created_at: timestamp,
84952
85273
  updated_at: timestamp,
84953
85274
  last_opened_at: timestamp,
84954
- write_mode: writeMode,
85275
+ write_mode: chatOrganizationOnly ? null : writeMode,
84955
85276
  default_focus_id: focus.focus_id,
84956
85277
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
84957
85278
  key_wrappers: wrapping.teamId ? [{
@@ -84963,7 +85284,7 @@ async function submitProjectForm(client2, form) {
84963
85284
  created_at: timestamp
84964
85285
  }] : []
84965
85286
  };
84966
- const result = await client2.createProject(payload, context);
85287
+ const result = await client2.createProject({ ...payload, ...chatOrganizationOnly ? { chat_organization_only: true } : {} }, context);
84967
85288
  return projectFromRecord(client2, { ...payload, ...result.project ?? {} }, context);
84968
85289
  }
84969
85290
 
@@ -85538,6 +85859,10 @@ var TUI_ACTIONS = [
85538
85859
  { label: "Move selected task", command: "/task-status" },
85539
85860
  { label: "Add task activity", command: "/task-activity" },
85540
85861
  { label: "Create Project", command: "/project-create" },
85862
+ { label: "Add chat to project", command: "/chat-add-to-project" },
85863
+ { label: "Move chat to project", command: "/chat-move-to-project" },
85864
+ { label: "Create project from chats", command: "/chat-create-project" },
85865
+ { label: "Show active chats", command: "/active" },
85541
85866
  { label: "New chat in Project", command: "/project-chat" },
85542
85867
  { label: "Edit workflow step", command: "/workflow-edit" },
85543
85868
  { label: "Enable or disable workflow", command: "/workflow-toggle" },
@@ -85552,6 +85877,259 @@ function paletteActions(query) {
85552
85877
  return TUI_ACTIONS.filter((item) => `${item.label} ${item.command}`.toLowerCase().includes(needle));
85553
85878
  }
85554
85879
 
85880
+ // src/tuiChatSidebar.ts
85881
+ import { createHash as createHash22, randomUUID as randomUUID18 } from "crypto";
85882
+
85883
+ // ../ui/src/utils/chatActivity.ts
85884
+ function processingAncestorIds(chats, runningIds) {
85885
+ const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
85886
+ const result = /* @__PURE__ */ new Set();
85887
+ for (const id of runningIds) {
85888
+ const ancestry = [];
85889
+ const visited = /* @__PURE__ */ new Set();
85890
+ let chat = byId.get(id);
85891
+ while (chat && !visited.has(chat.chat_id)) {
85892
+ visited.add(chat.chat_id);
85893
+ ancestry.push(chat);
85894
+ chat = chat.parent_id ? byId.get(chat.parent_id) : void 0;
85895
+ }
85896
+ if (ancestry.some((chat2) => chat2.is_hidden || chat2.is_hidden_candidate)) continue;
85897
+ for (const ancestor of ancestry) result.add(ancestor.chat_id);
85898
+ }
85899
+ return result;
85900
+ }
85901
+ function aggregateRunningChats(chats, runningIds) {
85902
+ const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
85903
+ const groups = /* @__PURE__ */ new Map();
85904
+ for (const id of runningIds) {
85905
+ let root = byId.get(id);
85906
+ if (!root) continue;
85907
+ const visited = /* @__PURE__ */ new Set([id]);
85908
+ let hidden = root.is_hidden || root.is_hidden_candidate;
85909
+ while (root.parent_id && byId.has(root.parent_id) && !visited.has(root.parent_id)) {
85910
+ visited.add(root.parent_id);
85911
+ root = byId.get(root.parent_id);
85912
+ hidden ||= root.is_hidden || root.is_hidden_candidate;
85913
+ }
85914
+ if (hidden) continue;
85915
+ let group = groups.get(root.chat_id);
85916
+ if (!group) {
85917
+ group = { chat: root, activeSubChatCount: 0 };
85918
+ groups.set(root.chat_id, group);
85919
+ }
85920
+ if (id !== root.chat_id) group.activeSubChatCount += 1;
85921
+ }
85922
+ return [...groups.values()];
85923
+ }
85924
+
85925
+ // ../ui/src/utils/chatProjectNavigation.ts
85926
+ function projectBreadcrumbs(project, folderId) {
85927
+ const ancestors = [];
85928
+ const visited = /* @__PURE__ */ new Set();
85929
+ let folder = project.folders.find((folder2) => folder2.id === folderId);
85930
+ while (folder && !visited.has(folder.id)) {
85931
+ visited.add(folder.id);
85932
+ ancestors.unshift({ id: folder.id, name: folder.name });
85933
+ folder = project.folders.find((candidate) => candidate.hash === folder.parentHash);
85934
+ }
85935
+ return [{ id: null, name: project.name }, ...ancestors];
85936
+ }
85937
+ function projectFolderChatIds(project, folderId, recursive = false) {
85938
+ const folder = project.folders.find((folder2) => folder2.id === folderId);
85939
+ if (folderId !== null && !folder) return /* @__PURE__ */ new Set();
85940
+ const hashes = /* @__PURE__ */ new Set([folder?.hash ?? null]);
85941
+ if (recursive) {
85942
+ let previous = -1;
85943
+ while (previous !== hashes.size) {
85944
+ previous = hashes.size;
85945
+ for (const candidate of project.folders) if (hashes.has(candidate.parentHash)) hashes.add(candidate.hash);
85946
+ }
85947
+ }
85948
+ return new Set(project.chats.filter((chat) => hashes.has(chat.folderHash)).map((chat) => chat.chatId));
85949
+ }
85950
+ function locationHasRunningChats(project, folderId, runningIds) {
85951
+ return [...projectFolderChatIds(project, folderId, true)].some((id) => runningIds.has(id));
85952
+ }
85953
+
85954
+ // src/tuiChatSidebar.ts
85955
+ function rowKey(row) {
85956
+ return [row.kind, row.chatId ?? "", row.projectId ?? "", row.folderId ?? ""].join(":");
85957
+ }
85958
+ function updateTuiChatSidebar(state, update) {
85959
+ const selected = tuiChatSidebarRows(state)[state.sidebarIndex];
85960
+ update();
85961
+ if (state.workspace !== "chats" || state.focus !== "sidebar") return;
85962
+ const rows2 = tuiChatSidebarRows(state);
85963
+ const index = selected ? rows2.findIndex((row) => rowKey(row) === rowKey(selected)) : -1;
85964
+ state.sidebarIndex = index >= 0 ? index : Math.max(0, Math.min(state.sidebarIndex, rows2.length - 1));
85965
+ }
85966
+ function runningTuiChatGroups(state) {
85967
+ const chats = new Map([...state.recentChats, ...state.activityChats].map((chat) => [chat.id, chat]));
85968
+ return aggregateRunningChats([...chats.values()].map((chat) => ({ chat_id: chat.id, parent_id: chat.parentId, is_hidden_candidate: chat.isHiddenCandidate })), new Set(state.runningChatIds)).map((group) => ({ ...group, chat: chats.get(group.chat.chat_id) }));
85969
+ }
85970
+ function tuiSidebarProjects(state) {
85971
+ return state.chatSidebarProjects.map((project) => ({
85972
+ id: project.id,
85973
+ name: project.name,
85974
+ folders: project.folders.map((folder) => ({
85975
+ id: folder.id,
85976
+ name: folder.name,
85977
+ hash: createHash22("sha256").update(folder.id).digest("hex"),
85978
+ parentHash: folder.parentHash ?? null
85979
+ })),
85980
+ chats: project.items.filter((item) => item.type === "chat").map((item) => ({
85981
+ id: item.id,
85982
+ chatId: item.targetId,
85983
+ folderHash: item.folderId ? createHash22("sha256").update(item.folderId).digest("hex") : null
85984
+ }))
85985
+ }));
85986
+ }
85987
+ function tuiChatBreadcrumb(state, width = 23) {
85988
+ const location = state.chatSidebarLocation;
85989
+ const project = tuiSidebarProjects(state).find((project2) => project2.id === location?.projectId);
85990
+ if (!project || !location) return null;
85991
+ const crumbs = projectBreadcrumbs(project, location.folderId);
85992
+ if (crumbs.length === 1) return truncateCells(project.name, width);
85993
+ const separator = crumbs.length > 2 ? " \u203A \u2026 \u203A " : " \u203A ";
85994
+ const current = crumbs.at(-1).name, budget = Math.max(2, width - cells(separator));
85995
+ if (cells(project.name) + cells(current) <= budget) return `${project.name}${separator}${current}`;
85996
+ const leafWidth = Math.min(cells(current), Math.max(1, budget - Math.min(cells(project.name), Math.ceil(budget / 2))));
85997
+ return `${truncateCells(project.name, budget - leafWidth)}${separator}${truncateCells(current, leafWidth)}`;
85998
+ }
85999
+ function tuiChatSidebarRows(state) {
86000
+ const groups = runningTuiChatGroups(state);
86001
+ const records = [...state.recentChats, ...state.activityChats].map((chat) => ({ chat_id: chat.id, parent_id: chat.parentId, is_hidden_candidate: chat.isHiddenCandidate }));
86002
+ const running = processingAncestorIds(records, new Set(state.runningChatIds));
86003
+ const rows2 = groups.map((group) => ({
86004
+ kind: "chat",
86005
+ chatId: group.chat.id,
86006
+ running: true,
86007
+ label: `${group.chat.title || "Untitled chat"}${group.activeSubChatCount ? ` (${group.activeSubChatCount} ${group.activeSubChatCount === 1 ? "subchat" : "subchats"})` : ""}`
86008
+ }));
86009
+ rows2.push({ kind: "new", label: "+ New chat" });
86010
+ if (state.chatProjectOperation) rows2.push({ kind: "create", label: "+ Create new project" });
86011
+ const projects = tuiSidebarProjects(state), location = state.chatSidebarLocation;
86012
+ const project = projects.find((project2) => project2.id === location?.projectId);
86013
+ let contents = null;
86014
+ if (project && location) {
86015
+ const crumbs = projectBreadcrumbs(project, location.folderId);
86016
+ rows2.push({ kind: "up", label: "\u2039 Up one level", projectId: project.id, folderId: crumbs.length > 1 ? crumbs[crumbs.length - 2].id : void 0 });
86017
+ if (crumbs.length > 2) rows2.push({ kind: "path", label: "\u2026 Show full path" });
86018
+ if (state.chatSidebarAncestors) for (const crumb of crumbs) rows2.push({ kind: "folder", label: crumb.name, projectId: project.id, folderId: crumb.id });
86019
+ if (state.chatProjectOperation) rows2.push({ kind: "choose", label: state.chatProjectOperation.mode === "move" ? "Move here" : "Add here", projectId: project.id, folderId: location.folderId });
86020
+ const hash2 = project.folders.find((folder) => folder.id === location.folderId)?.hash ?? null;
86021
+ rows2.push(...project.folders.filter((folder) => folder.parentHash === hash2).map((folder) => ({
86022
+ kind: "folder",
86023
+ label: folder.name,
86024
+ projectId: project.id,
86025
+ folderId: folder.id,
86026
+ running: locationHasRunningChats(project, folder.id, running)
86027
+ })));
86028
+ contents = projectFolderChatIds(project, location.folderId);
86029
+ } else rows2.push(...projects.map((project2) => ({
86030
+ kind: "project",
86031
+ label: project2.name,
86032
+ projectId: project2.id,
86033
+ running: locationHasRunningChats(project2, null, running)
86034
+ })));
86035
+ const organized = new Set(projects.flatMap((project2) => project2.chats.map((chat) => chat.chatId)));
86036
+ const candidates = new Map([...state.recentChats, ...state.sidebarLinkedChats].filter((chat) => !chat.isHiddenCandidate).map((chat) => [chat.id, { id: chat.id, title: chat.title, parentId: chat.parentId, isSubChat: chat.isSubChat }]));
86037
+ if (contents && location) for (const item of state.chatSidebarProjects.find((project2) => project2.id === location.projectId)?.items ?? []) {
86038
+ const verified = candidates.get(item.targetId);
86039
+ if (item.type === "chat" && contents.has(item.targetId) && verified && !verified.title)
86040
+ candidates.set(item.targetId, { ...verified, title: item.name });
86041
+ }
86042
+ if (!state.chatProjectOperation) rows2.push(...[...candidates.values()].filter((chat) => !running.has(chat.id) && (contents ? contents.has(chat.id) : !chat.parentId && !chat.isSubChat && !organized.has(chat.id))).map((chat) => ({ kind: "chat", chatId: chat.id, label: chat.title || "Untitled chat" })));
86043
+ return rows2;
86044
+ }
86045
+ async function refreshTuiChatSidebar(state, client2, render, includeProjects = false) {
86046
+ if (!state.signedIn) return;
86047
+ const team = typeof client2.getActiveTeamId === "function" ? client2.getActiveTeamId() : null;
86048
+ const master = typeof client2.getMasterKeyBytes === "function" ? Buffer.from(client2.getMasterKeyBytes()) : null;
86049
+ const current = () => state.signedIn && (typeof client2.getActiveTeamId !== "function" || client2.getActiveTeamId() === team) && (!master || master.equals(Buffer.from(client2.getMasterKeyBytes())));
86050
+ if (typeof client2.getChatActivity === "function") {
86051
+ try {
86052
+ const activity = await client2.getChatActivity();
86053
+ if (current()) {
86054
+ updateTuiChatSidebar(state, () => {
86055
+ state.runningChatIds = activity.ids;
86056
+ state.activityChats = activity.chats;
86057
+ });
86058
+ render();
86059
+ }
86060
+ } catch {
86061
+ if (current()) {
86062
+ state.status = "Running chat status unavailable.";
86063
+ render();
86064
+ }
86065
+ }
86066
+ }
86067
+ if (includeProjects && typeof client2.listProjects === "function") {
86068
+ const summaries = await loadTuiProjects(client2), projects = [];
86069
+ for (let start = 0; start < summaries.length; start += 4) projects.push(...await Promise.all(summaries.slice(start, start + 4).map((project) => loadTuiProject(client2, project.id, true))));
86070
+ const linkedIds = [...new Set(projects.flatMap((project) => project.items.filter((item) => item.type === "chat").map((item) => item.targetId)))];
86071
+ const linkedChats = typeof client2.getSidebarChats === "function" ? await client2.getSidebarChats(linkedIds) : [];
86072
+ if (current()) {
86073
+ updateTuiChatSidebar(state, () => {
86074
+ state.chatSidebarProjects = projects;
86075
+ state.sidebarLinkedChats = linkedChats;
86076
+ });
86077
+ render();
86078
+ }
86079
+ }
86080
+ }
86081
+ async function placeTuiChats(state, client2, chatIds, location, mode) {
86082
+ const context = { teamId: client2.getActiveTeamId() }, master = Buffer.from(client2.getMasterKeyBytes());
86083
+ const fence = () => {
86084
+ if (!state.signedIn || context.teamId !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
86085
+ };
86086
+ const destination = await loadTuiProject(client2, location.projectId, true);
86087
+ if (location.folderId && !destination.folders.some((folder) => folder.id === location.folderId)) throw new Error("Folder unavailable");
86088
+ for (const chatId of chatIds) {
86089
+ fence();
86090
+ const existing = destination.items.find((item) => item.type === "chat" && item.targetId === chatId);
86091
+ if (existing) {
86092
+ if ((existing.folderId ?? null) !== location.folderId) await client2.moveProjectItemToFolder(destination.id, existing.id, location.folderId, context);
86093
+ } else {
86094
+ const title = [...state.recentChats, ...state.activityChats].find((chat) => chat.id === chatId)?.title || "Untitled chat";
86095
+ const timestamp = Math.floor(Date.now() / 1e3);
86096
+ const payload = {
86097
+ project_item_id: randomUUID18(),
86098
+ folder_id: location.folderId,
86099
+ item_type: "chat",
86100
+ target_id: chatId,
86101
+ target_id_encrypted: await encryptWithAesGcmCombined(chatId, destination.projectKey),
86102
+ encrypted_display_name: await encryptWithAesGcmCombined(title, destination.projectKey),
86103
+ created_at: timestamp,
86104
+ updated_at: timestamp,
86105
+ position: timestamp
86106
+ };
86107
+ fence();
86108
+ await client2.createProjectItem(destination.id, payload, context);
86109
+ }
86110
+ }
86111
+ if (mode === "move") for (const source of state.chatSidebarProjects.filter((project) => project.id !== destination.id)) {
86112
+ for (const chatId of chatIds.filter((id) => source.items.some((item) => item.type === "chat" && item.targetId === id))) {
86113
+ fence();
86114
+ await client2.deleteProjectItemByTarget(source.id, "chat", chatId, context);
86115
+ }
86116
+ }
86117
+ }
86118
+ async function createTuiChatProject(state, client2, chatIds) {
86119
+ if (!state.signedIn || !chatIds.length) throw new Error("Choose a saved chat first.");
86120
+ const context = client2.getActiveTeamId(), master = Buffer.from(client2.getMasterKeyBytes());
86121
+ const titles = chatIds.slice(0, 8).map((id) => [...state.recentChats, ...state.activityChats, ...state.sidebarLinkedChats].find((chat) => chat.id === id)?.title?.slice(0, 200) || "Untitled chat");
86122
+ const plan = await client2.planProjectAsk({ instruction: "Name a new project from chat titles.", chatTitles: titles });
86123
+ const proposed = plan.proposed_project, name = proposed?.name?.trim();
86124
+ if (!name || name.length > 200) throw new Error("Could not generate a project title.");
86125
+ if (context !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
86126
+ const form = buildProjectForm();
86127
+ form.fields.find((field) => field.name === "name").value = name;
86128
+ const project = await submitProjectForm(client2, form, true);
86129
+ await placeTuiChats(state, client2, chatIds, { projectId: project.id, folderId: null }, "move");
86130
+ return project.id;
86131
+ }
86132
+
85555
86133
  // src/tuiLayout.ts
85556
86134
  var WORKSPACES = ["chats", "apps", "projects", "workflows", "tasks"];
85557
86135
  var DEFAULT_CHAT_BACKGROUND = "#4867cd";
@@ -85562,7 +86140,13 @@ function sidebarLines(state) {
85562
86140
  const selected = (label3, index) => `${state.focus === "sidebar" && state.sidebarIndex === index ? ">" : " "} ${label3}`;
85563
86141
  switch (state.workspace) {
85564
86142
  case "chats":
85565
- return ["Chats", "", selected("+ New chat", 0), "", "Recent chats", "", ...state.recentChats.map((c, i) => selected(c.title || "Untitled chat", i + 1)), "", "/chats browse /examples"];
86143
+ return [
86144
+ tuiChatBreadcrumb(state) ?? "Chats",
86145
+ "",
86146
+ ...tuiChatSidebarRows(state).map((row, index) => selected(`${row.running ? ["\u25F4", "\u25F7", "\u25F6", "\u25F5"][state.activityFrame % 4] + " " : ""}${row.label}`, index)),
86147
+ "",
86148
+ "/active /chat-add-to-project"
86149
+ ];
85566
86150
  case "projects":
85567
86151
  return ["Projects", "", ...state.projects.map((p, i) => selected(p.name, i)), "", "/project-create new"];
85568
86152
  case "tasks":
@@ -86025,6 +86609,8 @@ function homeHeader(state, width, height) {
86025
86609
  }
86026
86610
  function renderHomeChatCards(state, width, height) {
86027
86611
  const chats = homeChatItems(state), result = homeHeader(state, width, height);
86612
+ const activeCount = runningTuiChatGroups(state).length;
86613
+ if (activeCount) result.push(centered2(`${activeCount} ${activeCount === 1 ? "chat" : "chats"} active\u2026 /active`, width), "");
86028
86614
  if (!chats.length) {
86029
86615
  result.push(centered2(state.homeLoading ? "Loading your recent chats\u2026" : "Start a chat below. Your recent chats will appear here.", width), "");
86030
86616
  return result;
@@ -86048,7 +86634,7 @@ function renderHomeChatCards(state, width, height) {
86048
86634
  }
86049
86635
 
86050
86636
  // src/tuiAppsWorkspace.ts
86051
- import { randomBytes as randomBytes11, randomUUID as randomUUID18 } from "crypto";
86637
+ import { randomBytes as randomBytes11, randomUUID as randomUUID19 } from "crypto";
86052
86638
 
86053
86639
  // ../ui/src/components/apps/appsSkillFormUtils.ts
86054
86640
  var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -86505,7 +87091,7 @@ async function saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey,
86505
87091
  const value = record5(result);
86506
87092
  const type = string2(value.type, string2(value.embed_type));
86507
87093
  if (!type && typeof value.embed_id !== "string") return [];
86508
- const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id : randomUUID18();
87094
+ const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id : randomUUID19();
86509
87095
  return [(async () => ({
86510
87096
  embed_id: id,
86511
87097
  encrypted_type: await encryptWithAesGcmCombined(type || "app_skill_use", key),
@@ -86550,7 +87136,7 @@ async function executeConfirmedTuiAppsSkill(client2, run) {
86550
87136
  const owner = record5(await client2.whoAmI());
86551
87137
  const ownerId = string2(owner.id, string2(owner.user_id));
86552
87138
  if (!ownerId) throw new Error("Account identity unavailable; skill was not run.");
86553
- const rootId = randomUUID18();
87139
+ const rootId = randomUUID19();
86554
87140
  const key = randomBytes11(32);
86555
87141
  const encryptedKey = await encryptBytesWithAesGcm(key, client2.getMasterKeyBytes());
86556
87142
  await saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey, run, { status: "processing" }, "processing");
@@ -86796,6 +87382,15 @@ function createInitialTuiState() {
86796
87382
  taskStatusFilter: "",
86797
87383
  taskContext: null,
86798
87384
  recentChats: [],
87385
+ runningChatIds: [],
87386
+ activityChats: [],
87387
+ sidebarLinkedChats: [],
87388
+ activityFrame: 0,
87389
+ chatSidebarProjects: [],
87390
+ chatSidebarLocation: null,
87391
+ chatSidebarAncestors: false,
87392
+ chatProjectOperation: null,
87393
+ chatProjectBusy: false,
86799
87394
  activeChatId: null,
86800
87395
  activeChat: null,
86801
87396
  headerState: "new",
@@ -87207,7 +87802,7 @@ function wrap(line, width) {
87207
87802
  }
87208
87803
 
87209
87804
  // src/tuiWorkspaceController.ts
87210
- import { randomUUID as randomUUID19 } from "crypto";
87805
+ import { randomUUID as randomUUID20 } from "crypto";
87211
87806
  function chatDraftKey(state) {
87212
87807
  return state.screen === "example" ? `example:${state.activeExample?.chat.id}` : state.activeChatId ?? "new";
87213
87808
  }
@@ -87254,6 +87849,7 @@ async function recent(context) {
87254
87849
  if (request !== state.routeVersion || homeRequest !== state.homeLoadVersion || !state.signedIn) return;
87255
87850
  state.recentChats = chats;
87256
87851
  render();
87852
+ await refreshTuiChatSidebar(state, client2, render, true);
87257
87853
  }
87258
87854
  async function openSavedChat(context, id) {
87259
87855
  const { state, client: client2, render } = context;
@@ -87455,6 +88051,64 @@ async function handleWorkspaceCommand(context, command) {
87455
88051
  state.selectedProjectId = null;
87456
88052
  render();
87457
88053
  return true;
88054
+ case "/active":
88055
+ state.sidebarOpen = true;
88056
+ state.focus = "sidebar";
88057
+ state.sidebarIndex = 0;
88058
+ render();
88059
+ await refreshTuiChatSidebar(state, client2, render, true);
88060
+ state.sidebarIndex = 0;
88061
+ render();
88062
+ return true;
88063
+ case "/chat-add-to-project":
88064
+ case "/chat-move-to-project":
88065
+ case "/chat-create-project": {
88066
+ if (state.chatProjectBusy) return true;
88067
+ if (!state.signedIn) throw new Error("Sign in to organize chats.");
88068
+ const selected = state.focus === "sidebar" ? tuiChatSidebarRows(state)[state.sidebarIndex]?.chatId : ["start", "chats"].includes(state.screen) ? homeChatItems(state)[state.selectedIndex]?.id : state.activeChatId;
88069
+ const ids = arg ? arg.split(/\s+/).filter(Boolean) : selected ? [selected] : [];
88070
+ if (!ids.length) throw new Error("Choose a saved chat first.");
88071
+ await refreshTuiChatSidebar(state, client2, render, true);
88072
+ if (name === "/chat-create-project") {
88073
+ state.chatProjectBusy = true;
88074
+ state.status = "Organizing chats\u2026";
88075
+ render();
88076
+ try {
88077
+ const id = await createTuiChatProject(state, client2, ids);
88078
+ state.chatProjectOperation = null;
88079
+ await openProject(context, id);
88080
+ } finally {
88081
+ state.chatProjectBusy = false;
88082
+ }
88083
+ } else {
88084
+ state.chatProjectOperation = { chatIds: ids, mode: name === "/chat-move-to-project" ? "move" : "add" };
88085
+ state.chatSidebarLocation = null;
88086
+ state.sidebarOpen = true;
88087
+ state.focus = "sidebar";
88088
+ state.sidebarIndex = 0;
88089
+ state.status = "Choose a project or subfolder.";
88090
+ }
88091
+ render();
88092
+ return true;
88093
+ }
88094
+ case "/chat-subfolder": {
88095
+ const location = state.chatSidebarLocation;
88096
+ if (!location || !arg.trim()) throw new Error("Open a project folder and enter /chat-subfolder followed by a name.");
88097
+ const project = state.chatSidebarProjects.find((project2) => project2.id === location.projectId);
88098
+ if (!project) throw new Error("Project unavailable.");
88099
+ const timestamp = Math.floor(Date.now() / 1e3);
88100
+ await client2.createProjectFolder(project.id, {
88101
+ folder_id: randomUUID20(),
88102
+ parent_folder_id: location.folderId,
88103
+ encrypted_name: await encryptWithAesGcmCombined(arg.trim().slice(0, 200), project.projectKey),
88104
+ encrypted_sort_key: await encryptWithAesGcmCombined(arg.trim().toLowerCase().slice(0, 200), project.projectKey),
88105
+ created_at: timestamp,
88106
+ updated_at: timestamp,
88107
+ position: timestamp
88108
+ });
88109
+ await refreshTuiChatSidebar(state, client2, render, true);
88110
+ return true;
88111
+ }
87458
88112
  case "/sidebar":
87459
88113
  state.sidebarOpen = !state.sidebarOpen;
87460
88114
  state.focus = state.sidebarOpen ? "sidebar" : state.workspace === "chats" && !["start", "chats"].includes(state.screen) ? "composer" : "content";
@@ -87695,7 +88349,7 @@ async function saveForm(context) {
87695
88349
  } else if (form.kind === "workflow-create") {
87696
88350
  const description = formValue(form, "description").trim();
87697
88351
  if (!description) throw new Error("Describe what this workflow should do.");
87698
- const request = state.routeVersion, session = await client2.startWorkflowInput({ text: description, inputType: "text", optimisticSave: true, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, idempotencyKey: randomUUID19() });
88352
+ const request = state.routeVersion, session = await client2.startWorkflowInput({ text: description, inputType: "text", optimisticSave: true, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, idempotencyKey: randomUUID20() });
87699
88353
  if (request !== state.routeVersion || state.form !== form) return;
87700
88354
  const workflow = session.workflow ?? session.workflows?.[0];
87701
88355
  if (workflow) {
@@ -87721,7 +88375,7 @@ async function saveForm(context) {
87721
88375
  }
87722
88376
  }
87723
88377
  async function handleWorkspaceKey(context, chunk, key) {
87724
- const { state, render } = context;
88378
+ const { state, render, client: client2 } = context;
87725
88379
  if (state.form) {
87726
88380
  const form = state.form, field = form.fields[form.fieldIndex];
87727
88381
  if (!field) {
@@ -87802,6 +88456,13 @@ async function handleWorkspaceKey(context, chunk, key) {
87802
88456
  return true;
87803
88457
  }
87804
88458
  if (key.name === "escape") {
88459
+ if (state.chatProjectBusy) return true;
88460
+ if (state.chatProjectOperation) {
88461
+ state.chatProjectOperation = null;
88462
+ state.status = null;
88463
+ render();
88464
+ return true;
88465
+ }
87805
88466
  if (state.focus === "sidebar") {
87806
88467
  state.sidebarOpen = false;
87807
88468
  state.focus = chatHome ? "content" : state.workspace === "chats" ? "composer" : "content";
@@ -87878,11 +88539,34 @@ async function handleWorkspaceKey(context, chunk, key) {
87878
88539
  if (chunk !== "/") return true;
87879
88540
  }
87880
88541
  if (state.focus === "sidebar") {
87881
- const count = state.workspace === "chats" ? state.recentChats.length + 1 : state.workspace === "projects" ? state.projects.length : state.workspace === "tasks" ? state.tasks.length : state.workspace === "apps" ? state.apps.length : state.workflows.length;
88542
+ const chatRows = tuiChatSidebarRows(state);
88543
+ const count = state.workspace === "chats" ? chatRows.length : state.workspace === "projects" ? state.projects.length : state.workspace === "tasks" ? state.tasks.length : state.workspace === "apps" ? state.apps.length : state.workflows.length;
87882
88544
  if (key.name === "up" || key.name === "down") state.sidebarIndex = Math.max(0, Math.min(count - 1, state.sidebarIndex + (key.name === "up" ? -1 : 1)));
87883
88545
  else if (key.name === "return") {
87884
- if (state.workspace === "chats") await context.command(state.sidebarIndex === 0 ? "/new" : `/chat ${state.recentChats[state.sidebarIndex - 1]?.id}`);
87885
- else if (state.workspace === "projects") await context.command(`/project ${state.projects[state.sidebarIndex]?.id}`);
88546
+ if (state.workspace === "chats") {
88547
+ if (state.chatProjectBusy) return true;
88548
+ const row = chatRows[state.sidebarIndex];
88549
+ if (row?.kind === "new") await context.command("/new");
88550
+ else if (row?.kind === "chat") await context.command(`/chat ${row.chatId}`);
88551
+ else if (row?.kind === "path") state.chatSidebarAncestors = !state.chatSidebarAncestors;
88552
+ else if (row?.kind === "create" && state.chatProjectOperation) await context.command(`/chat-create-project ${state.chatProjectOperation.chatIds.join(" ")}`);
88553
+ else if (row?.kind === "choose" && state.chatProjectOperation) {
88554
+ const operation = state.chatProjectOperation;
88555
+ state.chatProjectBusy = true;
88556
+ try {
88557
+ await placeTuiChats(state, client2, operation.chatIds, { projectId: row.projectId, folderId: row.folderId ?? null }, operation.mode);
88558
+ state.chatProjectOperation = null;
88559
+ state.status = null;
88560
+ } finally {
88561
+ state.chatProjectBusy = false;
88562
+ await refreshTuiChatSidebar(state, client2, render, true);
88563
+ }
88564
+ } else if (row && ["project", "folder", "up"].includes(row.kind)) {
88565
+ state.chatSidebarLocation = row.kind === "up" && row.folderId === void 0 ? null : { projectId: row.projectId, folderId: row.folderId ?? null };
88566
+ state.chatSidebarAncestors = false;
88567
+ state.sidebarIndex = 0;
88568
+ }
88569
+ } else if (state.workspace === "projects") await context.command(`/project ${state.projects[state.sidebarIndex]?.id}`);
87886
88570
  else if (state.workspace === "tasks") await openTask(context, state.tasks[state.sidebarIndex]?.taskId ?? "");
87887
88571
  else if (state.workspace === "workflows") await context.command(`/workflow ${state.workflows[state.sidebarIndex]?.id}`);
87888
88572
  else if (state.workspace === "apps") await context.command(`/app ${state.apps[state.sidebarIndex]?.id}`);
@@ -88269,6 +88953,27 @@ async function runTui(client2, terminal = new TuiTerminal()) {
88269
88953
  let resolveResult = null;
88270
88954
  let renderTimer = null;
88271
88955
  let closed = false;
88956
+ let stopActivity;
88957
+ let activityTimer;
88958
+ let refreshingActivity = false;
88959
+ const refreshActivity = async () => {
88960
+ if (closed || refreshingActivity || !state.signedIn) return;
88961
+ refreshingActivity = true;
88962
+ try {
88963
+ await refreshTuiChatSidebar(state, client2, render);
88964
+ } finally {
88965
+ refreshingActivity = false;
88966
+ }
88967
+ };
88968
+ const activityPoll = setInterval(() => {
88969
+ void refreshActivity();
88970
+ }, 3e4);
88971
+ const activityAnimation = setInterval(() => {
88972
+ if (!closed && state.runningChatIds.length) {
88973
+ state.activityFrame = (state.activityFrame + 1) % 4;
88974
+ render();
88975
+ }
88976
+ }, 250);
88272
88977
  const render = () => {
88273
88978
  if (closed) return;
88274
88979
  if (state.screen !== "chat") client2.clearInteractiveChatViewer();
@@ -88284,6 +88989,10 @@ async function runTui(client2, terminal = new TuiTerminal()) {
88284
88989
  };
88285
88990
  const finish = (result) => {
88286
88991
  closed = true;
88992
+ stopActivity?.();
88993
+ clearTimeout(activityTimer);
88994
+ clearInterval(activityPoll);
88995
+ clearInterval(activityAnimation);
88287
88996
  client2.endInteractiveViewerSession();
88288
88997
  if (renderTimer) clearTimeout(renderTimer);
88289
88998
  renderTimer = null;
@@ -88300,6 +89009,19 @@ async function runTui(client2, terminal = new TuiTerminal()) {
88300
89009
  });
88301
89010
  render();
88302
89011
  void loadHomeData(state, client2, render);
89012
+ void refreshActivity();
89013
+ if (state.signedIn && typeof client2.observeChatActivity === "function") {
89014
+ void client2.observeChatActivity(() => {
89015
+ clearTimeout(activityTimer);
89016
+ activityTimer = setTimeout(() => {
89017
+ void refreshActivity();
89018
+ }, 300);
89019
+ }).then((stop) => {
89020
+ if (closed) stop();
89021
+ else stopActivity = stop;
89022
+ }).catch(() => {
89023
+ });
89024
+ }
88303
89025
  return new Promise((resolve18) => {
88304
89026
  resolveResult = resolve18;
88305
89027
  });
@@ -88612,12 +89334,12 @@ async function sendTuiMessage(params) {
88612
89334
  if (sourceExample) state.messages = sourceExample.messages.map((m) => ({ role: m.role === "user" ? "user" : "assistant", content: m.content, title: m.senderName }));
88613
89335
  const messages = state.messages;
88614
89336
  const existingChatId = sourceExample ? null : state.activeChatId;
88615
- const chatId = existingChatId ?? randomUUID20();
89337
+ const chatId = existingChatId ?? randomUUID21();
88616
89338
  const draftKey = sourceExample ? `example:${sourceExample.chat.id}` : existingChatId ?? "new";
88617
89339
  state.drafts[draftKey] = "";
88618
89340
  state.input = "";
88619
89341
  state.inputCursor = null;
88620
- const anonymousHistory = history ?? messages.filter((m) => m.role !== "system").map((m) => ({ message_id: randomUUID20(), role: m.role, content: m.content, sender_name: m.title ?? (m.role === "user" ? "User" : "Assistant"), created_at: Math.floor(Date.now() / 1e3) }));
89342
+ const anonymousHistory = history ?? messages.filter((m) => m.role !== "system").map((m) => ({ message_id: randomUUID21(), role: m.role, content: m.content, sender_name: m.title ?? (m.role === "user" ? "User" : "Assistant"), created_at: Math.floor(Date.now() / 1e3) }));
88621
89343
  state.activeChatId = chatId;
88622
89344
  state.headerState = existingChatId ? "ready" : "loading";
88623
89345
  state.headerError = null;
@@ -89042,7 +89764,7 @@ function clamp(value, min, max) {
89042
89764
  }
89043
89765
 
89044
89766
  // src/codexTaskBridge.ts
89045
- import { createHash as createHash22 } from "crypto";
89767
+ import { createHash as createHash23 } from "crypto";
89046
89768
  import { spawn as spawn3 } from "child_process";
89047
89769
  import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
89048
89770
  import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
@@ -89085,7 +89807,7 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
89085
89807
  const session = client2.getSession();
89086
89808
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
89087
89809
  const account = JSON.stringify(scope2);
89088
- const directory = join18(stateRoot, "codex-chat-deletions", createHash22("sha256").update(account).digest("hex"));
89810
+ const directory = join18(stateRoot, "codex-chat-deletions", createHash23("sha256").update(account).digest("hex"));
89089
89811
  if (!existsSync15(directory)) return;
89090
89812
  const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
89091
89813
  try {
@@ -89119,14 +89841,14 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
89119
89841
  }
89120
89842
 
89121
89843
  // src/taskCommandDelivery.ts
89122
- import { createHash as createHash23 } from "crypto";
89844
+ import { createHash as createHash24 } from "crypto";
89123
89845
  import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
89124
89846
  import { join as join19 } from "path";
89125
89847
  import lockfile6 from "proper-lockfile";
89126
89848
  async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
89127
89849
  const session = client2.getSession();
89128
89850
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
89129
- const directory = join19(root, "task-command-delivery", createHash23("sha256").update(JSON.stringify(scope2)).digest("hex"));
89851
+ const directory = join19(root, "task-command-delivery", createHash24("sha256").update(JSON.stringify(scope2)).digest("hex"));
89130
89852
  if (!existsSync16(directory)) return;
89131
89853
  const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
89132
89854
  try {
@@ -89236,7 +89958,7 @@ import {
89236
89958
  writeFileSync as writeFileSync13
89237
89959
  } from "fs";
89238
89960
  import { spawn as spawn7 } from "child_process";
89239
- import { createHash as createHash29 } from "crypto";
89961
+ import { createHash as createHash30 } from "crypto";
89240
89962
  import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
89241
89963
 
89242
89964
  // src/projectSourceRootPolicy.ts
@@ -89271,7 +89993,7 @@ function isEqualOrInside(parent, candidate) {
89271
89993
  }
89272
89994
 
89273
89995
  // src/projectPathPolicy.ts
89274
- import { createHash as createHash24, randomBytes as randomBytes12 } from "crypto";
89996
+ import { createHash as createHash25, randomBytes as randomBytes12 } from "crypto";
89275
89997
  import {
89276
89998
  chmodSync as chmodSync9,
89277
89999
  closeSync as closeSync5,
@@ -89552,7 +90274,7 @@ function isInside2(root, candidate) {
89552
90274
  return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
89553
90275
  }
89554
90276
  function sha2563(value) {
89555
- return createHash24("sha256").update(value).digest("hex");
90277
+ return createHash25("sha256").update(value).digest("hex");
89556
90278
  }
89557
90279
  function stableJson2(value) {
89558
90280
  return JSON.stringify(value, (_key, item) => {
@@ -89562,7 +90284,7 @@ function stableJson2(value) {
89562
90284
  }
89563
90285
 
89564
90286
  // src/remoteFileWrites.ts
89565
- import { createHash as createHash25, randomBytes as randomBytes13 } from "crypto";
90287
+ import { createHash as createHash26, randomBytes as randomBytes13 } from "crypto";
89566
90288
  import {
89567
90289
  chmodSync as chmodSync10,
89568
90290
  closeSync as closeSync7,
@@ -89827,7 +90549,7 @@ var MAX_FILE_BYTES2 = 200 * 1024;
89827
90549
  var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89828
90550
  var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89829
90551
  function sha256ProjectFile(content) {
89830
- return createHash25("sha256").update(content).digest("hex");
90552
+ return createHash26("sha256").update(content).digest("hex");
89831
90553
  }
89832
90554
  async function executeRemoteFileMutation(options) {
89833
90555
  const mutation = validateMutation(options.mutation);
@@ -90515,13 +91237,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
90515
91237
  import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
90516
91238
 
90517
91239
  // src/remoteCommandRuntime.ts
90518
- import { createHash as createHash27, randomUUID as randomUUID21 } from "crypto";
91240
+ import { createHash as createHash28, randomUUID as randomUUID22 } from "crypto";
90519
91241
  import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
90520
91242
  import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
90521
91243
  import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
90522
91244
 
90523
91245
  // src/remoteCommandAppArmor.ts
90524
- import { createHash as createHash26 } from "crypto";
91246
+ import { createHash as createHash27 } from "crypto";
90525
91247
  import { spawn as spawn5 } from "child_process";
90526
91248
  import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
90527
91249
  import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
@@ -90738,7 +91460,7 @@ function hasControl2(value) {
90738
91460
  });
90739
91461
  }
90740
91462
  function sha2564(value) {
90741
- return createHash26("sha256").update(value).digest("hex");
91463
+ return createHash27("sha256").update(value).digest("hex");
90742
91464
  }
90743
91465
 
90744
91466
  // src/remoteCommandRuntime.ts
@@ -91142,7 +91864,7 @@ var RemoteCommandRuntime = class {
91142
91864
  };
91143
91865
  function createRemoteCommandPreflight(request) {
91144
91866
  if (!request || typeof request !== "object") throw new RemoteCommandError("invalid_request", "Command request must be an object");
91145
- const executionId = request.execution_id ?? randomUUID21();
91867
+ const executionId = request.execution_id ?? randomUUID22();
91146
91868
  if (typeof executionId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(executionId)) throw new RemoteCommandError("invalid_request", "Invalid execution ID");
91147
91869
  if (typeof request.project_id !== "string" || !request.project_id || request.project_id.length > 256) throw new RemoteCommandError("invalid_request", "Invalid Project ID");
91148
91870
  if (typeof request.source_root !== "string" || !request.source_root) throw new RemoteCommandError("invalid_request", "Invalid Project source root");
@@ -91579,7 +92301,7 @@ function stableJson4(value) {
91579
92301
  return JSON.stringify(value);
91580
92302
  }
91581
92303
  function sha2565(value) {
91582
- return createHash27("sha256").update(value).digest("hex");
92304
+ return createHash28("sha256").update(value).digest("hex");
91583
92305
  }
91584
92306
  function deepFreeze(value) {
91585
92307
  if (value && typeof value === "object") {
@@ -91857,7 +92579,7 @@ function capabilityError(message) {
91857
92579
  }
91858
92580
 
91859
92581
  // src/remoteCommandResourceGrants.ts
91860
- import { createHash as createHash28 } from "crypto";
92582
+ import { createHash as createHash29 } from "crypto";
91861
92583
  import { chmodSync as chmodSync12, existsSync as existsSync22, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, realpathSync as realpathSync9, renameSync as renameSync9, statSync as statSync8, writeFileSync as writeFileSync12 } from "fs";
91862
92584
  import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
91863
92585
  var STORE_FILE = "remote-command-resource-grants.json";
@@ -91952,7 +92674,7 @@ function resolveRemoteCommandResourceGrants(context) {
91952
92674
  return { writable_targets, network_profiles, credential_profiles };
91953
92675
  }
91954
92676
  function definitionDigest(kind, definition) {
91955
- return createHash28("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
92677
+ return createHash29("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
91956
92678
  }
91957
92679
  function upsert(grant) {
91958
92680
  validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
@@ -93122,7 +93844,7 @@ function readRemoteAccessTextFile(options) {
93122
93844
  truncated,
93123
93845
  sizeBytes: stats.size,
93124
93846
  lineCount: countTextLines(content),
93125
- expected_base: truncated ? null : createHash29("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
93847
+ expected_base: truncated ? null : createHash30("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
93126
93848
  };
93127
93849
  } catch (error) {
93128
93850
  const code = error.code;
@@ -93172,7 +93894,7 @@ function readRemoteAccessImageChunk(options) {
93172
93894
  mime_type: mimeType,
93173
93895
  size_bytes: stats.size,
93174
93896
  offset: options.offset,
93175
- content_hash: createHash29("sha256").update(bytes2).digest("hex")
93897
+ content_hash: createHash30("sha256").update(bytes2).digest("hex")
93176
93898
  };
93177
93899
  } catch (error) {
93178
93900
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -93220,8 +93942,8 @@ function readRemoteAccessFileChunk(options) {
93220
93942
  content_base64: bytes2.toString("base64"),
93221
93943
  size_bytes: stats.size,
93222
93944
  offset: options.offset,
93223
- file_identity: createHash29("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
93224
- chunk_hash: createHash29("sha256").update(bytes2).digest("hex")
93945
+ file_identity: createHash30("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
93946
+ chunk_hash: createHash30("sha256").update(bytes2).digest("hex")
93225
93947
  };
93226
93948
  } catch (error) {
93227
93949
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -94783,7 +95505,7 @@ function writeWorkRecoveryAtomically(path2, document) {
94783
95505
 
94784
95506
  // src/revolutBusinessCertificate.ts
94785
95507
  import { execFileSync as execFileSync6 } from "child_process";
94786
- import { createSign, randomUUID as randomUUID22 } from "crypto";
95508
+ import { createSign, randomUUID as randomUUID23 } from "crypto";
94787
95509
  import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
94788
95510
  import { homedir as homedir8 } from "os";
94789
95511
  import { join as join28, resolve as resolve17 } from "path";
@@ -94857,7 +95579,7 @@ function generateRevolutBusinessClientAssertion(options) {
94857
95579
  aud: REVOLUT_BUSINESS_AUDIENCE,
94858
95580
  exp: now + 3600,
94859
95581
  iat: now,
94860
- jti: randomUUID22()
95582
+ jti: randomUUID23()
94861
95583
  });
94862
95584
  const unsigned = `${header2}.${payload}`;
94863
95585
  const signer = createSign("RSA-SHA256");
@@ -96230,6 +96952,7 @@ async function tryResolveWorkflow(client2, target, flags) {
96230
96952
  return singleExactNameMatch(workflows, target, (workflow) => workflow.title);
96231
96953
  }
96232
96954
  async function requiredResolvedWorkflowId(client2, target, flags, _action) {
96955
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(target)) return target;
96233
96956
  const workflow = await tryResolveWorkflow(client2, target, flags);
96234
96957
  if (!workflow) throw new Error(`Workflow '${target}' not found.`);
96235
96958
  return workflow.id;
@@ -96329,7 +97052,7 @@ async function resolveAddToProjectStorageChoice(client2, project, flags, objectT
96329
97052
  async function createEncryptedProjectItem(client2, project, input) {
96330
97053
  const timestamp = nowSeconds3();
96331
97054
  return client2.createProjectItem(project.projectId, {
96332
- project_item_id: randomUUID23(),
97055
+ project_item_id: randomUUID24(),
96333
97056
  folder_id: input.folderId ?? null,
96334
97057
  item_type: input.itemType,
96335
97058
  target_id: input.targetId,
@@ -96528,7 +97251,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
96528
97251
  const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
96529
97252
  if (action === "create" || action === "add") {
96530
97253
  const assumption = await client2.createPlanAssumption(plan.planId, {
96531
- assumption_id: typeof flags.id === "string" ? flags.id : randomUUID23(),
97254
+ assumption_id: typeof flags.id === "string" ? flags.id : randomUUID24(),
96532
97255
  encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
96533
97256
  status: typeof flags.status === "string" ? flags.status : void 0,
96534
97257
  linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
@@ -96570,7 +97293,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
96570
97293
  if (action === "diff") throw new Error("Revision diffs are reviewed in the authenticated web review page; use submit-for-review to print its URL.");
96571
97294
  if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
96572
97295
  const snapshot2 = JSON.stringify(planToJson(plan));
96573
- const revision = await client2.submitPlanRevision(plan.planId, { fingerprint: createHash30("sha256").update(snapshot2).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot2), created_at: nowSeconds3() });
97296
+ const revision = await client2.submitPlanRevision(plan.planId, { fingerprint: createHash31("sha256").update(snapshot2).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot2), created_at: nowSeconds3() });
96574
97297
  const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
96575
97298
  if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
96576
97299
  else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
@@ -97464,7 +98187,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
97464
98187
  const writeMode = await resolveProjectWriteMode(flags, `Choose how OpenMates may change files in ${JSON.stringify(name)}:`);
97465
98188
  const projectKey = randomBytes14(32);
97466
98189
  const wrapping = await client2.projectWrappingKey(context);
97467
- const projectId = randomUUID23();
98190
+ const projectId = randomUUID24();
97468
98191
  const timestamp = nowSeconds3();
97469
98192
  const defaultFocus = buildProjectDefaultFocus(name);
97470
98193
  const slugMetadata = await buildEncryptedObjectSlugMetadata({
@@ -97490,7 +98213,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
97490
98213
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
97491
98214
  key_wrappers: wrapping.teamId ? [{
97492
98215
  key_type: "team",
97493
- hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
98216
+ hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
97494
98217
  team_key_epoch: 1,
97495
98218
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
97496
98219
  wrapper_version: 1,
@@ -97730,7 +98453,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
97730
98453
  const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client2.planProjectAsk({ instruction }), "proposed_project");
97731
98454
  const projectKey = randomBytes14(32);
97732
98455
  const timestamp = Math.floor(Date.now() / 1e3);
97733
- const projectId = randomUUID23();
98456
+ const projectId = randomUUID24();
97734
98457
  const name = requiredString(proposal, "name", instruction);
97735
98458
  const slugMetadata = await buildEncryptedObjectSlugMetadata({
97736
98459
  value: typeof flags.slug === "string" ? flags.slug : name,
@@ -97853,8 +98576,8 @@ async function handleProjectFiles(client2, masterKey, rest, flags, context) {
97853
98576
  ...flags["include-ignored"] === true ? {
97854
98577
  approvedIgnoredRead: {
97855
98578
  path: String(argumentsValue.path),
97856
- chatId: `cli-direct-${randomUUID23()}`,
97857
- operationId: `cli-direct-${randomUUID23()}`
98579
+ chatId: `cli-direct-${randomUUID24()}`,
98580
+ operationId: `cli-direct-${randomUUID24()}`
97858
98581
  }
97859
98582
  } : {}
97860
98583
  });
@@ -98642,7 +99365,7 @@ async function handleRemoteAccess(client2, subcommand, rest, flags) {
98642
99365
  const projects = await loadProjects(client2, masterKey, hostingFlags, hostingContext);
98643
99366
  const bindings = await resolveRemoteAccessBindings(client2, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
98644
99367
  if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
98645
- const sourceSessionId = randomUUID23();
99368
+ const sourceSessionId = randomUUID24();
98646
99369
  const controller = new AbortController();
98647
99370
  const stop = () => controller.abort();
98648
99371
  process.once("SIGINT", stop);
@@ -98796,7 +99519,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
98796
99519
  updated_at: nowSeconds3()
98797
99520
  }, context);
98798
99521
  }
98799
- resolved.push({ rootPath, project, sourceId: randomUUID23() });
99522
+ resolved.push({ rootPath, project, sourceId: randomUUID24() });
98800
99523
  }
98801
99524
  }
98802
99525
  const bindings = [];
@@ -98840,7 +99563,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
98840
99563
  }
98841
99564
  async function createEncryptedRemoteAccessProject(client2, name, rootPath, writeMode, context) {
98842
99565
  const projectKey = randomBytes14(32);
98843
- const projectId = randomUUID23();
99566
+ const projectId = randomUUID24();
98844
99567
  const timestamp = Math.floor(Date.now() / 1e3);
98845
99568
  const wrapping = await client2.projectWrappingKey(context);
98846
99569
  const importedInstructions = readRootProjectInstructions(rootPath);
@@ -98866,7 +99589,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
98866
99589
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
98867
99590
  key_wrappers: wrapping.teamId ? [{
98868
99591
  key_type: "team",
98869
- hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
99592
+ hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
98870
99593
  team_key_epoch: 1,
98871
99594
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
98872
99595
  wrapper_version: 1,
@@ -98924,7 +99647,7 @@ async function ensureProjectDefaultFocus(client2, project, context, rootPath) {
98924
99647
  function buildProjectDefaultFocus(projectName, instructions, source = "generated") {
98925
99648
  const name = projectName.trim() || "Untitled project";
98926
99649
  return {
98927
- focus_id: randomUUID23(),
99650
+ focus_id: randomUUID24(),
98928
99651
  name: `Work on ${name}`,
98929
99652
  instructions: instructions?.trim() || `Help with work in ${name}. Follow the user's instructions and the Project's connected source guidance.`,
98930
99653
  source
@@ -100257,7 +100980,7 @@ function normalizeApiKeyLoadedChat(payload) {
100257
100980
  const messages = rawMessages.map((message) => {
100258
100981
  const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
100259
100982
  const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
100260
- const hashedMessageId = messageId ? createHash30("sha256").update(messageId).digest("hex") : null;
100983
+ const hashedMessageId = messageId ? createHash31("sha256").update(messageId).digest("hex") : null;
100261
100984
  const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
100262
100985
  return {
100263
100986
  id: messageId,
@@ -100805,6 +101528,52 @@ async function handleWorkflows(client2, subcommand, rest, flags, redactor) {
100805
101528
  }
100806
101529
  return;
100807
101530
  }
101531
+ if (subcommand === "save-as-reusable") {
101532
+ const workflowId = rest[0] ? await requiredResolvedWorkflowId(client2, rest[0], flags, "save-as-reusable") : void 0;
101533
+ if (!workflowId) throw new Error("Missing workflow ID. Example: openmates workflows save-as-reusable <id>");
101534
+ const idempotencyKey = typeof flags["idempotency-key"] === "string" ? flags["idempotency-key"] : "";
101535
+ if (!idempotencyKey) throw new Error("Missing --idempotency-key for workflow save-as-reusable.");
101536
+ const workflow = await client2.saveWorkflowAsReusable(workflowId, { idempotencyKey });
101537
+ if (flags.json === true) printJson2(workflow);
101538
+ else printWorkflowDetail(workflow);
101539
+ return;
101540
+ }
101541
+ const runRouting = () => ({
101542
+ ...typeof flags["source-chat"] === "string" ? { sourceChatId: flags["source-chat"] } : {},
101543
+ ...typeof flags["message-destination-overrides"] === "string" ? { messageDestinationOverrides: parseJsonFlag(flags["message-destination-overrides"], "--message-destination-overrides") } : {},
101544
+ ...typeof flags["return-outputs"] === "string" ? { returnOutputs: parseJsonFlag(flags["return-outputs"], "--return-outputs") } : {}
101545
+ });
101546
+ if (subcommand === "run-once") {
101547
+ const titleFlag = typeof flags.title === "string" ? flags.title.trim() : "";
101548
+ const graphFlag = typeof flags.graph === "string" ? flags.graph : "";
101549
+ const fileFlag = typeof flags.file === "string" ? flags.file : "";
101550
+ if (Boolean(graphFlag) === Boolean(fileFlag)) throw new Error("Specify exactly one of --graph JSON or --file definition.workflow.yml for run-once.");
101551
+ let graph;
101552
+ let title = titleFlag;
101553
+ if (fileFlag) {
101554
+ assertWorkflowFilePath(fileFlag);
101555
+ if (statSync11(fileFlag).size > WORKFLOW_FILE_MAX_BYTES) throw new Error("Workflow file exceeds the size limit.");
101556
+ const yaml = parseYamlDocument(readFileSync24(fileFlag, "utf8"), { uniqueKeys: true, strict: true });
101557
+ if (yaml.errors.length > 0) throw new Error(`Invalid workflow YAML: ${yaml.errors[0].message}`);
101558
+ const document = validateWorkflowFile(yaml.toJS({ maxAliasCount: 20 }));
101559
+ graph = document.workflow.graph;
101560
+ title ||= document.workflow.title;
101561
+ } else graph = parseJsonFlag(graphFlag, "--graph");
101562
+ if (!title) throw new Error("Missing --title for workflow run-once.");
101563
+ const idempotencyKey = typeof flags["idempotency-key"] === "string" ? flags["idempotency-key"] : "";
101564
+ if (!idempotencyKey) throw new Error("Missing --idempotency-key for workflow run-once.");
101565
+ const sourceChatId = flags["source-chat"];
101566
+ if (typeof sourceChatId !== "string" || !sourceChatId) throw new Error("Missing --source-chat for workflow run-once.");
101567
+ const input = typeof flags.input === "string" ? parseJsonFlag(flags.input, "--input") : {};
101568
+ const result = await client2.runWorkflowOnce({ title, graph, idempotencyKey, input, ...runRouting(), sourceChatId });
101569
+ if (flags.wait === true) result.run = await waitForWorkflowRun(client2, result.workflow.id, result.run);
101570
+ if (flags.json === true) printJson2(result);
101571
+ else {
101572
+ printWorkflowDetail(result.workflow);
101573
+ printWorkflowRun(result.run);
101574
+ }
101575
+ return;
101576
+ }
100808
101577
  if (subcommand === "run") {
100809
101578
  const workflowId = rest[0] ? await requiredResolvedWorkflowId(client2, rest[0], flags, "run") : void 0;
100810
101579
  if (!workflowId) throw new Error("Missing workflow ID. Example: openmates workflows run <id>");
@@ -100812,7 +101581,7 @@ async function handleWorkflows(client2, subcommand, rest, flags, redactor) {
100812
101581
  if (!idempotencyKey) throw new Error("Missing --idempotency-key. Reuse this stable key when retrying the same workflow run.");
100813
101582
  const mode = flags.mode === "test" ? "test" : "manual";
100814
101583
  const input = typeof flags.input === "string" ? parseJsonFlag(flags.input, "--input") : {};
100815
- let run = await client2.runWorkflow(workflowId, { idempotencyKey, mode, input });
101584
+ let run = await client2.runWorkflow(workflowId, { idempotencyKey, mode, input, ...runRouting() });
100816
101585
  if (flags.wait === true) {
100817
101586
  run = await waitForWorkflowRun(client2, workflowId, run);
100818
101587
  }
@@ -102454,7 +103223,7 @@ async function runAccountImport(client2, parserFormat, file, flags) {
102454
103223
  if (flags.yes !== true) {
102455
103224
  await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
102456
103225
  }
102457
- const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID23();
103226
+ const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID24();
102458
103227
  const selectedChats = parsed.chats.slice(0, selectedCount);
102459
103228
  const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
102460
103229
  const confirmation = await client2.confirmAccountImport(importId, selectedFingerprints);
@@ -102948,8 +103717,8 @@ async function handleFinance(client2, subcommand, _rest, flags) {
102948
103717
  refresh_token_envelope: connectedAccount.refreshTokenBundle
102949
103718
  }
102950
103719
  ] : [],
102951
- chatId: `cli-finance-${randomUUID23()}`,
102952
- messageId: `cli-finance-message-${randomUUID23()}`,
103720
+ chatId: `cli-finance-${randomUUID24()}`,
103721
+ messageId: `cli-finance-message-${randomUUID24()}`,
102953
103722
  apiKey: resolveApiKey(flags) ?? void 0,
102954
103723
  promptInjectionProtection: false
102955
103724
  });
@@ -107174,7 +107943,9 @@ function printWorkflowsHelp() {
107174
107943
  openmates workflows show <workflow-id> [--json]
107175
107944
  openmates workflows enable <workflow-id> [--json]
107176
107945
  openmates workflows disable <workflow-id> [--json]
107177
- openmates workflows run <workflow-id> --idempotency-key <stable-key> [--mode manual|test] [--input '<json>'] [--wait] [--json]
107946
+ openmates workflows run <workflow-id> --idempotency-key <stable-key> [--mode manual|test] [--input '<json>'] [--source-chat <id>] [--message-destination-overrides '<json>'] [--return-outputs '<json>'] [--wait] [--json]
107947
+ openmates workflows run-once (--title <title> --graph '<json>' | --file definition.workflow.yml) --source-chat <id> --idempotency-key <stable-key> [--input '<json>'] [--message-destination-overrides '<json>'] [--return-outputs '<json>'] [--wait] [--json]
107948
+ openmates workflows save-as-reusable <workflow-id> --idempotency-key <stable-key> [--json]
107178
107949
  openmates workflows runs <workflow-id> [--json]
107179
107950
  openmates workflows run-show <workflow-id> <run-id> [--json]
107180
107951
  openmates workflows run-cancel <workflow-id> <run-id> [--json]
@@ -107189,6 +107960,9 @@ Workflows run on the OpenMates server, not in this terminal process. The CLI
107189
107960
  uses your paired session and shows the same workflow/run records as web, SDKs,
107190
107961
  and Apple clients.
107191
107962
  Export writes a saved workflow's portable authoring graph to .workflow.yml.
107963
+ Run-once keeps its definition with the source chat. Save-as-reusable creates a new disabled workflow.
107964
+ --message-destination-overrides maps Send step IDs to owned chat IDs.
107965
+ --return-outputs maps result names to {"ref":"$nodes.<step>.output.<field>","type":"string"}.
107192
107966
  Run history and account bindings are excluded. Import creates a disabled workflow;
107193
107967
  review and complete its bindings before enabling it.
107194
107968
  Import creates a Personal Workflow. In an active Team context, pass --personal