openmates 0.27.0-alpha.6 → 0.27.0-alpha.7

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,
@@ -17357,7 +17411,7 @@ var OpenMatesClient = class _OpenMatesClient {
17357
17411
  this.requireSession();
17358
17412
  const response = await this.http.post(
17359
17413
  "/v1/projects/ask/plan",
17360
- { instruction: input.instruction },
17414
+ { instruction: input.instruction, ...input.chatTitles !== void 0 ? { chat_titles: input.chatTitles } : {} },
17361
17415
  this.getCliRequestHeaders()
17362
17416
  );
17363
17417
  if (!response.ok) throw new Error(`Project ask planning failed with HTTP ${response.status}`);
@@ -17366,7 +17420,7 @@ var OpenMatesClient = class _OpenMatesClient {
17366
17420
  async listProjectItems(projectId, options = {}) {
17367
17421
  this.requireSession();
17368
17422
  const response = await this.http.get(
17369
- this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
17423
+ this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items${options.chatOnly ? "?chat_only=true" : ""}`, options),
17370
17424
  this.getCliRequestHeaders()
17371
17425
  );
17372
17426
  if (!response.ok) throw this.projectRequestError("item list", response);
@@ -17503,10 +17557,10 @@ var OpenMatesClient = class _OpenMatesClient {
17503
17557
  }
17504
17558
  return { ws: opened.ws, ownerId: opened.ownerId };
17505
17559
  }
17506
- async createProjectItem(projectId, input) {
17560
+ async createProjectItem(projectId, input, options = {}) {
17507
17561
  this.requireSession();
17508
17562
  const response = await this.http.post(
17509
- `/v1/projects/${encodeURIComponent(projectId)}/items`,
17563
+ this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
17510
17564
  input,
17511
17565
  this.getCliRequestHeaders()
17512
17566
  );
@@ -17515,6 +17569,20 @@ var OpenMatesClient = class _OpenMatesClient {
17515
17569
  }
17516
17570
  return response.data.item;
17517
17571
  }
17572
+ async moveProjectItemToFolder(projectId, itemId, folderId, options = {}) {
17573
+ this.requireSession();
17574
+ const response = await this.http.patch(
17575
+ this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items/${encodeURIComponent(itemId)}`, options),
17576
+ { folder_id: folderId, updated_at: Math.floor(Date.now() / 1e3) },
17577
+ this.getCliRequestHeaders()
17578
+ );
17579
+ if (!response.ok) throw this.projectRequestError("folder move", response);
17580
+ }
17581
+ async createProjectFolder(projectId, input, options = {}) {
17582
+ this.requireSession();
17583
+ const response = await this.http.post(this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/folders`, options), input, this.getCliRequestHeaders());
17584
+ if (!response.ok) throw this.projectRequestError("folder create", response);
17585
+ }
17518
17586
  async deleteProjectItemByTarget(projectId, itemType, targetId, options = {}) {
17519
17587
  this.requireSession();
17520
17588
  const params = new URLSearchParams({ item_type: itemType, target_id: targetId });
@@ -19150,7 +19218,7 @@ Required: ${schema.required.join(", ")}`
19150
19218
  const session = this.requireSession();
19151
19219
  const masterKey = base64ToBytes(session.masterKeyExportedB64);
19152
19220
  const cache = await this.ensureSynced();
19153
- const { createHash: createHash31 } = await import("crypto");
19221
+ const { createHash: createHash32 } = await import("crypto");
19154
19222
  const embed = cache.embeds.find(
19155
19223
  (e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
19156
19224
  );
@@ -19158,7 +19226,7 @@ Required: ${schema.required.join(", ")}`
19158
19226
  throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
19159
19227
  }
19160
19228
  const embedId = String(embed.embed_id ?? embed.id ?? "");
19161
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
19229
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
19162
19230
  const embedKeyBytes = await this.resolveEmbedKey(
19163
19231
  cache,
19164
19232
  masterKey,
@@ -19266,8 +19334,8 @@ Required: ${schema.required.join(", ")}`
19266
19334
  if (!embed) {
19267
19335
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
19268
19336
  }
19269
- const { createHash: createHash31 } = await import("crypto");
19270
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
19337
+ const { createHash: createHash32 } = await import("crypto");
19338
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
19271
19339
  const embedKey = await this.resolveEmbedKey(
19272
19340
  cache,
19273
19341
  masterKey,
@@ -19383,8 +19451,8 @@ Required: ${schema.required.join(", ")}`
19383
19451
  if (!embed) {
19384
19452
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
19385
19453
  }
19386
- const { createHash: createHash31 } = await import("crypto");
19387
- const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
19454
+ const { createHash: createHash32 } = await import("crypto");
19455
+ const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
19388
19456
  const embedKey = await this.resolveEmbedKey(
19389
19457
  cache,
19390
19458
  masterKey,
@@ -19916,9 +19984,9 @@ Required: ${schema.required.join(", ")}`
19916
19984
  chats.splice(0, chats.length, ...reconciledChats);
19917
19985
  if (totalChatCount === 0) totalChatCount = chats.length;
19918
19986
  if (reconciliation.deleted_chat_ids?.length) {
19919
- const { createHash: createHash31 } = await import("crypto");
19987
+ const { createHash: createHash32 } = await import("crypto");
19920
19988
  const deletedHashes = new Set(
19921
- reconciliation.deleted_chat_ids.map((id) => createHash31("sha256").update(id).digest("hex"))
19989
+ reconciliation.deleted_chat_ids.map((id) => createHash32("sha256").update(id).digest("hex"))
19922
19990
  );
19923
19991
  const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
19924
19992
  const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
@@ -31158,7 +31226,7 @@ async function flushPendingTaskActivities(client2, teamId, stateDir2 = resolveSt
31158
31226
  import { existsSync as existsSync26, readFileSync as readFileSync24, realpathSync as realpathSync12, readdirSync as readdirSync14, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
31159
31227
  import { fileURLToPath as fileURLToPath4 } from "url";
31160
31228
  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";
31229
+ import { createHash as createHash31, randomBytes as randomBytes14, randomUUID as randomUUID24 } from "crypto";
31162
31230
  import { arch as arch2, platform as platform3 } from "os";
31163
31231
  import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
31164
31232
 
@@ -62135,6 +62203,64 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
62135
62203
  }
62136
62204
  },
62137
62205
  chats: {
62206
+ projects: {
62207
+ move: {
62208
+ text: "Move to project"
62209
+ },
62210
+ move_here: {
62211
+ text: "Move here"
62212
+ },
62213
+ add: {
62214
+ text: "Add to project"
62215
+ },
62216
+ create: {
62217
+ text: "Create new project"
62218
+ },
62219
+ add_here: {
62220
+ text: "Add here"
62221
+ },
62222
+ new_folder: {
62223
+ text: "New subfolder"
62224
+ },
62225
+ folder_name: {
62226
+ text: "Folder name"
62227
+ },
62228
+ path: {
62229
+ text: "Project path"
62230
+ },
62231
+ up: {
62232
+ text: "Go up one level"
62233
+ },
62234
+ ancestors: {
62235
+ text: "Show full path"
62236
+ },
62237
+ open: {
62238
+ text: "Open project"
62239
+ },
62240
+ organizing: {
62241
+ text: "Organizing chats\u2026"
62242
+ },
62243
+ error: {
62244
+ text: "Could not organize chats. Please try again."
62245
+ }
62246
+ },
62247
+ activity: {
62248
+ heading: {
62249
+ text: "Active chats"
62250
+ },
62251
+ count: {
62252
+ text: "{count} chats active\u2026"
62253
+ },
62254
+ count_single: {
62255
+ text: "1 chat active\u2026"
62256
+ },
62257
+ subchats: {
62258
+ text: "{count} subchats running"
62259
+ },
62260
+ subchats_single: {
62261
+ text: "1 subchat running"
62262
+ }
62263
+ },
62138
62264
  chat: {
62139
62265
  welcome: {
62140
62266
  back_to_recent: {
@@ -83994,7 +84120,7 @@ function formatMetricDelta(delta, suffix) {
83994
84120
  // src/tui.ts
83995
84121
  import { createInterface as createInterface4 } from "readline/promises";
83996
84122
  import { stdin as nodeStdin, stdout as nodeStdout } from "process";
83997
- import { randomUUID as randomUUID20 } from "crypto";
84123
+ import { randomUUID as randomUUID21 } from "crypto";
83998
84124
 
83999
84125
  // src/tuiExampleContinuation.ts
84000
84126
  function buildExampleContinuationHistory(conversation) {
@@ -84687,13 +84813,13 @@ function storedPath(metadata, displayName) {
84687
84813
  return null;
84688
84814
  }
84689
84815
  }
84690
- async function loadTuiProject(client2, id) {
84816
+ async function loadTuiProject(client2, id, chatOnly = false) {
84691
84817
  const context = contextFor(client2);
84692
84818
  const detail = await client2.getProject(id, context);
84693
84819
  const project = await projectFromRecord(client2, detail.project, context);
84694
84820
  const [itemsResponse, sourceRecords] = await Promise.all([
84695
- client2.listProjectItems(id, context),
84696
- client2.listProjectSources(id, context)
84821
+ client2.listProjectItems(id, { ...context, chatOnly }),
84822
+ chatOnly ? Promise.resolve([]) : client2.listProjectSources(id, context)
84697
84823
  ]);
84698
84824
  const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
84699
84825
  const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
@@ -84927,7 +85053,7 @@ function buildProjectForm() {
84927
85053
  ]
84928
85054
  };
84929
85055
  }
84930
- async function submitProjectForm(client2, form) {
85056
+ async function submitProjectForm(client2, form, chatOrganizationOnly = false) {
84931
85057
  const name = formValue(form, "name").trim();
84932
85058
  if (!name) throw new Error("Project name is required.");
84933
85059
  const writeMode = formValue(form, "write_mode");
@@ -84951,7 +85077,7 @@ async function submitProjectForm(client2, form) {
84951
85077
  created_at: timestamp,
84952
85078
  updated_at: timestamp,
84953
85079
  last_opened_at: timestamp,
84954
- write_mode: writeMode,
85080
+ write_mode: chatOrganizationOnly ? null : writeMode,
84955
85081
  default_focus_id: focus.focus_id,
84956
85082
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
84957
85083
  key_wrappers: wrapping.teamId ? [{
@@ -84963,7 +85089,7 @@ async function submitProjectForm(client2, form) {
84963
85089
  created_at: timestamp
84964
85090
  }] : []
84965
85091
  };
84966
- const result = await client2.createProject(payload, context);
85092
+ const result = await client2.createProject({ ...payload, ...chatOrganizationOnly ? { chat_organization_only: true } : {} }, context);
84967
85093
  return projectFromRecord(client2, { ...payload, ...result.project ?? {} }, context);
84968
85094
  }
84969
85095
 
@@ -85538,6 +85664,10 @@ var TUI_ACTIONS = [
85538
85664
  { label: "Move selected task", command: "/task-status" },
85539
85665
  { label: "Add task activity", command: "/task-activity" },
85540
85666
  { label: "Create Project", command: "/project-create" },
85667
+ { label: "Add chat to project", command: "/chat-add-to-project" },
85668
+ { label: "Move chat to project", command: "/chat-move-to-project" },
85669
+ { label: "Create project from chats", command: "/chat-create-project" },
85670
+ { label: "Show active chats", command: "/active" },
85541
85671
  { label: "New chat in Project", command: "/project-chat" },
85542
85672
  { label: "Edit workflow step", command: "/workflow-edit" },
85543
85673
  { label: "Enable or disable workflow", command: "/workflow-toggle" },
@@ -85552,6 +85682,259 @@ function paletteActions(query) {
85552
85682
  return TUI_ACTIONS.filter((item) => `${item.label} ${item.command}`.toLowerCase().includes(needle));
85553
85683
  }
85554
85684
 
85685
+ // src/tuiChatSidebar.ts
85686
+ import { createHash as createHash22, randomUUID as randomUUID18 } from "crypto";
85687
+
85688
+ // ../ui/src/utils/chatActivity.ts
85689
+ function processingAncestorIds(chats, runningIds) {
85690
+ const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
85691
+ const result = /* @__PURE__ */ new Set();
85692
+ for (const id of runningIds) {
85693
+ const ancestry = [];
85694
+ const visited = /* @__PURE__ */ new Set();
85695
+ let chat = byId.get(id);
85696
+ while (chat && !visited.has(chat.chat_id)) {
85697
+ visited.add(chat.chat_id);
85698
+ ancestry.push(chat);
85699
+ chat = chat.parent_id ? byId.get(chat.parent_id) : void 0;
85700
+ }
85701
+ if (ancestry.some((chat2) => chat2.is_hidden || chat2.is_hidden_candidate)) continue;
85702
+ for (const ancestor of ancestry) result.add(ancestor.chat_id);
85703
+ }
85704
+ return result;
85705
+ }
85706
+ function aggregateRunningChats(chats, runningIds) {
85707
+ const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
85708
+ const groups = /* @__PURE__ */ new Map();
85709
+ for (const id of runningIds) {
85710
+ let root = byId.get(id);
85711
+ if (!root) continue;
85712
+ const visited = /* @__PURE__ */ new Set([id]);
85713
+ let hidden = root.is_hidden || root.is_hidden_candidate;
85714
+ while (root.parent_id && byId.has(root.parent_id) && !visited.has(root.parent_id)) {
85715
+ visited.add(root.parent_id);
85716
+ root = byId.get(root.parent_id);
85717
+ hidden ||= root.is_hidden || root.is_hidden_candidate;
85718
+ }
85719
+ if (hidden) continue;
85720
+ let group = groups.get(root.chat_id);
85721
+ if (!group) {
85722
+ group = { chat: root, activeSubChatCount: 0 };
85723
+ groups.set(root.chat_id, group);
85724
+ }
85725
+ if (id !== root.chat_id) group.activeSubChatCount += 1;
85726
+ }
85727
+ return [...groups.values()];
85728
+ }
85729
+
85730
+ // ../ui/src/utils/chatProjectNavigation.ts
85731
+ function projectBreadcrumbs(project, folderId) {
85732
+ const ancestors = [];
85733
+ const visited = /* @__PURE__ */ new Set();
85734
+ let folder = project.folders.find((folder2) => folder2.id === folderId);
85735
+ while (folder && !visited.has(folder.id)) {
85736
+ visited.add(folder.id);
85737
+ ancestors.unshift({ id: folder.id, name: folder.name });
85738
+ folder = project.folders.find((candidate) => candidate.hash === folder.parentHash);
85739
+ }
85740
+ return [{ id: null, name: project.name }, ...ancestors];
85741
+ }
85742
+ function projectFolderChatIds(project, folderId, recursive = false) {
85743
+ const folder = project.folders.find((folder2) => folder2.id === folderId);
85744
+ if (folderId !== null && !folder) return /* @__PURE__ */ new Set();
85745
+ const hashes = /* @__PURE__ */ new Set([folder?.hash ?? null]);
85746
+ if (recursive) {
85747
+ let previous = -1;
85748
+ while (previous !== hashes.size) {
85749
+ previous = hashes.size;
85750
+ for (const candidate of project.folders) if (hashes.has(candidate.parentHash)) hashes.add(candidate.hash);
85751
+ }
85752
+ }
85753
+ return new Set(project.chats.filter((chat) => hashes.has(chat.folderHash)).map((chat) => chat.chatId));
85754
+ }
85755
+ function locationHasRunningChats(project, folderId, runningIds) {
85756
+ return [...projectFolderChatIds(project, folderId, true)].some((id) => runningIds.has(id));
85757
+ }
85758
+
85759
+ // src/tuiChatSidebar.ts
85760
+ function rowKey(row) {
85761
+ return [row.kind, row.chatId ?? "", row.projectId ?? "", row.folderId ?? ""].join(":");
85762
+ }
85763
+ function updateTuiChatSidebar(state, update) {
85764
+ const selected = tuiChatSidebarRows(state)[state.sidebarIndex];
85765
+ update();
85766
+ if (state.workspace !== "chats" || state.focus !== "sidebar") return;
85767
+ const rows2 = tuiChatSidebarRows(state);
85768
+ const index = selected ? rows2.findIndex((row) => rowKey(row) === rowKey(selected)) : -1;
85769
+ state.sidebarIndex = index >= 0 ? index : Math.max(0, Math.min(state.sidebarIndex, rows2.length - 1));
85770
+ }
85771
+ function runningTuiChatGroups(state) {
85772
+ const chats = new Map([...state.recentChats, ...state.activityChats].map((chat) => [chat.id, chat]));
85773
+ 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) }));
85774
+ }
85775
+ function tuiSidebarProjects(state) {
85776
+ return state.chatSidebarProjects.map((project) => ({
85777
+ id: project.id,
85778
+ name: project.name,
85779
+ folders: project.folders.map((folder) => ({
85780
+ id: folder.id,
85781
+ name: folder.name,
85782
+ hash: createHash22("sha256").update(folder.id).digest("hex"),
85783
+ parentHash: folder.parentHash ?? null
85784
+ })),
85785
+ chats: project.items.filter((item) => item.type === "chat").map((item) => ({
85786
+ id: item.id,
85787
+ chatId: item.targetId,
85788
+ folderHash: item.folderId ? createHash22("sha256").update(item.folderId).digest("hex") : null
85789
+ }))
85790
+ }));
85791
+ }
85792
+ function tuiChatBreadcrumb(state, width = 23) {
85793
+ const location = state.chatSidebarLocation;
85794
+ const project = tuiSidebarProjects(state).find((project2) => project2.id === location?.projectId);
85795
+ if (!project || !location) return null;
85796
+ const crumbs = projectBreadcrumbs(project, location.folderId);
85797
+ if (crumbs.length === 1) return truncateCells(project.name, width);
85798
+ const separator = crumbs.length > 2 ? " \u203A \u2026 \u203A " : " \u203A ";
85799
+ const current = crumbs.at(-1).name, budget = Math.max(2, width - cells(separator));
85800
+ if (cells(project.name) + cells(current) <= budget) return `${project.name}${separator}${current}`;
85801
+ const leafWidth = Math.min(cells(current), Math.max(1, budget - Math.min(cells(project.name), Math.ceil(budget / 2))));
85802
+ return `${truncateCells(project.name, budget - leafWidth)}${separator}${truncateCells(current, leafWidth)}`;
85803
+ }
85804
+ function tuiChatSidebarRows(state) {
85805
+ const groups = runningTuiChatGroups(state);
85806
+ const records = [...state.recentChats, ...state.activityChats].map((chat) => ({ chat_id: chat.id, parent_id: chat.parentId, is_hidden_candidate: chat.isHiddenCandidate }));
85807
+ const running = processingAncestorIds(records, new Set(state.runningChatIds));
85808
+ const rows2 = groups.map((group) => ({
85809
+ kind: "chat",
85810
+ chatId: group.chat.id,
85811
+ running: true,
85812
+ label: `${group.chat.title || "Untitled chat"}${group.activeSubChatCount ? ` (${group.activeSubChatCount} ${group.activeSubChatCount === 1 ? "subchat" : "subchats"})` : ""}`
85813
+ }));
85814
+ rows2.push({ kind: "new", label: "+ New chat" });
85815
+ if (state.chatProjectOperation) rows2.push({ kind: "create", label: "+ Create new project" });
85816
+ const projects = tuiSidebarProjects(state), location = state.chatSidebarLocation;
85817
+ const project = projects.find((project2) => project2.id === location?.projectId);
85818
+ let contents = null;
85819
+ if (project && location) {
85820
+ const crumbs = projectBreadcrumbs(project, location.folderId);
85821
+ rows2.push({ kind: "up", label: "\u2039 Up one level", projectId: project.id, folderId: crumbs.length > 1 ? crumbs[crumbs.length - 2].id : void 0 });
85822
+ if (crumbs.length > 2) rows2.push({ kind: "path", label: "\u2026 Show full path" });
85823
+ if (state.chatSidebarAncestors) for (const crumb of crumbs) rows2.push({ kind: "folder", label: crumb.name, projectId: project.id, folderId: crumb.id });
85824
+ if (state.chatProjectOperation) rows2.push({ kind: "choose", label: state.chatProjectOperation.mode === "move" ? "Move here" : "Add here", projectId: project.id, folderId: location.folderId });
85825
+ const hash2 = project.folders.find((folder) => folder.id === location.folderId)?.hash ?? null;
85826
+ rows2.push(...project.folders.filter((folder) => folder.parentHash === hash2).map((folder) => ({
85827
+ kind: "folder",
85828
+ label: folder.name,
85829
+ projectId: project.id,
85830
+ folderId: folder.id,
85831
+ running: locationHasRunningChats(project, folder.id, running)
85832
+ })));
85833
+ contents = projectFolderChatIds(project, location.folderId);
85834
+ } else rows2.push(...projects.map((project2) => ({
85835
+ kind: "project",
85836
+ label: project2.name,
85837
+ projectId: project2.id,
85838
+ running: locationHasRunningChats(project2, null, running)
85839
+ })));
85840
+ const organized = new Set(projects.flatMap((project2) => project2.chats.map((chat) => chat.chatId)));
85841
+ 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 }]));
85842
+ if (contents && location) for (const item of state.chatSidebarProjects.find((project2) => project2.id === location.projectId)?.items ?? []) {
85843
+ const verified = candidates.get(item.targetId);
85844
+ if (item.type === "chat" && contents.has(item.targetId) && verified && !verified.title)
85845
+ candidates.set(item.targetId, { ...verified, title: item.name });
85846
+ }
85847
+ 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" })));
85848
+ return rows2;
85849
+ }
85850
+ async function refreshTuiChatSidebar(state, client2, render, includeProjects = false) {
85851
+ if (!state.signedIn) return;
85852
+ const team = typeof client2.getActiveTeamId === "function" ? client2.getActiveTeamId() : null;
85853
+ const master = typeof client2.getMasterKeyBytes === "function" ? Buffer.from(client2.getMasterKeyBytes()) : null;
85854
+ const current = () => state.signedIn && (typeof client2.getActiveTeamId !== "function" || client2.getActiveTeamId() === team) && (!master || master.equals(Buffer.from(client2.getMasterKeyBytes())));
85855
+ if (typeof client2.getChatActivity === "function") {
85856
+ try {
85857
+ const activity = await client2.getChatActivity();
85858
+ if (current()) {
85859
+ updateTuiChatSidebar(state, () => {
85860
+ state.runningChatIds = activity.ids;
85861
+ state.activityChats = activity.chats;
85862
+ });
85863
+ render();
85864
+ }
85865
+ } catch {
85866
+ if (current()) {
85867
+ state.status = "Running chat status unavailable.";
85868
+ render();
85869
+ }
85870
+ }
85871
+ }
85872
+ if (includeProjects && typeof client2.listProjects === "function") {
85873
+ const summaries = await loadTuiProjects(client2), projects = [];
85874
+ 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))));
85875
+ const linkedIds = [...new Set(projects.flatMap((project) => project.items.filter((item) => item.type === "chat").map((item) => item.targetId)))];
85876
+ const linkedChats = typeof client2.getSidebarChats === "function" ? await client2.getSidebarChats(linkedIds) : [];
85877
+ if (current()) {
85878
+ updateTuiChatSidebar(state, () => {
85879
+ state.chatSidebarProjects = projects;
85880
+ state.sidebarLinkedChats = linkedChats;
85881
+ });
85882
+ render();
85883
+ }
85884
+ }
85885
+ }
85886
+ async function placeTuiChats(state, client2, chatIds, location, mode) {
85887
+ const context = { teamId: client2.getActiveTeamId() }, master = Buffer.from(client2.getMasterKeyBytes());
85888
+ const fence = () => {
85889
+ if (!state.signedIn || context.teamId !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
85890
+ };
85891
+ const destination = await loadTuiProject(client2, location.projectId, true);
85892
+ if (location.folderId && !destination.folders.some((folder) => folder.id === location.folderId)) throw new Error("Folder unavailable");
85893
+ for (const chatId of chatIds) {
85894
+ fence();
85895
+ const existing = destination.items.find((item) => item.type === "chat" && item.targetId === chatId);
85896
+ if (existing) {
85897
+ if ((existing.folderId ?? null) !== location.folderId) await client2.moveProjectItemToFolder(destination.id, existing.id, location.folderId, context);
85898
+ } else {
85899
+ const title = [...state.recentChats, ...state.activityChats].find((chat) => chat.id === chatId)?.title || "Untitled chat";
85900
+ const timestamp = Math.floor(Date.now() / 1e3);
85901
+ const payload = {
85902
+ project_item_id: randomUUID18(),
85903
+ folder_id: location.folderId,
85904
+ item_type: "chat",
85905
+ target_id: chatId,
85906
+ target_id_encrypted: await encryptWithAesGcmCombined(chatId, destination.projectKey),
85907
+ encrypted_display_name: await encryptWithAesGcmCombined(title, destination.projectKey),
85908
+ created_at: timestamp,
85909
+ updated_at: timestamp,
85910
+ position: timestamp
85911
+ };
85912
+ fence();
85913
+ await client2.createProjectItem(destination.id, payload, context);
85914
+ }
85915
+ }
85916
+ if (mode === "move") for (const source of state.chatSidebarProjects.filter((project) => project.id !== destination.id)) {
85917
+ for (const chatId of chatIds.filter((id) => source.items.some((item) => item.type === "chat" && item.targetId === id))) {
85918
+ fence();
85919
+ await client2.deleteProjectItemByTarget(source.id, "chat", chatId, context);
85920
+ }
85921
+ }
85922
+ }
85923
+ async function createTuiChatProject(state, client2, chatIds) {
85924
+ if (!state.signedIn || !chatIds.length) throw new Error("Choose a saved chat first.");
85925
+ const context = client2.getActiveTeamId(), master = Buffer.from(client2.getMasterKeyBytes());
85926
+ 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");
85927
+ const plan = await client2.planProjectAsk({ instruction: "Name a new project from chat titles.", chatTitles: titles });
85928
+ const proposed = plan.proposed_project, name = proposed?.name?.trim();
85929
+ if (!name || name.length > 200) throw new Error("Could not generate a project title.");
85930
+ if (context !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
85931
+ const form = buildProjectForm();
85932
+ form.fields.find((field) => field.name === "name").value = name;
85933
+ const project = await submitProjectForm(client2, form, true);
85934
+ await placeTuiChats(state, client2, chatIds, { projectId: project.id, folderId: null }, "move");
85935
+ return project.id;
85936
+ }
85937
+
85555
85938
  // src/tuiLayout.ts
85556
85939
  var WORKSPACES = ["chats", "apps", "projects", "workflows", "tasks"];
85557
85940
  var DEFAULT_CHAT_BACKGROUND = "#4867cd";
@@ -85562,7 +85945,13 @@ function sidebarLines(state) {
85562
85945
  const selected = (label3, index) => `${state.focus === "sidebar" && state.sidebarIndex === index ? ">" : " "} ${label3}`;
85563
85946
  switch (state.workspace) {
85564
85947
  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"];
85948
+ return [
85949
+ tuiChatBreadcrumb(state) ?? "Chats",
85950
+ "",
85951
+ ...tuiChatSidebarRows(state).map((row, index) => selected(`${row.running ? ["\u25F4", "\u25F7", "\u25F6", "\u25F5"][state.activityFrame % 4] + " " : ""}${row.label}`, index)),
85952
+ "",
85953
+ "/active /chat-add-to-project"
85954
+ ];
85566
85955
  case "projects":
85567
85956
  return ["Projects", "", ...state.projects.map((p, i) => selected(p.name, i)), "", "/project-create new"];
85568
85957
  case "tasks":
@@ -86025,6 +86414,8 @@ function homeHeader(state, width, height) {
86025
86414
  }
86026
86415
  function renderHomeChatCards(state, width, height) {
86027
86416
  const chats = homeChatItems(state), result = homeHeader(state, width, height);
86417
+ const activeCount = runningTuiChatGroups(state).length;
86418
+ if (activeCount) result.push(centered2(`${activeCount} ${activeCount === 1 ? "chat" : "chats"} active\u2026 /active`, width), "");
86028
86419
  if (!chats.length) {
86029
86420
  result.push(centered2(state.homeLoading ? "Loading your recent chats\u2026" : "Start a chat below. Your recent chats will appear here.", width), "");
86030
86421
  return result;
@@ -86048,7 +86439,7 @@ function renderHomeChatCards(state, width, height) {
86048
86439
  }
86049
86440
 
86050
86441
  // src/tuiAppsWorkspace.ts
86051
- import { randomBytes as randomBytes11, randomUUID as randomUUID18 } from "crypto";
86442
+ import { randomBytes as randomBytes11, randomUUID as randomUUID19 } from "crypto";
86052
86443
 
86053
86444
  // ../ui/src/components/apps/appsSkillFormUtils.ts
86054
86445
  var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -86505,7 +86896,7 @@ async function saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey,
86505
86896
  const value = record5(result);
86506
86897
  const type = string2(value.type, string2(value.embed_type));
86507
86898
  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();
86899
+ const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id : randomUUID19();
86509
86900
  return [(async () => ({
86510
86901
  embed_id: id,
86511
86902
  encrypted_type: await encryptWithAesGcmCombined(type || "app_skill_use", key),
@@ -86550,7 +86941,7 @@ async function executeConfirmedTuiAppsSkill(client2, run) {
86550
86941
  const owner = record5(await client2.whoAmI());
86551
86942
  const ownerId = string2(owner.id, string2(owner.user_id));
86552
86943
  if (!ownerId) throw new Error("Account identity unavailable; skill was not run.");
86553
- const rootId = randomUUID18();
86944
+ const rootId = randomUUID19();
86554
86945
  const key = randomBytes11(32);
86555
86946
  const encryptedKey = await encryptBytesWithAesGcm(key, client2.getMasterKeyBytes());
86556
86947
  await saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey, run, { status: "processing" }, "processing");
@@ -86796,6 +87187,15 @@ function createInitialTuiState() {
86796
87187
  taskStatusFilter: "",
86797
87188
  taskContext: null,
86798
87189
  recentChats: [],
87190
+ runningChatIds: [],
87191
+ activityChats: [],
87192
+ sidebarLinkedChats: [],
87193
+ activityFrame: 0,
87194
+ chatSidebarProjects: [],
87195
+ chatSidebarLocation: null,
87196
+ chatSidebarAncestors: false,
87197
+ chatProjectOperation: null,
87198
+ chatProjectBusy: false,
86799
87199
  activeChatId: null,
86800
87200
  activeChat: null,
86801
87201
  headerState: "new",
@@ -87207,7 +87607,7 @@ function wrap(line, width) {
87207
87607
  }
87208
87608
 
87209
87609
  // src/tuiWorkspaceController.ts
87210
- import { randomUUID as randomUUID19 } from "crypto";
87610
+ import { randomUUID as randomUUID20 } from "crypto";
87211
87611
  function chatDraftKey(state) {
87212
87612
  return state.screen === "example" ? `example:${state.activeExample?.chat.id}` : state.activeChatId ?? "new";
87213
87613
  }
@@ -87254,6 +87654,7 @@ async function recent(context) {
87254
87654
  if (request !== state.routeVersion || homeRequest !== state.homeLoadVersion || !state.signedIn) return;
87255
87655
  state.recentChats = chats;
87256
87656
  render();
87657
+ await refreshTuiChatSidebar(state, client2, render, true);
87257
87658
  }
87258
87659
  async function openSavedChat(context, id) {
87259
87660
  const { state, client: client2, render } = context;
@@ -87455,6 +87856,64 @@ async function handleWorkspaceCommand(context, command) {
87455
87856
  state.selectedProjectId = null;
87456
87857
  render();
87457
87858
  return true;
87859
+ case "/active":
87860
+ state.sidebarOpen = true;
87861
+ state.focus = "sidebar";
87862
+ state.sidebarIndex = 0;
87863
+ render();
87864
+ await refreshTuiChatSidebar(state, client2, render, true);
87865
+ state.sidebarIndex = 0;
87866
+ render();
87867
+ return true;
87868
+ case "/chat-add-to-project":
87869
+ case "/chat-move-to-project":
87870
+ case "/chat-create-project": {
87871
+ if (state.chatProjectBusy) return true;
87872
+ if (!state.signedIn) throw new Error("Sign in to organize chats.");
87873
+ const selected = state.focus === "sidebar" ? tuiChatSidebarRows(state)[state.sidebarIndex]?.chatId : ["start", "chats"].includes(state.screen) ? homeChatItems(state)[state.selectedIndex]?.id : state.activeChatId;
87874
+ const ids = arg ? arg.split(/\s+/).filter(Boolean) : selected ? [selected] : [];
87875
+ if (!ids.length) throw new Error("Choose a saved chat first.");
87876
+ await refreshTuiChatSidebar(state, client2, render, true);
87877
+ if (name === "/chat-create-project") {
87878
+ state.chatProjectBusy = true;
87879
+ state.status = "Organizing chats\u2026";
87880
+ render();
87881
+ try {
87882
+ const id = await createTuiChatProject(state, client2, ids);
87883
+ state.chatProjectOperation = null;
87884
+ await openProject(context, id);
87885
+ } finally {
87886
+ state.chatProjectBusy = false;
87887
+ }
87888
+ } else {
87889
+ state.chatProjectOperation = { chatIds: ids, mode: name === "/chat-move-to-project" ? "move" : "add" };
87890
+ state.chatSidebarLocation = null;
87891
+ state.sidebarOpen = true;
87892
+ state.focus = "sidebar";
87893
+ state.sidebarIndex = 0;
87894
+ state.status = "Choose a project or subfolder.";
87895
+ }
87896
+ render();
87897
+ return true;
87898
+ }
87899
+ case "/chat-subfolder": {
87900
+ const location = state.chatSidebarLocation;
87901
+ if (!location || !arg.trim()) throw new Error("Open a project folder and enter /chat-subfolder followed by a name.");
87902
+ const project = state.chatSidebarProjects.find((project2) => project2.id === location.projectId);
87903
+ if (!project) throw new Error("Project unavailable.");
87904
+ const timestamp = Math.floor(Date.now() / 1e3);
87905
+ await client2.createProjectFolder(project.id, {
87906
+ folder_id: randomUUID20(),
87907
+ parent_folder_id: location.folderId,
87908
+ encrypted_name: await encryptWithAesGcmCombined(arg.trim().slice(0, 200), project.projectKey),
87909
+ encrypted_sort_key: await encryptWithAesGcmCombined(arg.trim().toLowerCase().slice(0, 200), project.projectKey),
87910
+ created_at: timestamp,
87911
+ updated_at: timestamp,
87912
+ position: timestamp
87913
+ });
87914
+ await refreshTuiChatSidebar(state, client2, render, true);
87915
+ return true;
87916
+ }
87458
87917
  case "/sidebar":
87459
87918
  state.sidebarOpen = !state.sidebarOpen;
87460
87919
  state.focus = state.sidebarOpen ? "sidebar" : state.workspace === "chats" && !["start", "chats"].includes(state.screen) ? "composer" : "content";
@@ -87695,7 +88154,7 @@ async function saveForm(context) {
87695
88154
  } else if (form.kind === "workflow-create") {
87696
88155
  const description = formValue(form, "description").trim();
87697
88156
  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() });
88157
+ const request = state.routeVersion, session = await client2.startWorkflowInput({ text: description, inputType: "text", optimisticSave: true, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, idempotencyKey: randomUUID20() });
87699
88158
  if (request !== state.routeVersion || state.form !== form) return;
87700
88159
  const workflow = session.workflow ?? session.workflows?.[0];
87701
88160
  if (workflow) {
@@ -87721,7 +88180,7 @@ async function saveForm(context) {
87721
88180
  }
87722
88181
  }
87723
88182
  async function handleWorkspaceKey(context, chunk, key) {
87724
- const { state, render } = context;
88183
+ const { state, render, client: client2 } = context;
87725
88184
  if (state.form) {
87726
88185
  const form = state.form, field = form.fields[form.fieldIndex];
87727
88186
  if (!field) {
@@ -87802,6 +88261,13 @@ async function handleWorkspaceKey(context, chunk, key) {
87802
88261
  return true;
87803
88262
  }
87804
88263
  if (key.name === "escape") {
88264
+ if (state.chatProjectBusy) return true;
88265
+ if (state.chatProjectOperation) {
88266
+ state.chatProjectOperation = null;
88267
+ state.status = null;
88268
+ render();
88269
+ return true;
88270
+ }
87805
88271
  if (state.focus === "sidebar") {
87806
88272
  state.sidebarOpen = false;
87807
88273
  state.focus = chatHome ? "content" : state.workspace === "chats" ? "composer" : "content";
@@ -87878,11 +88344,34 @@ async function handleWorkspaceKey(context, chunk, key) {
87878
88344
  if (chunk !== "/") return true;
87879
88345
  }
87880
88346
  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;
88347
+ const chatRows = tuiChatSidebarRows(state);
88348
+ 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
88349
  if (key.name === "up" || key.name === "down") state.sidebarIndex = Math.max(0, Math.min(count - 1, state.sidebarIndex + (key.name === "up" ? -1 : 1)));
87883
88350
  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}`);
88351
+ if (state.workspace === "chats") {
88352
+ if (state.chatProjectBusy) return true;
88353
+ const row = chatRows[state.sidebarIndex];
88354
+ if (row?.kind === "new") await context.command("/new");
88355
+ else if (row?.kind === "chat") await context.command(`/chat ${row.chatId}`);
88356
+ else if (row?.kind === "path") state.chatSidebarAncestors = !state.chatSidebarAncestors;
88357
+ else if (row?.kind === "create" && state.chatProjectOperation) await context.command(`/chat-create-project ${state.chatProjectOperation.chatIds.join(" ")}`);
88358
+ else if (row?.kind === "choose" && state.chatProjectOperation) {
88359
+ const operation = state.chatProjectOperation;
88360
+ state.chatProjectBusy = true;
88361
+ try {
88362
+ await placeTuiChats(state, client2, operation.chatIds, { projectId: row.projectId, folderId: row.folderId ?? null }, operation.mode);
88363
+ state.chatProjectOperation = null;
88364
+ state.status = null;
88365
+ } finally {
88366
+ state.chatProjectBusy = false;
88367
+ await refreshTuiChatSidebar(state, client2, render, true);
88368
+ }
88369
+ } else if (row && ["project", "folder", "up"].includes(row.kind)) {
88370
+ state.chatSidebarLocation = row.kind === "up" && row.folderId === void 0 ? null : { projectId: row.projectId, folderId: row.folderId ?? null };
88371
+ state.chatSidebarAncestors = false;
88372
+ state.sidebarIndex = 0;
88373
+ }
88374
+ } else if (state.workspace === "projects") await context.command(`/project ${state.projects[state.sidebarIndex]?.id}`);
87886
88375
  else if (state.workspace === "tasks") await openTask(context, state.tasks[state.sidebarIndex]?.taskId ?? "");
87887
88376
  else if (state.workspace === "workflows") await context.command(`/workflow ${state.workflows[state.sidebarIndex]?.id}`);
87888
88377
  else if (state.workspace === "apps") await context.command(`/app ${state.apps[state.sidebarIndex]?.id}`);
@@ -88269,6 +88758,27 @@ async function runTui(client2, terminal = new TuiTerminal()) {
88269
88758
  let resolveResult = null;
88270
88759
  let renderTimer = null;
88271
88760
  let closed = false;
88761
+ let stopActivity;
88762
+ let activityTimer;
88763
+ let refreshingActivity = false;
88764
+ const refreshActivity = async () => {
88765
+ if (closed || refreshingActivity || !state.signedIn) return;
88766
+ refreshingActivity = true;
88767
+ try {
88768
+ await refreshTuiChatSidebar(state, client2, render);
88769
+ } finally {
88770
+ refreshingActivity = false;
88771
+ }
88772
+ };
88773
+ const activityPoll = setInterval(() => {
88774
+ void refreshActivity();
88775
+ }, 3e4);
88776
+ const activityAnimation = setInterval(() => {
88777
+ if (!closed && state.runningChatIds.length) {
88778
+ state.activityFrame = (state.activityFrame + 1) % 4;
88779
+ render();
88780
+ }
88781
+ }, 250);
88272
88782
  const render = () => {
88273
88783
  if (closed) return;
88274
88784
  if (state.screen !== "chat") client2.clearInteractiveChatViewer();
@@ -88284,6 +88794,10 @@ async function runTui(client2, terminal = new TuiTerminal()) {
88284
88794
  };
88285
88795
  const finish = (result) => {
88286
88796
  closed = true;
88797
+ stopActivity?.();
88798
+ clearTimeout(activityTimer);
88799
+ clearInterval(activityPoll);
88800
+ clearInterval(activityAnimation);
88287
88801
  client2.endInteractiveViewerSession();
88288
88802
  if (renderTimer) clearTimeout(renderTimer);
88289
88803
  renderTimer = null;
@@ -88300,6 +88814,19 @@ async function runTui(client2, terminal = new TuiTerminal()) {
88300
88814
  });
88301
88815
  render();
88302
88816
  void loadHomeData(state, client2, render);
88817
+ void refreshActivity();
88818
+ if (state.signedIn && typeof client2.observeChatActivity === "function") {
88819
+ void client2.observeChatActivity(() => {
88820
+ clearTimeout(activityTimer);
88821
+ activityTimer = setTimeout(() => {
88822
+ void refreshActivity();
88823
+ }, 300);
88824
+ }).then((stop) => {
88825
+ if (closed) stop();
88826
+ else stopActivity = stop;
88827
+ }).catch(() => {
88828
+ });
88829
+ }
88303
88830
  return new Promise((resolve18) => {
88304
88831
  resolveResult = resolve18;
88305
88832
  });
@@ -88612,12 +89139,12 @@ async function sendTuiMessage(params) {
88612
89139
  if (sourceExample) state.messages = sourceExample.messages.map((m) => ({ role: m.role === "user" ? "user" : "assistant", content: m.content, title: m.senderName }));
88613
89140
  const messages = state.messages;
88614
89141
  const existingChatId = sourceExample ? null : state.activeChatId;
88615
- const chatId = existingChatId ?? randomUUID20();
89142
+ const chatId = existingChatId ?? randomUUID21();
88616
89143
  const draftKey = sourceExample ? `example:${sourceExample.chat.id}` : existingChatId ?? "new";
88617
89144
  state.drafts[draftKey] = "";
88618
89145
  state.input = "";
88619
89146
  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) }));
89147
+ 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
89148
  state.activeChatId = chatId;
88622
89149
  state.headerState = existingChatId ? "ready" : "loading";
88623
89150
  state.headerError = null;
@@ -89042,7 +89569,7 @@ function clamp(value, min, max) {
89042
89569
  }
89043
89570
 
89044
89571
  // src/codexTaskBridge.ts
89045
- import { createHash as createHash22 } from "crypto";
89572
+ import { createHash as createHash23 } from "crypto";
89046
89573
  import { spawn as spawn3 } from "child_process";
89047
89574
  import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
89048
89575
  import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
@@ -89085,7 +89612,7 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
89085
89612
  const session = client2.getSession();
89086
89613
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
89087
89614
  const account = JSON.stringify(scope2);
89088
- const directory = join18(stateRoot, "codex-chat-deletions", createHash22("sha256").update(account).digest("hex"));
89615
+ const directory = join18(stateRoot, "codex-chat-deletions", createHash23("sha256").update(account).digest("hex"));
89089
89616
  if (!existsSync15(directory)) return;
89090
89617
  const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
89091
89618
  try {
@@ -89119,14 +89646,14 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
89119
89646
  }
89120
89647
 
89121
89648
  // src/taskCommandDelivery.ts
89122
- import { createHash as createHash23 } from "crypto";
89649
+ import { createHash as createHash24 } from "crypto";
89123
89650
  import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
89124
89651
  import { join as join19 } from "path";
89125
89652
  import lockfile6 from "proper-lockfile";
89126
89653
  async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
89127
89654
  const session = client2.getSession();
89128
89655
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
89129
- const directory = join19(root, "task-command-delivery", createHash23("sha256").update(JSON.stringify(scope2)).digest("hex"));
89656
+ const directory = join19(root, "task-command-delivery", createHash24("sha256").update(JSON.stringify(scope2)).digest("hex"));
89130
89657
  if (!existsSync16(directory)) return;
89131
89658
  const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
89132
89659
  try {
@@ -89236,7 +89763,7 @@ import {
89236
89763
  writeFileSync as writeFileSync13
89237
89764
  } from "fs";
89238
89765
  import { spawn as spawn7 } from "child_process";
89239
- import { createHash as createHash29 } from "crypto";
89766
+ import { createHash as createHash30 } from "crypto";
89240
89767
  import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
89241
89768
 
89242
89769
  // src/projectSourceRootPolicy.ts
@@ -89271,7 +89798,7 @@ function isEqualOrInside(parent, candidate) {
89271
89798
  }
89272
89799
 
89273
89800
  // src/projectPathPolicy.ts
89274
- import { createHash as createHash24, randomBytes as randomBytes12 } from "crypto";
89801
+ import { createHash as createHash25, randomBytes as randomBytes12 } from "crypto";
89275
89802
  import {
89276
89803
  chmodSync as chmodSync9,
89277
89804
  closeSync as closeSync5,
@@ -89552,7 +90079,7 @@ function isInside2(root, candidate) {
89552
90079
  return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
89553
90080
  }
89554
90081
  function sha2563(value) {
89555
- return createHash24("sha256").update(value).digest("hex");
90082
+ return createHash25("sha256").update(value).digest("hex");
89556
90083
  }
89557
90084
  function stableJson2(value) {
89558
90085
  return JSON.stringify(value, (_key, item) => {
@@ -89562,7 +90089,7 @@ function stableJson2(value) {
89562
90089
  }
89563
90090
 
89564
90091
  // src/remoteFileWrites.ts
89565
- import { createHash as createHash25, randomBytes as randomBytes13 } from "crypto";
90092
+ import { createHash as createHash26, randomBytes as randomBytes13 } from "crypto";
89566
90093
  import {
89567
90094
  chmodSync as chmodSync10,
89568
90095
  closeSync as closeSync7,
@@ -89827,7 +90354,7 @@ var MAX_FILE_BYTES2 = 200 * 1024;
89827
90354
  var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89828
90355
  var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89829
90356
  function sha256ProjectFile(content) {
89830
- return createHash25("sha256").update(content).digest("hex");
90357
+ return createHash26("sha256").update(content).digest("hex");
89831
90358
  }
89832
90359
  async function executeRemoteFileMutation(options) {
89833
90360
  const mutation = validateMutation(options.mutation);
@@ -90515,13 +91042,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
90515
91042
  import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
90516
91043
 
90517
91044
  // src/remoteCommandRuntime.ts
90518
- import { createHash as createHash27, randomUUID as randomUUID21 } from "crypto";
91045
+ import { createHash as createHash28, randomUUID as randomUUID22 } from "crypto";
90519
91046
  import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
90520
91047
  import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
90521
91048
  import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
90522
91049
 
90523
91050
  // src/remoteCommandAppArmor.ts
90524
- import { createHash as createHash26 } from "crypto";
91051
+ import { createHash as createHash27 } from "crypto";
90525
91052
  import { spawn as spawn5 } from "child_process";
90526
91053
  import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
90527
91054
  import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
@@ -90738,7 +91265,7 @@ function hasControl2(value) {
90738
91265
  });
90739
91266
  }
90740
91267
  function sha2564(value) {
90741
- return createHash26("sha256").update(value).digest("hex");
91268
+ return createHash27("sha256").update(value).digest("hex");
90742
91269
  }
90743
91270
 
90744
91271
  // src/remoteCommandRuntime.ts
@@ -91142,7 +91669,7 @@ var RemoteCommandRuntime = class {
91142
91669
  };
91143
91670
  function createRemoteCommandPreflight(request) {
91144
91671
  if (!request || typeof request !== "object") throw new RemoteCommandError("invalid_request", "Command request must be an object");
91145
- const executionId = request.execution_id ?? randomUUID21();
91672
+ const executionId = request.execution_id ?? randomUUID22();
91146
91673
  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
91674
  if (typeof request.project_id !== "string" || !request.project_id || request.project_id.length > 256) throw new RemoteCommandError("invalid_request", "Invalid Project ID");
91148
91675
  if (typeof request.source_root !== "string" || !request.source_root) throw new RemoteCommandError("invalid_request", "Invalid Project source root");
@@ -91579,7 +92106,7 @@ function stableJson4(value) {
91579
92106
  return JSON.stringify(value);
91580
92107
  }
91581
92108
  function sha2565(value) {
91582
- return createHash27("sha256").update(value).digest("hex");
92109
+ return createHash28("sha256").update(value).digest("hex");
91583
92110
  }
91584
92111
  function deepFreeze(value) {
91585
92112
  if (value && typeof value === "object") {
@@ -91857,7 +92384,7 @@ function capabilityError(message) {
91857
92384
  }
91858
92385
 
91859
92386
  // src/remoteCommandResourceGrants.ts
91860
- import { createHash as createHash28 } from "crypto";
92387
+ import { createHash as createHash29 } from "crypto";
91861
92388
  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
92389
  import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
91863
92390
  var STORE_FILE = "remote-command-resource-grants.json";
@@ -91952,7 +92479,7 @@ function resolveRemoteCommandResourceGrants(context) {
91952
92479
  return { writable_targets, network_profiles, credential_profiles };
91953
92480
  }
91954
92481
  function definitionDigest(kind, definition) {
91955
- return createHash28("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
92482
+ return createHash29("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
91956
92483
  }
91957
92484
  function upsert(grant) {
91958
92485
  validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
@@ -93122,7 +93649,7 @@ function readRemoteAccessTextFile(options) {
93122
93649
  truncated,
93123
93650
  sizeBytes: stats.size,
93124
93651
  lineCount: countTextLines(content),
93125
- expected_base: truncated ? null : createHash29("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
93652
+ expected_base: truncated ? null : createHash30("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
93126
93653
  };
93127
93654
  } catch (error) {
93128
93655
  const code = error.code;
@@ -93172,7 +93699,7 @@ function readRemoteAccessImageChunk(options) {
93172
93699
  mime_type: mimeType,
93173
93700
  size_bytes: stats.size,
93174
93701
  offset: options.offset,
93175
- content_hash: createHash29("sha256").update(bytes2).digest("hex")
93702
+ content_hash: createHash30("sha256").update(bytes2).digest("hex")
93176
93703
  };
93177
93704
  } catch (error) {
93178
93705
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -93220,8 +93747,8 @@ function readRemoteAccessFileChunk(options) {
93220
93747
  content_base64: bytes2.toString("base64"),
93221
93748
  size_bytes: stats.size,
93222
93749
  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")
93750
+ file_identity: createHash30("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
93751
+ chunk_hash: createHash30("sha256").update(bytes2).digest("hex")
93225
93752
  };
93226
93753
  } catch (error) {
93227
93754
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -94783,7 +95310,7 @@ function writeWorkRecoveryAtomically(path2, document) {
94783
95310
 
94784
95311
  // src/revolutBusinessCertificate.ts
94785
95312
  import { execFileSync as execFileSync6 } from "child_process";
94786
- import { createSign, randomUUID as randomUUID22 } from "crypto";
95313
+ import { createSign, randomUUID as randomUUID23 } from "crypto";
94787
95314
  import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
94788
95315
  import { homedir as homedir8 } from "os";
94789
95316
  import { join as join28, resolve as resolve17 } from "path";
@@ -94857,7 +95384,7 @@ function generateRevolutBusinessClientAssertion(options) {
94857
95384
  aud: REVOLUT_BUSINESS_AUDIENCE,
94858
95385
  exp: now + 3600,
94859
95386
  iat: now,
94860
- jti: randomUUID22()
95387
+ jti: randomUUID23()
94861
95388
  });
94862
95389
  const unsigned = `${header2}.${payload}`;
94863
95390
  const signer = createSign("RSA-SHA256");
@@ -96329,7 +96856,7 @@ async function resolveAddToProjectStorageChoice(client2, project, flags, objectT
96329
96856
  async function createEncryptedProjectItem(client2, project, input) {
96330
96857
  const timestamp = nowSeconds3();
96331
96858
  return client2.createProjectItem(project.projectId, {
96332
- project_item_id: randomUUID23(),
96859
+ project_item_id: randomUUID24(),
96333
96860
  folder_id: input.folderId ?? null,
96334
96861
  item_type: input.itemType,
96335
96862
  target_id: input.targetId,
@@ -96528,7 +97055,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
96528
97055
  const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
96529
97056
  if (action === "create" || action === "add") {
96530
97057
  const assumption = await client2.createPlanAssumption(plan.planId, {
96531
- assumption_id: typeof flags.id === "string" ? flags.id : randomUUID23(),
97058
+ assumption_id: typeof flags.id === "string" ? flags.id : randomUUID24(),
96532
97059
  encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
96533
97060
  status: typeof flags.status === "string" ? flags.status : void 0,
96534
97061
  linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
@@ -96570,7 +97097,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
96570
97097
  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
97098
  if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
96572
97099
  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() });
97100
+ 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
97101
  const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
96575
97102
  if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
96576
97103
  else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
@@ -97464,7 +97991,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
97464
97991
  const writeMode = await resolveProjectWriteMode(flags, `Choose how OpenMates may change files in ${JSON.stringify(name)}:`);
97465
97992
  const projectKey = randomBytes14(32);
97466
97993
  const wrapping = await client2.projectWrappingKey(context);
97467
- const projectId = randomUUID23();
97994
+ const projectId = randomUUID24();
97468
97995
  const timestamp = nowSeconds3();
97469
97996
  const defaultFocus = buildProjectDefaultFocus(name);
97470
97997
  const slugMetadata = await buildEncryptedObjectSlugMetadata({
@@ -97490,7 +98017,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
97490
98017
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
97491
98018
  key_wrappers: wrapping.teamId ? [{
97492
98019
  key_type: "team",
97493
- hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
98020
+ hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
97494
98021
  team_key_epoch: 1,
97495
98022
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
97496
98023
  wrapper_version: 1,
@@ -97730,7 +98257,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
97730
98257
  const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client2.planProjectAsk({ instruction }), "proposed_project");
97731
98258
  const projectKey = randomBytes14(32);
97732
98259
  const timestamp = Math.floor(Date.now() / 1e3);
97733
- const projectId = randomUUID23();
98260
+ const projectId = randomUUID24();
97734
98261
  const name = requiredString(proposal, "name", instruction);
97735
98262
  const slugMetadata = await buildEncryptedObjectSlugMetadata({
97736
98263
  value: typeof flags.slug === "string" ? flags.slug : name,
@@ -97853,8 +98380,8 @@ async function handleProjectFiles(client2, masterKey, rest, flags, context) {
97853
98380
  ...flags["include-ignored"] === true ? {
97854
98381
  approvedIgnoredRead: {
97855
98382
  path: String(argumentsValue.path),
97856
- chatId: `cli-direct-${randomUUID23()}`,
97857
- operationId: `cli-direct-${randomUUID23()}`
98383
+ chatId: `cli-direct-${randomUUID24()}`,
98384
+ operationId: `cli-direct-${randomUUID24()}`
97858
98385
  }
97859
98386
  } : {}
97860
98387
  });
@@ -98642,7 +99169,7 @@ async function handleRemoteAccess(client2, subcommand, rest, flags) {
98642
99169
  const projects = await loadProjects(client2, masterKey, hostingFlags, hostingContext);
98643
99170
  const bindings = await resolveRemoteAccessBindings(client2, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
98644
99171
  if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
98645
- const sourceSessionId = randomUUID23();
99172
+ const sourceSessionId = randomUUID24();
98646
99173
  const controller = new AbortController();
98647
99174
  const stop = () => controller.abort();
98648
99175
  process.once("SIGINT", stop);
@@ -98796,7 +99323,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
98796
99323
  updated_at: nowSeconds3()
98797
99324
  }, context);
98798
99325
  }
98799
- resolved.push({ rootPath, project, sourceId: randomUUID23() });
99326
+ resolved.push({ rootPath, project, sourceId: randomUUID24() });
98800
99327
  }
98801
99328
  }
98802
99329
  const bindings = [];
@@ -98840,7 +99367,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
98840
99367
  }
98841
99368
  async function createEncryptedRemoteAccessProject(client2, name, rootPath, writeMode, context) {
98842
99369
  const projectKey = randomBytes14(32);
98843
- const projectId = randomUUID23();
99370
+ const projectId = randomUUID24();
98844
99371
  const timestamp = Math.floor(Date.now() / 1e3);
98845
99372
  const wrapping = await client2.projectWrappingKey(context);
98846
99373
  const importedInstructions = readRootProjectInstructions(rootPath);
@@ -98866,7 +99393,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
98866
99393
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
98867
99394
  key_wrappers: wrapping.teamId ? [{
98868
99395
  key_type: "team",
98869
- hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
99396
+ hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
98870
99397
  team_key_epoch: 1,
98871
99398
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
98872
99399
  wrapper_version: 1,
@@ -98924,7 +99451,7 @@ async function ensureProjectDefaultFocus(client2, project, context, rootPath) {
98924
99451
  function buildProjectDefaultFocus(projectName, instructions, source = "generated") {
98925
99452
  const name = projectName.trim() || "Untitled project";
98926
99453
  return {
98927
- focus_id: randomUUID23(),
99454
+ focus_id: randomUUID24(),
98928
99455
  name: `Work on ${name}`,
98929
99456
  instructions: instructions?.trim() || `Help with work in ${name}. Follow the user's instructions and the Project's connected source guidance.`,
98930
99457
  source
@@ -100257,7 +100784,7 @@ function normalizeApiKeyLoadedChat(payload) {
100257
100784
  const messages = rawMessages.map((message) => {
100258
100785
  const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
100259
100786
  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;
100787
+ const hashedMessageId = messageId ? createHash31("sha256").update(messageId).digest("hex") : null;
100261
100788
  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
100789
  return {
100263
100790
  id: messageId,
@@ -102454,7 +102981,7 @@ async function runAccountImport(client2, parserFormat, file, flags) {
102454
102981
  if (flags.yes !== true) {
102455
102982
  await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
102456
102983
  }
102457
- const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID23();
102984
+ const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID24();
102458
102985
  const selectedChats = parsed.chats.slice(0, selectedCount);
102459
102986
  const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
102460
102987
  const confirmation = await client2.confirmAccountImport(importId, selectedFingerprints);
@@ -102948,8 +103475,8 @@ async function handleFinance(client2, subcommand, _rest, flags) {
102948
103475
  refresh_token_envelope: connectedAccount.refreshTokenBundle
102949
103476
  }
102950
103477
  ] : [],
102951
- chatId: `cli-finance-${randomUUID23()}`,
102952
- messageId: `cli-finance-message-${randomUUID23()}`,
103478
+ chatId: `cli-finance-${randomUUID24()}`,
103479
+ messageId: `cli-finance-message-${randomUUID24()}`,
102953
103480
  apiKey: resolveApiKey(flags) ?? void 0,
102954
103481
  promptInjectionProtection: false
102955
103482
  });