openmates 0.15.0-alpha.21 → 0.15.0-alpha.23

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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-AXNRPVLE.js";
5
5
 
6
6
  // src/client.ts
7
- import { randomUUID as randomUUID5 } from "crypto";
7
+ import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
8
8
  import { arch, platform as platform2, release } from "os";
9
9
  import { createInterface as createInterface2 } from "readline/promises";
10
10
  import { stdin as stdin2, stdout } from "process";
@@ -3540,9 +3540,11 @@ function stripTimer(job) {
3540
3540
  }
3541
3541
 
3542
3542
  // src/tasksCli.ts
3543
- import { createHash as createHash5, randomBytes as randomBytes2, randomUUID as randomUUID4 } from "crypto";
3543
+ import { createHash as createHash5, createHmac, hkdfSync, randomBytes as randomBytes2, randomUUID as randomUUID4 } from "crypto";
3544
3544
  var TASK_STATUSES2 = ["backlog", "todo", "in_progress", "blocked", "done"];
3545
3545
  var DEFAULT_STANDALONE_PREFIX = "TASK";
3546
+ var PRIORITY_LEVELS = ["none", "low", "medium", "high", "urgent"];
3547
+ var LABEL_INDEX_INFO = "openmates-task-label-index-v1";
3546
3548
  function normalizeTaskStatus(value) {
3547
3549
  if (value === void 0) return void 0;
3548
3550
  if (TASK_STATUSES2.includes(value)) return value;
@@ -3566,12 +3568,43 @@ function parseDueAt(value) {
3566
3568
  if (Number.isNaN(parsed)) throw new Error(`Invalid --due value '${value}'.`);
3567
3569
  return Math.floor(parsed / 1e3);
3568
3570
  }
3571
+ function normalizeLabels(labels) {
3572
+ const output = [];
3573
+ const seen = /* @__PURE__ */ new Set();
3574
+ for (const label of labels ?? []) {
3575
+ const normalized = label.trim().toLowerCase().replace(/\s+/g, " ");
3576
+ if (!normalized || seen.has(normalized)) continue;
3577
+ seen.add(normalized);
3578
+ output.push(normalized);
3579
+ }
3580
+ return output;
3581
+ }
3582
+ function labelHashes(masterKey, labels) {
3583
+ const indexKey = Buffer.from(hkdfSync("sha256", Buffer.from(masterKey), Buffer.alloc(0), LABEL_INDEX_INFO, 32));
3584
+ return normalizeLabels(labels).map((label) => createHmac("sha256", indexKey).update(label).digest("hex"));
3585
+ }
3586
+ function normalizeTaskPriority(value) {
3587
+ if (value === void 0) return void 0;
3588
+ if (value === null) return 0;
3589
+ if (typeof value === "number") {
3590
+ if (!Number.isInteger(value) || value < 0 || value > 4) throw new Error(`Invalid task priority '${value}'.`);
3591
+ return value;
3592
+ }
3593
+ const normalized = value.trim().toLowerCase();
3594
+ const index = PRIORITY_LEVELS.indexOf(normalized);
3595
+ if (index < 0) throw new Error(`Unknown task priority '${value}'. Expected one of: ${PRIORITY_LEVELS.join(", ")}`);
3596
+ return index;
3597
+ }
3598
+ function taskPriorityLevel(priority) {
3599
+ return PRIORITY_LEVELS[Math.max(0, Math.min(4, Math.trunc(priority ?? 0)))] ?? "none";
3600
+ }
3569
3601
  async function buildCreateUserTaskInput(masterKey, input) {
3570
3602
  const taskKey = randomBytes2(32);
3571
3603
  const encryptedTaskKey = await encryptBytesWithAesGcm(taskKey, masterKey);
3572
3604
  const timestamp = nowSeconds();
3573
3605
  const assignee = parseAssignee(input.assign);
3574
3606
  const linkedProjectIds = input.projectIds ?? [];
3607
+ const labels = normalizeLabels(input.labels ?? input.tags ?? []);
3575
3608
  const status = input.status ?? (assignee.assigneeType === "ai" && !input.dueAt ? "in_progress" : "todo");
3576
3609
  return {
3577
3610
  task_id: randomUUIDCompat(),
@@ -3580,7 +3613,9 @@ async function buildCreateUserTaskInput(masterKey, input) {
3580
3613
  encrypted_task_key: encryptedTaskKey,
3581
3614
  encrypted_title: await encryptWithAesGcmCombined(input.title, taskKey),
3582
3615
  encrypted_description: await encryptWithAesGcmCombined(input.description ?? "", taskKey),
3583
- encrypted_tags: await encryptWithAesGcmCombined("[]", taskKey),
3616
+ encrypted_labels: await encryptWithAesGcmCombined(JSON.stringify(labels), taskKey),
3617
+ encrypted_tags: await encryptWithAesGcmCombined(JSON.stringify(labels), taskKey),
3618
+ label_hashes: labelHashes(masterKey, labels),
3584
3619
  encrypted_linked_project_ids: await encryptWithAesGcmCombined(JSON.stringify(linkedProjectIds), taskKey),
3585
3620
  status,
3586
3621
  assignee_type: assignee.assigneeType,
@@ -3589,7 +3624,7 @@ async function buildCreateUserTaskInput(masterKey, input) {
3589
3624
  linked_project_ids: linkedProjectIds,
3590
3625
  plan_id: input.planId ?? null,
3591
3626
  due_at: input.dueAt ?? null,
3592
- priority: 0,
3627
+ priority: normalizeTaskPriority(input.priority) ?? 0,
3593
3628
  position: timestamp,
3594
3629
  created_at: timestamp,
3595
3630
  updated_at: timestamp
@@ -3612,20 +3647,32 @@ async function buildUpdateUserTaskInput(task, masterKey, input) {
3612
3647
  patch.encrypted_linked_project_ids = await encryptWithAesGcmCombined(JSON.stringify(input.projectIds), taskKey);
3613
3648
  }
3614
3649
  if (input.planId !== void 0) patch.plan_id = input.planId;
3650
+ const priority = normalizeTaskPriority(input.priority);
3651
+ if (priority !== void 0) patch.priority = priority;
3652
+ const replaceLabels = input.labels ?? input.tags;
3653
+ if (replaceLabels !== void 0 || input.addLabels !== void 0 || input.addTags !== void 0 || input.removeLabels !== void 0 || input.removeTags !== void 0) {
3654
+ const remove = new Set(normalizeLabels([...input.removeLabels ?? [], ...input.removeTags ?? []]));
3655
+ const base = replaceLabels !== void 0 ? normalizeLabels(replaceLabels) : task.labels;
3656
+ const labels = normalizeLabels([...base.filter((label) => !remove.has(label)), ...input.addLabels ?? [], ...input.addTags ?? []]);
3657
+ patch.encrypted_labels = await encryptWithAesGcmCombined(JSON.stringify(labels), taskKey);
3658
+ patch.encrypted_tags = await encryptWithAesGcmCombined(JSON.stringify(labels), taskKey);
3659
+ patch.label_hashes = labelHashes(masterKey, labels);
3660
+ }
3615
3661
  return patch;
3616
3662
  }
3617
3663
  async function decryptUserTask(record, masterKey) {
3618
3664
  if (record.source === "workflow_run") return workflowProjectionToTask(record);
3619
3665
  if (typeof record.version !== "number") throw new Error(`Task ${record.task_id} is missing version.`);
3620
3666
  const taskKey = await taskKeyFromRecord(record, masterKey);
3621
- const tags = parseStringArray(await decryptOptional(record.encrypted_tags, taskKey));
3667
+ const labels = parseStringArray(await decryptOptional(record.encrypted_labels || record.encrypted_tags, taskKey));
3622
3668
  const linkedProjectIds = parseStringArray(await decryptOptional(record.encrypted_linked_project_ids, taskKey));
3623
3669
  return {
3624
3670
  taskId: record.task_id,
3625
3671
  shortId: record.short_id || deriveShortId(record),
3626
3672
  title: await decryptOptional(record.encrypted_title, taskKey) || "(untitled task)",
3627
3673
  description: await decryptOptional(record.encrypted_description, taskKey),
3628
- tags,
3674
+ labels,
3675
+ tags: labels,
3629
3676
  latestInstruction: await decryptOptional(record.encrypted_latest_instruction, taskKey),
3630
3677
  status: record.status,
3631
3678
  assigneeType: record.assignee_type,
@@ -3635,6 +3682,7 @@ async function decryptUserTask(record, masterKey) {
3635
3682
  planId: record.plan_id ?? null,
3636
3683
  dueAt: record.due_at ?? null,
3637
3684
  priority: record.priority ?? 0,
3685
+ priorityLevel: taskPriorityLevel(record.priority),
3638
3686
  position: record.position ?? 0,
3639
3687
  queueState: record.queue_state ?? "none",
3640
3688
  blockedReasonCode: record.blocked_reason_code ?? null,
@@ -3651,6 +3699,7 @@ function workflowProjectionToTask(record) {
3651
3699
  shortId: record.short_id || workflowProjectionShortId(record),
3652
3700
  title: record.title || "Workflow run",
3653
3701
  description: record.blocked_message ?? "",
3702
+ labels: [],
3654
3703
  tags: [],
3655
3704
  latestInstruction: "",
3656
3705
  status: record.status,
@@ -3661,6 +3710,7 @@ function workflowProjectionToTask(record) {
3661
3710
  planId: null,
3662
3711
  dueAt: record.due_at ?? null,
3663
3712
  priority: record.priority ?? 0,
3713
+ priorityLevel: taskPriorityLevel(record.priority),
3664
3714
  position: record.position ?? 0,
3665
3715
  queueState: String(record.run_status ?? "workflow"),
3666
3716
  blockedReasonCode: blockedReason,
@@ -3690,9 +3740,9 @@ function findTask(tasks, id) {
3690
3740
  }
3691
3741
  function renderTaskList(tasks) {
3692
3742
  if (tasks.length === 0) return "No tasks found.";
3693
- const lines = ["Tasks", "ID Status Assignee Queue Title"];
3743
+ const lines = ["Tasks", "ID Status Priority Labels Title"];
3694
3744
  for (const task of tasks) {
3695
- lines.push(`${pad(task.shortId, 9)} ${pad(task.status, 12)} ${pad(assigneeLabel(task), 11)} ${pad(task.queueState, 11)} ${task.title}`);
3745
+ lines.push(`${pad(task.shortId, 9)} ${pad(task.status, 12)} ${pad(task.priorityLevel, 9)} ${pad(task.labels.join(","), 19)} ${task.title}`);
3696
3746
  }
3697
3747
  return lines.join("\n");
3698
3748
  }
@@ -3701,11 +3751,13 @@ function renderTaskDetail(task) {
3701
3751
  `Task ${task.shortId}`,
3702
3752
  `Title: ${task.title}`,
3703
3753
  `Status: ${task.status}`,
3754
+ `Priority: ${task.priorityLevel}`,
3704
3755
  `Assignee: ${assigneeLabel(task)}`,
3705
3756
  `Queue: ${task.queueState}`,
3706
3757
  `Task ID: ${task.taskId}`
3707
3758
  ];
3708
3759
  if (task.description) lines.push(`Description: ${task.description}`);
3760
+ if (task.labels.length > 0) lines.push(`Labels: ${task.labels.join(", ")}`);
3709
3761
  if (task.primaryChatId) lines.push(`Chat: ${task.primaryChatId}`);
3710
3762
  if (task.linkedProjectIds.length > 0) lines.push(`Projects: ${task.linkedProjectIds.join(", ")}`);
3711
3763
  if (task.planId) lines.push(`Plan: ${task.planId}`);
@@ -4035,7 +4087,10 @@ function assertTaskPersistPayloadEncrypted(payload) {
4035
4087
  "blocked_reason_code",
4036
4088
  "created_at",
4037
4089
  "key_wrappers",
4090
+ "label_hashes",
4038
4091
  "linked_project_ids",
4092
+ "plan_id",
4093
+ "plan_step_id",
4039
4094
  "position",
4040
4095
  "primary_chat_id",
4041
4096
  "priority",
@@ -4854,7 +4909,7 @@ var OpenMatesClient = class _OpenMatesClient {
4854
4909
  if (!userId) {
4855
4910
  throw new Error("Could not resolve current user id for local connected-account connector.");
4856
4911
  }
4857
- const { createHash: createHash9 } = await import("crypto");
4912
+ const { createHash: createHash10 } = await import("crypto");
4858
4913
  const accountId = randomUUID5();
4859
4914
  const masterKey = this.getMasterKeyBytes();
4860
4915
  const encryptLocalConnectorValue = async (value) => {
@@ -4863,9 +4918,9 @@ var OpenMatesClient = class _OpenMatesClient {
4863
4918
  };
4864
4919
  const payload = {
4865
4920
  id: accountId,
4866
- hashed_user_id: createHash9("sha256").update(userId).digest("hex"),
4921
+ hashed_user_id: createHash10("sha256").update(userId).digest("hex"),
4867
4922
  encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
4868
- provider_type_hash: createHash9("sha256").update(input.provider_id).digest("hex"),
4923
+ provider_type_hash: createHash10("sha256").update(input.provider_id).digest("hex"),
4869
4924
  encrypted_account_label: await encryptLocalConnectorValue(input.label),
4870
4925
  encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
4871
4926
  encrypted_app_permissions: await encryptLocalConnectorValue({
@@ -5306,14 +5361,26 @@ var OpenMatesClient = class _OpenMatesClient {
5306
5361
  async decryptChatKey(encryptedChatKey, masterKey) {
5307
5362
  return decryptBytesWithAesGcm(encryptedChatKey, masterKey);
5308
5363
  }
5364
+ getMasterWrapperEncryptedChatKey(cache, chatId) {
5365
+ const hashedChatId = createHash6("sha256").update(chatId).digest("hex");
5366
+ const wrapper = (cache?.chatKeyWrappers ?? []).find(
5367
+ (entry) => entry.key_type === "master" && entry.hashed_chat_id === hashedChatId && typeof entry.encrypted_chat_key === "string"
5368
+ );
5369
+ return typeof wrapper?.encrypted_chat_key === "string" ? wrapper.encrypted_chat_key : null;
5370
+ }
5371
+ async resolveChatKey(cache, cached, masterKey) {
5372
+ const chatId = String(cached.details.id ?? "");
5373
+ const wrapperKey = chatId ? this.getMasterWrapperEncryptedChatKey(cache, chatId) : null;
5374
+ const encryptedChatKey = wrapperKey ?? (typeof cached.details.encrypted_chat_key === "string" ? cached.details.encrypted_chat_key : null);
5375
+ return encryptedChatKey ? this.decryptChatKey(encryptedChatKey, masterKey) : null;
5376
+ }
5309
5377
  /**
5310
5378
  * Decrypt a single chat record from the sync cache into a ChatListItem.
5311
5379
  */
5312
- async decryptChatListItem(cached, masterKey) {
5380
+ async decryptChatListItem(cached, masterKey, cache = loadSyncCache()) {
5313
5381
  const d = cached.details;
5314
5382
  const id = String(d.id ?? "");
5315
- const encKey = typeof d.encrypted_chat_key === "string" ? d.encrypted_chat_key : null;
5316
- const chatKeyBytes = encKey ? await this.decryptChatKey(encKey, masterKey) : null;
5383
+ const chatKeyBytes = await this.resolveChatKey(cache, cached, masterKey);
5317
5384
  const title = typeof d.encrypted_title === "string" && chatKeyBytes ? await decryptWithAesGcmCombined(d.encrypted_title, chatKeyBytes) : null;
5318
5385
  const summary = typeof d.encrypted_chat_summary === "string" && chatKeyBytes ? await decryptWithAesGcmCombined(
5319
5386
  d.encrypted_chat_summary,
@@ -5338,7 +5405,7 @@ var OpenMatesClient = class _OpenMatesClient {
5338
5405
  const slice = cache.chats.slice(offset, offset + limit);
5339
5406
  const output = [];
5340
5407
  for (const chat of slice) {
5341
- output.push(await this.decryptChatListItem(chat, masterKey));
5408
+ output.push(await this.decryptChatListItem(chat, masterKey, cache));
5342
5409
  }
5343
5410
  return {
5344
5411
  chats: output,
@@ -5547,7 +5614,7 @@ var OpenMatesClient = class _OpenMatesClient {
5547
5614
  }
5548
5615
  if (!found) {
5549
5616
  for (const cached of cache.chats) {
5550
- const item = await this.decryptChatListItem(cached, masterKey);
5617
+ const item = await this.decryptChatListItem(cached, masterKey, cache);
5551
5618
  const title = (item.title ?? "").toLowerCase();
5552
5619
  if (title === normalized) {
5553
5620
  found = cached;
@@ -5570,9 +5637,8 @@ var OpenMatesClient = class _OpenMatesClient {
5570
5637
  `Chat '${query}' not found. Try 'openmates chats search "${query}"' to browse matches.`
5571
5638
  );
5572
5639
  }
5573
- const chatItem = await this.decryptChatListItem(found, masterKey);
5574
- const encKey = typeof found.details.encrypted_chat_key === "string" ? found.details.encrypted_chat_key : null;
5575
- const chatKeyBytes = encKey ? await this.decryptChatKey(encKey, masterKey) : null;
5640
+ const chatItem = await this.decryptChatListItem(found, masterKey, cache);
5641
+ const chatKeyBytes = await this.resolveChatKey(cache, found, masterKey);
5576
5642
  const messages = [];
5577
5643
  for (const raw of found.messages) {
5578
5644
  const m = typeof raw === "string" ? JSON.parse(raw) : raw;
@@ -5591,8 +5657,8 @@ var OpenMatesClient = class _OpenMatesClient {
5591
5657
  );
5592
5658
  let msgEmbedIds = [];
5593
5659
  if (clientMsgId && cache.embeds.length > 0) {
5594
- const { createHash: createHash9 } = await import("crypto");
5595
- const hashed = createHash9("sha256").update(clientMsgId).digest("hex");
5660
+ const { createHash: createHash10 } = await import("crypto");
5661
+ const hashed = createHash10("sha256").update(clientMsgId).digest("hex");
5596
5662
  msgEmbedIds = cache.embeds.filter(
5597
5663
  (e) => e.hashed_message_id === hashed && // Only include parent embeds (no parent_embed_id).
5598
5664
  // Child embeds inherit the parent's key and are loaded
@@ -5639,8 +5705,8 @@ var OpenMatesClient = class _OpenMatesClient {
5639
5705
  );
5640
5706
  }
5641
5707
  const embedId = String(embed.embed_id ?? embed.id ?? "");
5642
- const { createHash: createHash9 } = await import("crypto");
5643
- const hashedEmbedId = createHash9("sha256").update(embedId).digest("hex");
5708
+ const { createHash: createHash10 } = await import("crypto");
5709
+ const hashedEmbedId = createHash10("sha256").update(embedId).digest("hex");
5644
5710
  const embedKeyBytes = await this.resolveEmbedKey(
5645
5711
  cache,
5646
5712
  masterKey,
@@ -5748,7 +5814,7 @@ var OpenMatesClient = class _OpenMatesClient {
5748
5814
  async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
5749
5815
  if (visited.has(embedId)) return null;
5750
5816
  visited.add(embedId);
5751
- const { createHash: createHash9 } = await import("crypto");
5817
+ const { createHash: createHash10 } = await import("crypto");
5752
5818
  const masterKeyEntry = cache.embedKeys.find(
5753
5819
  (ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
5754
5820
  );
@@ -5765,7 +5831,7 @@ var OpenMatesClient = class _OpenMatesClient {
5765
5831
  if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
5766
5832
  const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
5767
5833
  const owningChat = cache.chats.find((c) => {
5768
- const chatHash = createHash9("sha256").update(String(c.details.id ?? "")).digest("hex");
5834
+ const chatHash = createHash10("sha256").update(String(c.details.id ?? "")).digest("hex");
5769
5835
  return chatHash === hashedChatId;
5770
5836
  });
5771
5837
  if (owningChat) {
@@ -5794,7 +5860,7 @@ var OpenMatesClient = class _OpenMatesClient {
5794
5860
  const parentFullId = String(
5795
5861
  parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
5796
5862
  );
5797
- const parentHashedId = createHash9("sha256").update(parentFullId).digest("hex");
5863
+ const parentHashedId = createHash10("sha256").update(parentFullId).digest("hex");
5798
5864
  const parentKey = await this.resolveEmbedKey(
5799
5865
  cache,
5800
5866
  masterKey,
@@ -5886,9 +5952,7 @@ var OpenMatesClient = class _OpenMatesClient {
5886
5952
  (c) => String(c.details.id ?? "") === chatId || String(c.details.id ?? "").startsWith(chatId)
5887
5953
  );
5888
5954
  if (!found) return [];
5889
- const encKey = typeof found.details.encrypted_chat_key === "string" ? found.details.encrypted_chat_key : null;
5890
- if (!encKey) return [];
5891
- const chatKeyBytes = await this.decryptChatKey(encKey, masterKey);
5955
+ const chatKeyBytes = await this.resolveChatKey(cache, found, masterKey);
5892
5956
  if (!chatKeyBytes) return [];
5893
5957
  const encSuggestions = typeof found.details.encrypted_follow_up_request_suggestions === "string" ? found.details.encrypted_follow_up_request_suggestions : null;
5894
5958
  if (!encSuggestions) return [];
@@ -7783,6 +7847,8 @@ var OpenMatesClient = class _OpenMatesClient {
7783
7847
  if (filters.status) params.set("status", filters.status);
7784
7848
  if (filters.chatId) params.set("chat_id", filters.chatId);
7785
7849
  if (filters.projectId) params.set("project_id", filters.projectId);
7850
+ for (const labelHash of filters.labelHashes ?? []) params.append("label_hash", labelHash);
7851
+ if (filters.priority !== void 0) params.set("priority", String(filters.priority));
7786
7852
  const limit = filters.limit;
7787
7853
  if (Number.isSafeInteger(limit) && limit !== void 0 && limit > 0) params.set("limit", String(limit));
7788
7854
  const query = params.toString();
@@ -8837,7 +8903,7 @@ Required: ${schema.required.join(", ")}`
8837
8903
  const session = this.requireSession();
8838
8904
  const masterKey = base64ToBytes(session.masterKeyExportedB64);
8839
8905
  const cache = await this.ensureSynced();
8840
- const { createHash: createHash9 } = await import("crypto");
8906
+ const { createHash: createHash10 } = await import("crypto");
8841
8907
  const embed = cache.embeds.find(
8842
8908
  (e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
8843
8909
  );
@@ -8845,7 +8911,7 @@ Required: ${schema.required.join(", ")}`
8845
8911
  throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
8846
8912
  }
8847
8913
  const embedId = String(embed.embed_id ?? embed.id ?? "");
8848
- const hashedEmbedId = createHash9("sha256").update(embedId).digest("hex");
8914
+ const hashedEmbedId = createHash10("sha256").update(embedId).digest("hex");
8849
8915
  const embedKeyBytes = await this.resolveEmbedKey(
8850
8916
  cache,
8851
8917
  masterKey,
@@ -8953,8 +9019,8 @@ Required: ${schema.required.join(", ")}`
8953
9019
  if (!embed) {
8954
9020
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
8955
9021
  }
8956
- const { createHash: createHash9 } = await import("crypto");
8957
- const hashedEmbedId = createHash9("sha256").update(embedId).digest("hex");
9022
+ const { createHash: createHash10 } = await import("crypto");
9023
+ const hashedEmbedId = createHash10("sha256").update(embedId).digest("hex");
8958
9024
  const embedKey = await this.resolveEmbedKey(
8959
9025
  cache,
8960
9026
  masterKey,
@@ -9048,8 +9114,8 @@ Required: ${schema.required.join(", ")}`
9048
9114
  if (!embed) {
9049
9115
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
9050
9116
  }
9051
- const { createHash: createHash9 } = await import("crypto");
9052
- const hashedEmbedId = createHash9("sha256").update(embedId).digest("hex");
9117
+ const { createHash: createHash10 } = await import("crypto");
9118
+ const hashedEmbedId = createHash10("sha256").update(embedId).digest("hex");
9053
9119
  const embedKey = await this.resolveEmbedKey(
9054
9120
  cache,
9055
9121
  masterKey,
@@ -9321,6 +9387,7 @@ Required: ${schema.required.join(", ")}`
9321
9387
  const chats = [];
9322
9388
  const embeds = [];
9323
9389
  const embedKeys = [];
9390
+ const chatKeyWrappers = [];
9324
9391
  let newChatSuggestions = [];
9325
9392
  let totalChatCount = 0;
9326
9393
  let reconciliation = { authoritative: false };
@@ -9348,6 +9415,7 @@ Required: ${schema.required.join(", ")}`
9348
9415
  }
9349
9416
  if (p.embeds) embeds.push(...p.embeds);
9350
9417
  if (p.embed_keys) embedKeys.push(...p.embed_keys);
9418
+ if (p.chat_key_wrappers) chatKeyWrappers.push(...p.chat_key_wrappers);
9351
9419
  } else if (frame.type === "phase_2_last_20_chats_ready") {
9352
9420
  const p = frame.payload;
9353
9421
  totalChatCount = p.total_chat_count ?? 0;
@@ -9361,6 +9429,7 @@ Required: ${schema.required.join(", ")}`
9361
9429
  if (!details || typeof details.id !== "string") continue;
9362
9430
  chats.push({ details, messages: [] });
9363
9431
  }
9432
+ if (p.chat_key_wrappers) chatKeyWrappers.push(...p.chat_key_wrappers);
9364
9433
  } else if (frame.type === "background_message_sync") {
9365
9434
  const p = frame.payload;
9366
9435
  for (const c of p.chats ?? []) {
@@ -9370,6 +9439,7 @@ Required: ${schema.required.join(", ")}`
9370
9439
  }
9371
9440
  if (p.embeds) embeds.push(...p.embeds);
9372
9441
  if (p.embed_keys) embedKeys.push(...p.embed_keys);
9442
+ if (p.chat_key_wrappers) chatKeyWrappers.push(...p.chat_key_wrappers);
9373
9443
  } else if (frame.type === "pending_ai_response") {
9374
9444
  pendingAIResponses.push(frame.payload);
9375
9445
  }
@@ -9435,18 +9505,29 @@ Required: ${schema.required.join(", ")}`
9435
9505
  embedKeys.push(cached);
9436
9506
  }
9437
9507
  }
9508
+ const serverChatKeyWrapperIds = new Set(
9509
+ chatKeyWrappers.map((entry) => String(entry.id ?? ""))
9510
+ );
9511
+ for (const cached of existingCache.chatKeyWrappers ?? []) {
9512
+ const cachedId = String(cached.id ?? "");
9513
+ if (cachedId && !serverChatKeyWrapperIds.has(cachedId)) {
9514
+ chatKeyWrappers.push(cached);
9515
+ }
9516
+ }
9438
9517
  }
9439
9518
  const reconciledChats = reconcileAuthoritativeChats(chats, reconciliation);
9440
9519
  chats.splice(0, chats.length, ...reconciledChats);
9441
9520
  if (reconciliation.deleted_chat_ids?.length) {
9442
- const { createHash: createHash9 } = await import("crypto");
9521
+ const { createHash: createHash10 } = await import("crypto");
9443
9522
  const deletedHashes = new Set(
9444
- reconciliation.deleted_chat_ids.map((id) => createHash9("sha256").update(id).digest("hex"))
9523
+ reconciliation.deleted_chat_ids.map((id) => createHash10("sha256").update(id).digest("hex"))
9445
9524
  );
9446
9525
  const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
9447
9526
  const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
9527
+ const keptChatKeyWrappers = chatKeyWrappers.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
9448
9528
  embeds.splice(0, embeds.length, ...keptEmbeds);
9449
9529
  embedKeys.splice(0, embedKeys.length, ...keptKeys);
9530
+ chatKeyWrappers.splice(0, chatKeyWrappers.length, ...keptChatKeyWrappers);
9450
9531
  }
9451
9532
  chats.sort(
9452
9533
  (a, b) => (typeof b.details.last_edited_overall_timestamp === "number" ? b.details.last_edited_overall_timestamp : 0) - (typeof a.details.last_edited_overall_timestamp === "number" ? a.details.last_edited_overall_timestamp : 0)
@@ -9478,6 +9559,7 @@ Required: ${schema.required.join(", ")}`
9478
9559
  chats,
9479
9560
  embeds,
9480
9561
  embedKeys,
9562
+ chatKeyWrappers,
9481
9563
  newChatSuggestions
9482
9564
  };
9483
9565
  saveSyncCache(cache);
@@ -9509,9 +9591,7 @@ Required: ${schema.required.join(", ")}`
9509
9591
  } catch {
9510
9592
  }
9511
9593
  }
9512
- const encryptedChatKey = typeof chat.details.encrypted_chat_key === "string" ? chat.details.encrypted_chat_key : null;
9513
- if (!encryptedChatKey) continue;
9514
- const chatKeyBytes = await this.decryptChatKey(encryptedChatKey, masterKey);
9594
+ const chatKeyBytes = await this.resolveChatKey(loadSyncCache(), chat, masterKey);
9515
9595
  if (!chatKeyBytes) continue;
9516
9596
  const completedAt = normalizeUnixSeconds(
9517
9597
  pending.fired_at,
@@ -13625,7 +13705,7 @@ var GeneratedAppSkills = class {
13625
13705
 
13626
13706
  // src/sdk.ts
13627
13707
  import { decode as toonDecode } from "@toon-format/toon";
13628
- import { createHash as createHash6, randomBytes as randomBytes3, randomUUID as randomUUID6 } from "crypto";
13708
+ import { createHash as createHash7, randomBytes as randomBytes3, randomUUID as randomUUID6 } from "crypto";
13629
13709
  import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
13630
13710
  import { homedir as homedir4 } from "os";
13631
13711
  import { dirname, join as join3 } from "path";
@@ -13763,15 +13843,11 @@ var OpenMates = class {
13763
13843
  }
13764
13844
  async resolveEmbedKeyForShare(embedKeys, embedId) {
13765
13845
  const masterKey = await this.getMasterKey();
13766
- const hashedEmbedId = createHash6("sha256").update(embedId).digest("hex");
13846
+ const hashedEmbedId = createHash7("sha256").update(embedId).digest("hex");
13767
13847
  return this.resolveLoadedEmbedKey(embedKeys, hashedEmbedId, masterKey, masterKey);
13768
13848
  }
13769
- async decryptChatMetadata(chat) {
13770
- if (typeof chat.encrypted_chat_key !== "string") {
13771
- return chat;
13772
- }
13773
- const masterKey = await this.getMasterKey();
13774
- const chatKey = await decryptBytesWithAesGcm(chat.encrypted_chat_key, masterKey);
13849
+ async decryptChatMetadata(chat, chatKeyWrappers) {
13850
+ const chatKey = await this.resolveLoadedChatKey(chat, chatKeyWrappers);
13775
13851
  if (!chatKey) {
13776
13852
  return chat;
13777
13853
  }
@@ -13793,8 +13869,9 @@ var OpenMates = class {
13793
13869
  return payload;
13794
13870
  }
13795
13871
  const chatMetadata = chat;
13796
- const decryptedChat = await this.decryptChatMetadata(chatMetadata);
13797
- const chatKey = typeof chatMetadata.encrypted_chat_key === "string" ? await decryptBytesWithAesGcm(chatMetadata.encrypted_chat_key, await this.getMasterKey()) : null;
13872
+ const chatKeyWrappers = Array.isArray(payload.chat_key_wrappers) ? payload.chat_key_wrappers : Array.isArray(chatMetadata.chat_key_wrappers) ? chatMetadata.chat_key_wrappers : [];
13873
+ const decryptedChat = await this.decryptChatMetadata(chatMetadata, chatKeyWrappers);
13874
+ const chatKey = await this.resolveLoadedChatKey(chatMetadata, chatKeyWrappers);
13798
13875
  if (!chatKey || !Array.isArray(payload.messages)) {
13799
13876
  return { ...payload, chat: decryptedChat };
13800
13877
  }
@@ -13821,6 +13898,15 @@ var OpenMates = class {
13821
13898
  ) : payload.embeds;
13822
13899
  return { ...payload, chat: decryptedChat, messages, embeds };
13823
13900
  }
13901
+ async resolveLoadedChatKey(chat, chatKeyWrappers) {
13902
+ const masterKey = await this.getMasterKey();
13903
+ const hashedChatId = createHash7("sha256").update(chat.id).digest("hex");
13904
+ const wrapper = (chatKeyWrappers ?? []).find(
13905
+ (entry) => entry.key_type === "master" && entry.hashed_chat_id === hashedChatId && typeof entry.encrypted_chat_key === "string"
13906
+ );
13907
+ const encryptedChatKey = typeof wrapper?.encrypted_chat_key === "string" ? wrapper.encrypted_chat_key : typeof chat.encrypted_chat_key === "string" ? chat.encrypted_chat_key : null;
13908
+ return encryptedChatKey ? decryptBytesWithAesGcm(encryptedChatKey, masterKey) : null;
13909
+ }
13824
13910
  async decryptLoadedChatEmbeds(embeds, embedKeys, chatKey) {
13825
13911
  const masterKey = await this.getMasterKey();
13826
13912
  return Promise.all(embeds.map(async (embed) => {
@@ -13828,7 +13914,7 @@ var OpenMates = class {
13828
13914
  if (!embedId) {
13829
13915
  return { ...embed };
13830
13916
  }
13831
- const hashedEmbedId = createHash6("sha256").update(embedId).digest("hex");
13917
+ const hashedEmbedId = createHash7("sha256").update(embedId).digest("hex");
13832
13918
  const embedKey = await this.resolveLoadedEmbedKey(embedKeys, hashedEmbedId, masterKey, chatKey);
13833
13919
  if (!embedKey) {
13834
13920
  return { ...embed };
@@ -13951,7 +14037,12 @@ function loadOrCreateDeviceId(customPath) {
13951
14037
  function withQuery(path, query = {}) {
13952
14038
  const params = new URLSearchParams();
13953
14039
  for (const [key, value] of Object.entries(query)) {
13954
- if (value !== void 0 && value !== null) params.set(key, String(value));
14040
+ if (value === void 0 || value === null) continue;
14041
+ if (Array.isArray(value)) {
14042
+ for (const item of value) params.append(key, String(item));
14043
+ } else {
14044
+ params.set(key, String(value));
14045
+ }
13955
14046
  }
13956
14047
  const serialized = params.toString();
13957
14048
  return serialized ? `${path}?${serialized}` : path;
@@ -14601,29 +14692,171 @@ var OpenMatesTasks = class {
14601
14692
  this.client = client;
14602
14693
  }
14603
14694
  async list(filters = {}) {
14695
+ return (await this.listInternal(filters)).map(toPublicTask);
14696
+ }
14697
+ async listDecrypted(filters = {}) {
14698
+ return this.list(filters);
14699
+ }
14700
+ async show(id, filters = {}) {
14701
+ return toPublicTask(await this.resolve(id, filters));
14702
+ }
14703
+ async create(input) {
14704
+ const masterKey = await this.client.masterKey();
14705
+ const created = await this.createRaw(await buildCreateUserTaskInput(masterKey, input));
14706
+ return toPublicTask(await decryptUserTask(created, masterKey));
14707
+ }
14708
+ async update(id, input, filters = {}) {
14709
+ let task = await this.resolve(id, filters);
14710
+ const masterKey = await this.client.masterKey();
14711
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14712
+ try {
14713
+ const updated = await this.updateRaw(task.taskId, await buildUpdateUserTaskInput(task, masterKey, input));
14714
+ return toPublicTask(await decryptUserTask(updated, masterKey));
14715
+ } catch (error) {
14716
+ if (attempt > 0 || !isTaskVersionConflict(error)) throw error;
14717
+ await delay(1e3);
14718
+ task = await this.resolve(id, filters);
14719
+ }
14720
+ }
14721
+ throw new OpenMatesConfigError("Task update retry failed unexpectedly");
14722
+ }
14723
+ async edit(id, input, filters = {}) {
14724
+ return this.update(id, input, filters);
14725
+ }
14726
+ async start(id, filters = {}) {
14727
+ let task = await this.resolve(id, filters);
14728
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14729
+ try {
14730
+ const started = await this.startAIRaw(task.taskId, {
14731
+ version: task.version,
14732
+ primary_chat_id: task.primaryChatId ?? void 0,
14733
+ linked_project_ids: task.linkedProjectIds,
14734
+ plaintext_title: task.title,
14735
+ plaintext_description: task.description,
14736
+ plaintext_latest_instruction: task.latestInstruction
14737
+ });
14738
+ return toPublicTask(await decryptUserTask(started, await this.client.masterKey()));
14739
+ } catch (error) {
14740
+ if (attempt > 0 || !isTaskVersionConflict(error)) throw error;
14741
+ await delay(1e3);
14742
+ task = await this.resolve(id, filters);
14743
+ }
14744
+ }
14745
+ throw new OpenMatesConfigError("Task start retry failed unexpectedly");
14746
+ }
14747
+ async startAI(id, filters = {}) {
14748
+ return this.start(id, filters);
14749
+ }
14750
+ async delete(id, options = {}) {
14751
+ requireConfirmed(options, "Deleting a task");
14752
+ let task = await this.resolve(id, options.filters ?? {});
14753
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14754
+ try {
14755
+ return await this.client.delete(`/v1/user-tasks/${encodeURIComponent(task.taskId)}?version=${encodeURIComponent(String(task.version))}`);
14756
+ } catch (error) {
14757
+ if (attempt > 0 || !isTaskVersionConflict(error)) throw error;
14758
+ await delay(1e3);
14759
+ task = await this.resolve(id, options.filters ?? {});
14760
+ }
14761
+ }
14762
+ throw new OpenMatesConfigError("Task delete retry failed unexpectedly");
14763
+ }
14764
+ async done(id, filters = {}) {
14765
+ return this.actionById(id, "complete", {}, filters);
14766
+ }
14767
+ async complete(id, filters = {}) {
14768
+ return this.done(id, filters);
14769
+ }
14770
+ async block(id, reason, filters = {}) {
14771
+ return this.actionById(id, "block", { blocked_reason_code: reason }, filters);
14772
+ }
14773
+ async unblock(id, filters = {}) {
14774
+ return this.actionById(id, "unblock", {}, filters);
14775
+ }
14776
+ async skip(id, filters = {}) {
14777
+ return this.actionById(id, "skip", {}, filters);
14778
+ }
14779
+ async reorder(id, move, filters = {}) {
14780
+ let task = await this.resolve(id, filters);
14781
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14782
+ try {
14783
+ const response = await this.client.request("/v1/user-tasks/reorder", {
14784
+ moves: [{ ...move, task_id: task.taskId, version: task.version }]
14785
+ });
14786
+ return (await decryptUserTasks(response.tasks ?? [], await this.client.masterKey())).map(toPublicTask);
14787
+ } catch (error) {
14788
+ if (attempt > 0 || !isTaskVersionConflict(error)) throw error;
14789
+ await delay(1e3);
14790
+ task = await this.resolve(id, filters);
14791
+ }
14792
+ }
14793
+ throw new OpenMatesConfigError("Task reorder retry failed unexpectedly");
14794
+ }
14795
+ async move(id, move, filters = {}) {
14796
+ return this.reorder(id, move, filters);
14797
+ }
14798
+ async listRaw(filters = {}) {
14799
+ const masterKey = filters.labels || filters.tags ? await this.client.masterKey() : void 0;
14604
14800
  const response = await this.client.get(withQuery("/v1/user-tasks", {
14605
14801
  status: filters.status,
14606
14802
  chat_id: filters.chatId,
14607
- project_id: filters.projectId
14803
+ project_id: filters.projectId,
14804
+ label_hash: masterKey ? labelHashes(masterKey, normalizeLabels(filters.labels ?? filters.tags ?? [])) : void 0,
14805
+ priority: normalizeTaskPriority(filters.priority)
14608
14806
  }));
14609
14807
  return response.tasks ?? [];
14610
14808
  }
14611
- async create(input) {
14809
+ async createRaw(input) {
14612
14810
  const response = await this.client.request("/v1/user-tasks", input);
14613
14811
  if (!response.task) throw new OpenMatesApiError(500, { detail: "User task response missing task" });
14614
14812
  return response.task;
14615
14813
  }
14616
- async update(taskId, input) {
14814
+ async updateRaw(taskId, input) {
14617
14815
  const response = await this.client.patch(`/v1/user-tasks/${encodeURIComponent(taskId)}`, input);
14618
14816
  if (!response.task) throw new OpenMatesApiError(500, { detail: "User task response missing task" });
14619
14817
  return response.task;
14620
14818
  }
14621
- async startAI(taskId, input) {
14819
+ async startAIRaw(taskId, input) {
14622
14820
  const response = await this.client.request(`/v1/user-tasks/${encodeURIComponent(taskId)}/start-ai`, input);
14623
14821
  if (!response.task) throw new OpenMatesApiError(500, { detail: "User task response missing task" });
14624
14822
  return response.task;
14625
14823
  }
14824
+ async listInternal(filters) {
14825
+ return decryptUserTasks(await this.listRaw(filters), await this.client.masterKey());
14826
+ }
14827
+ async resolve(id, filters) {
14828
+ return findTask(await this.listInternal(filters), id);
14829
+ }
14830
+ async actionRaw(taskId, action, input) {
14831
+ const response = await this.client.request(`/v1/user-tasks/${encodeURIComponent(taskId)}/${encodeURIComponent(action)}`, input);
14832
+ if (!response.task) throw new OpenMatesApiError(500, { detail: "User task response missing task" });
14833
+ return response.task;
14834
+ }
14835
+ async actionById(id, action, patch, filters) {
14836
+ let task = await this.resolve(id, filters);
14837
+ for (let attempt = 0; attempt < 2; attempt += 1) {
14838
+ try {
14839
+ const updated = await this.actionRaw(task.taskId, action, { version: task.version, ...patch });
14840
+ return toPublicTask(await decryptUserTask(updated, await this.client.masterKey()));
14841
+ } catch (error) {
14842
+ if (attempt > 0 || !isTaskVersionConflict(error)) throw error;
14843
+ await delay(1e3);
14844
+ task = await this.resolve(id, filters);
14845
+ }
14846
+ }
14847
+ throw new OpenMatesConfigError("Task action retry failed unexpectedly");
14848
+ }
14626
14849
  };
14850
+ function toPublicTask(task) {
14851
+ const { encrypted: _encrypted, ...publicTask } = task;
14852
+ return publicTask;
14853
+ }
14854
+ function isTaskVersionConflict(error) {
14855
+ return error instanceof OpenMatesApiError && error.status === 409 && String(JSON.stringify(error.data)).includes("TASK_VERSION_CONFLICT");
14856
+ }
14857
+ function delay(ms) {
14858
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
14859
+ }
14627
14860
  var OpenMatesPlans = class {
14628
14861
  client;
14629
14862
  constructor(client) {
@@ -15767,7 +16000,7 @@ var OutputRedactor = class {
15767
16000
  import { readFileSync as readFileSync5, statSync, existsSync as existsSync6 } from "fs";
15768
16001
  import { basename, extname, resolve as resolve2 } from "path";
15769
16002
  import { homedir as homedir5 } from "os";
15770
- import { createHash as createHash7 } from "crypto";
16003
+ import { createHash as createHash8 } from "crypto";
15771
16004
  var MAX_PER_FILE_SIZE = 100 * 1024 * 1024;
15772
16005
  var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
15773
16006
  ".pem",
@@ -16077,7 +16310,7 @@ function processCodeFile(filePath, filename, redactor) {
16077
16310
  line_count: lineCount
16078
16311
  });
16079
16312
  const textPreview = `${filename} (${language}, ${lineCount} lines)`;
16080
- const contentHash = createHash7("sha256").update(content).digest("hex");
16313
+ const contentHash = createHash8("sha256").update(content).digest("hex");
16081
16314
  const embed = {
16082
16315
  embedId,
16083
16316
  embedRef,
@@ -17632,7 +17865,7 @@ function formatTs(ts) {
17632
17865
 
17633
17866
  // src/server.ts
17634
17867
  import { execFileSync as execFileSync2, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
17635
- import { createHash as createHash8, randomBytes as randomBytes4 } from "crypto";
17868
+ import { createHash as createHash9, randomBytes as randomBytes4 } from "crypto";
17636
17869
  import { chmodSync as chmodSync3, closeSync, copyFileSync, cpSync, existsSync as existsSync7, mkdirSync as mkdirSync4, mkdtempSync, openSync, readFileSync as readFileSync6, readSync, readdirSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
17637
17870
  import { createInterface as createInterface3 } from "readline";
17638
17871
  import { createInterface as createPromptInterface } from "readline/promises";
@@ -18297,7 +18530,7 @@ function packagedCaddyTemplatePath(role) {
18297
18530
  }
18298
18531
  function fileHash(path) {
18299
18532
  if (!existsSync7(path)) return null;
18300
- return createHash8("sha256").update(readFileSync6(path)).digest("hex");
18533
+ return createHash9("sha256").update(readFileSync6(path)).digest("hex");
18301
18534
  }
18302
18535
  async function loadSelfHostComposeTemplate(templateRef, role) {
18303
18536
  const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
@@ -18683,7 +18916,7 @@ function formatSecretPreflight(preflight) {
18683
18916
  return parts.join("; ");
18684
18917
  }
18685
18918
  function hashFile(path) {
18686
- const hash = createHash8("sha256");
18919
+ const hash = createHash9("sha256");
18687
18920
  const fd = openSync(path, "r");
18688
18921
  const buffer = Buffer.allocUnsafe(CHECKSUM_BUFFER_BYTES);
18689
18922
  try {
@@ -61081,11 +61314,11 @@ function decodeBase32(input) {
61081
61314
  return Buffer.from(bytes);
61082
61315
  }
61083
61316
  async function generateTotpCode(secret) {
61084
- const { createHmac } = await import("crypto");
61317
+ const { createHmac: createHmac2 } = await import("crypto");
61085
61318
  const counter = Math.floor(Date.now() / 1e3 / 30);
61086
61319
  const counterBuffer = Buffer.alloc(8);
61087
61320
  counterBuffer.writeBigUInt64BE(BigInt(counter));
61088
- const hmac = createHmac("sha1", decodeBase32(secret)).update(counterBuffer).digest();
61321
+ const hmac = createHmac2("sha1", decodeBase32(secret)).update(counterBuffer).digest();
61089
61322
  const offset = hmac[hmac.length - 1] & 15;
61090
61323
  const code = (hmac.readUInt32BE(offset) & 2147483647) % 1e6;
61091
61324
  return String(code).padStart(6, "0");
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getExtForLang,
4
4
  serializeToYaml
5
- } from "./chunk-GTFUGLRD.js";
5
+ } from "./chunk-7ONUJGJ2.js";
6
6
  import "./chunk-AXNRPVLE.js";
7
7
  export {
8
8
  getExtForLang,
package/dist/index.d.ts CHANGED
@@ -31,6 +31,9 @@ interface CachedEmbed {
31
31
  interface CachedEmbedKey {
32
32
  [key: string]: unknown;
33
33
  }
34
+ interface CachedChatKeyWrapper {
35
+ [key: string]: unknown;
36
+ }
34
37
  interface CachedNewChatSuggestion {
35
38
  [key: string]: unknown;
36
39
  }
@@ -47,6 +50,8 @@ interface SyncCache {
47
50
  embeds: CachedEmbed[];
48
51
  /** Embed keys for embed decryption */
49
52
  embedKeys: CachedEmbedKey[];
53
+ /** Chat key wrappers for wrapper-first chat decryption */
54
+ chatKeyWrappers?: CachedChatKeyWrapper[];
50
55
  /**
51
56
  * New chat suggestions from the last sync.
52
57
  * Each entry has id, chat_id, encrypted_suggestion, created_at.
@@ -559,7 +564,9 @@ interface UserTaskRecord {
559
564
  encrypted_task_key?: string | null;
560
565
  encrypted_title: string;
561
566
  encrypted_description?: string | null;
567
+ encrypted_labels?: string | null;
562
568
  encrypted_tags?: string | null;
569
+ label_hashes?: string[] | null;
563
570
  encrypted_linked_project_ids?: string | null;
564
571
  encrypted_activity_summary?: string | null;
565
572
  encrypted_latest_instruction?: string | null;
@@ -1283,6 +1290,8 @@ declare class OpenMatesClient {
1283
1290
  * encrypt/decrypt the chat's title, category, messages, etc.
1284
1291
  */
1285
1292
  private decryptChatKey;
1293
+ private getMasterWrapperEncryptedChatKey;
1294
+ private resolveChatKey;
1286
1295
  /**
1287
1296
  * Decrypt a single chat record from the sync cache into a ChatListItem.
1288
1297
  */
@@ -1582,6 +1591,8 @@ declare class OpenMatesClient {
1582
1591
  status?: UserTaskStatus;
1583
1592
  chatId?: string;
1584
1593
  projectId?: string;
1594
+ labelHashes?: string[];
1595
+ priority?: number;
1585
1596
  limit?: number;
1586
1597
  }): Promise<UserTaskRecord[]>;
1587
1598
  createUserTask(input: UserTaskCreateInput): Promise<UserTaskRecord>;
@@ -4800,6 +4811,64 @@ declare class GeneratedAppSkills {
4800
4811
  readonly workflows: WorkflowsAppSkills;
4801
4812
  }
4802
4813
 
4814
+ declare const PRIORITY_LEVELS: readonly ["none", "low", "medium", "high", "urgent"];
4815
+ type TaskPriorityLevel = typeof PRIORITY_LEVELS[number];
4816
+ interface DecryptedUserTask {
4817
+ taskId: string;
4818
+ source?: string;
4819
+ shortId: string;
4820
+ title: string;
4821
+ description: string;
4822
+ labels: string[];
4823
+ tags: string[];
4824
+ latestInstruction: string;
4825
+ status: UserTaskStatus;
4826
+ assigneeType: UserTaskAssigneeType;
4827
+ assigneeHash: string | null;
4828
+ primaryChatId: string | null;
4829
+ linkedProjectIds: string[];
4830
+ planId: string | null;
4831
+ dueAt: number | null;
4832
+ priority: number;
4833
+ priorityLevel: TaskPriorityLevel;
4834
+ position: number;
4835
+ queueState: string;
4836
+ blockedReasonCode: string | null;
4837
+ aiExecutionState: string | null;
4838
+ readOnly?: boolean;
4839
+ version: number;
4840
+ encrypted: UserTaskRecord;
4841
+ }
4842
+ interface TaskCreateOptions {
4843
+ title: string;
4844
+ description?: string;
4845
+ labels?: string[];
4846
+ tags?: string[];
4847
+ status?: UserTaskStatus;
4848
+ assign?: string;
4849
+ chatId?: string | null;
4850
+ projectIds?: string[];
4851
+ planId?: string | null;
4852
+ dueAt?: number | null;
4853
+ priority?: TaskPriorityLevel | number | null;
4854
+ }
4855
+ interface TaskUpdateOptions {
4856
+ title?: string;
4857
+ description?: string;
4858
+ labels?: string[];
4859
+ tags?: string[];
4860
+ addLabels?: string[];
4861
+ addTags?: string[];
4862
+ removeLabels?: string[];
4863
+ removeTags?: string[];
4864
+ status?: UserTaskStatus;
4865
+ assign?: string;
4866
+ chatId?: string | null;
4867
+ projectIds?: string[];
4868
+ planId?: string | null;
4869
+ priority?: TaskPriorityLevel | number | null;
4870
+ }
4871
+
4803
4872
  interface OpenMatesOptions {
4804
4873
  apiKey?: string;
4805
4874
  apiUrl?: string;
@@ -4894,6 +4963,7 @@ interface EncryptedChatMetadata {
4894
4963
  id: string;
4895
4964
  encrypted_title?: string;
4896
4965
  encrypted_chat_key?: string;
4966
+ chat_key_wrappers?: ChatKeyWrapperRecord[];
4897
4967
  encrypted_chat_summary?: string;
4898
4968
  encrypted_category?: string;
4899
4969
  title?: string;
@@ -4913,6 +4983,17 @@ interface DraftRecord extends EncryptedDraftRecord {
4913
4983
  markdown: string;
4914
4984
  preview: string | null;
4915
4985
  }
4986
+ type TaskListFilters = {
4987
+ status?: UserTaskStatus;
4988
+ chatId?: string;
4989
+ projectId?: string;
4990
+ labels?: string[];
4991
+ tags?: string[];
4992
+ priority?: TaskPriorityLevel | number | null;
4993
+ };
4994
+ type TaskPlainCreateOptions = TaskCreateOptions;
4995
+ type TaskPlainUpdateOptions = TaskUpdateOptions;
4996
+ type TaskRecord = Omit<DecryptedUserTask, "encrypted">;
4916
4997
  interface SdkSessionResponse {
4917
4998
  user?: {
4918
4999
  id?: string;
@@ -4934,6 +5015,12 @@ interface EmbedKeyRecord {
4934
5015
  encrypted_embed_key?: string;
4935
5016
  [key: string]: unknown;
4936
5017
  }
5018
+ interface ChatKeyWrapperRecord {
5019
+ hashed_chat_id?: string;
5020
+ key_type?: string;
5021
+ encrypted_chat_key?: string;
5022
+ [key: string]: unknown;
5023
+ }
4937
5024
  interface FocusModeSelection {
4938
5025
  appId: string;
4939
5026
  focusModeId: string;
@@ -4991,8 +5078,9 @@ declare class OpenMates {
4991
5078
  masterKey(): Promise<Uint8Array>;
4992
5079
  sdkSession(): Promise<SdkSessionResponse>;
4993
5080
  resolveEmbedKeyForShare(embedKeys: EmbedKeyRecord[], embedId: string): Promise<Uint8Array | null>;
4994
- decryptChatMetadata<T extends EncryptedChatMetadata>(chat: T): Promise<T>;
5081
+ decryptChatMetadata<T extends EncryptedChatMetadata>(chat: T, chatKeyWrappers?: ChatKeyWrapperRecord[]): Promise<T>;
4995
5082
  decryptLoadedChatPayload<T extends Record<string, unknown>>(payload: T): Promise<T>;
5083
+ private resolveLoadedChatKey;
4996
5084
  private decryptLoadedChatEmbeds;
4997
5085
  private resolveLoadedEmbedKey;
4998
5086
  private requestWithMethod;
@@ -5147,14 +5235,35 @@ declare class OpenMatesProjects {
5147
5235
  declare class OpenMatesTasks {
5148
5236
  private readonly client;
5149
5237
  constructor(client: OpenMates);
5150
- list(filters?: {
5151
- status?: UserTaskStatus;
5152
- chatId?: string;
5153
- projectId?: string;
5154
- }): Promise<UserTaskRecord[]>;
5155
- create(input: UserTaskCreateInput): Promise<UserTaskRecord>;
5156
- update(taskId: string, input: UserTaskUpdateInput): Promise<UserTaskRecord>;
5157
- startAI(taskId: string, input: UserTaskStartAIInput): Promise<UserTaskRecord>;
5238
+ list(filters?: TaskListFilters): Promise<TaskRecord[]>;
5239
+ listDecrypted(filters?: TaskListFilters): Promise<TaskRecord[]>;
5240
+ show(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5241
+ create(input: TaskPlainCreateOptions): Promise<TaskRecord>;
5242
+ update(id: string, input: TaskPlainUpdateOptions, filters?: TaskListFilters): Promise<TaskRecord>;
5243
+ edit(id: string, input: TaskPlainUpdateOptions, filters?: TaskListFilters): Promise<TaskRecord>;
5244
+ start(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5245
+ startAI(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5246
+ delete(id: string, options?: ConfirmedMutationOptions & {
5247
+ filters?: TaskListFilters;
5248
+ }): Promise<{
5249
+ deleted?: boolean;
5250
+ task_id?: string;
5251
+ }>;
5252
+ done(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5253
+ complete(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5254
+ block(id: string, reason: string, filters?: TaskListFilters): Promise<TaskRecord>;
5255
+ unblock(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5256
+ skip(id: string, filters?: TaskListFilters): Promise<TaskRecord>;
5257
+ reorder(id: string, move: Omit<UserTaskReorderInput["moves"][number], "task_id" | "version">, filters?: TaskListFilters): Promise<TaskRecord[]>;
5258
+ move(id: string, move: Omit<UserTaskReorderInput["moves"][number], "task_id" | "version">, filters?: TaskListFilters): Promise<TaskRecord[]>;
5259
+ private listRaw;
5260
+ private createRaw;
5261
+ private updateRaw;
5262
+ private startAIRaw;
5263
+ private listInternal;
5264
+ private resolve;
5265
+ private actionRaw;
5266
+ private actionById;
5158
5267
  }
5159
5268
  declare class OpenMatesPlans {
5160
5269
  private readonly client;
@@ -5324,4 +5433,4 @@ type AssistantFeedbackDecision = {
5324
5433
  };
5325
5434
  declare function buildAssistantFeedbackDecision(rating: number): AssistantFeedbackDecision;
5326
5435
 
5327
- export { APP_SKILL_METADATA, ASSISTANT_FEEDBACK_REPORT_TITLE, ASSISTANT_FEEDBACK_THANKS, type ApiKeyCreateOptions, type ApiKeyCreateResult, type ApiKeyRecord, type AssistantFeedbackDecision, type AuthMethodsStatus, type AuthoritativeChatReconciliation, type BackupCodesResult, type BankTransferOrderDetails, type BankTransferStatus, type CachedChat, type CachedNewChatSuggestion, type ChatCreateOptions, type ChatListOptions, type ChatListPage, type ChatResponse, type CliSignupResult, type DecryptedDraft, type DecryptedEmbed, type DecryptedMemoryEntry, type DecryptedMessage, type DecryptedNewChatSuggestion, type DocsFile, type DocsFolder, type DocsSearchResult, type DocsTree, type DraftRecord, type EncryptedChatMetadata, type EncryptedDraft, type EncryptedDraftRecord, type FocusModeSelection, type GiftCardBankTransferStatus, INTEREST_TAG_IDS, type InterestTagId, MATE_NAMES, MEMORY_TYPE_REGISTRY, type MemoryFieldDef, type MemoryTypeDef, OpenMates, OpenMatesApiError, OpenMatesClient, type OpenMatesClientOptions, OpenMatesConfigError, type OpenMatesOptions, type OpenMatesSession, type ProjectSourceCapability, type ProjectSourceCreateInput, type ProjectSourceRecord, type ProjectSourceStatus, type ProjectSourceType, SUPPORT_URL, type SyncCache, type TopicPreferencesPayload, type TotpSetupStartResult, type WorkflowCapability, type WorkflowDetail, type WorkflowEdge, type WorkflowGraph, type WorkflowNode, type WorkflowNodeRun, type WorkflowNodeType, type WorkflowRunContentRetention, type WorkflowRunContentStorage, type WorkflowRunDetail, type WorkflowSummary, buildAssistantFeedbackDecision, defaultCloneBranchForVersion, deriveAppUrl, normalizeInterestTagIds, reconcileAuthoritativeChats, renderSupportInfo };
5436
+ export { APP_SKILL_METADATA, ASSISTANT_FEEDBACK_REPORT_TITLE, ASSISTANT_FEEDBACK_THANKS, type ApiKeyCreateOptions, type ApiKeyCreateResult, type ApiKeyRecord, type AssistantFeedbackDecision, type AuthMethodsStatus, type AuthoritativeChatReconciliation, type BackupCodesResult, type BankTransferOrderDetails, type BankTransferStatus, type CachedChat, type CachedNewChatSuggestion, type ChatCreateOptions, type ChatListOptions, type ChatListPage, type ChatResponse, type CliSignupResult, type DecryptedDraft, type DecryptedEmbed, type DecryptedMemoryEntry, type DecryptedMessage, type DecryptedNewChatSuggestion, type DecryptedUserTask, type DocsFile, type DocsFolder, type DocsSearchResult, type DocsTree, type DraftRecord, type EncryptedChatMetadata, type EncryptedDraft, type EncryptedDraftRecord, type FocusModeSelection, type GiftCardBankTransferStatus, INTEREST_TAG_IDS, type InterestTagId, MATE_NAMES, MEMORY_TYPE_REGISTRY, type MemoryFieldDef, type MemoryTypeDef, OpenMates, OpenMatesApiError, OpenMatesClient, type OpenMatesClientOptions, OpenMatesConfigError, type OpenMatesOptions, type OpenMatesSession, type ProjectSourceCapability, type ProjectSourceCreateInput, type ProjectSourceRecord, type ProjectSourceStatus, type ProjectSourceType, SUPPORT_URL, type SyncCache, type TaskListFilters, type TaskPlainCreateOptions, type TaskPlainUpdateOptions, type TaskRecord, type TopicPreferencesPayload, type TotpSetupStartResult, type WorkflowCapability, type WorkflowDetail, type WorkflowEdge, type WorkflowGraph, type WorkflowNode, type WorkflowNodeRun, type WorkflowNodeType, type WorkflowRunContentRetention, type WorkflowRunContentStorage, type WorkflowRunDetail, type WorkflowSummary, buildAssistantFeedbackDecision, defaultCloneBranchForVersion, deriveAppUrl, normalizeInterestTagIds, reconcileAuthoritativeChats, renderSupportInfo };
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  reconcileAuthoritativeChats,
19
19
  renderSupportInfo,
20
20
  serializeToYaml
21
- } from "./chunk-GTFUGLRD.js";
21
+ } from "./chunk-7ONUJGJ2.js";
22
22
  import "./chunk-AXNRPVLE.js";
23
23
  export {
24
24
  APP_SKILL_METADATA,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmates",
3
- "version": "0.15.0-alpha.21",
3
+ "version": "0.15.0-alpha.23",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",