openmates 0.14.8-alpha.1 → 0.14.8-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-RR6H47VR.js → chunk-WEU7CSZD.js} +794 -264
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +52 -12
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-AXNRPVLE.js";
|
|
5
5
|
|
|
6
6
|
// src/client.ts
|
|
7
|
-
import { randomUUID as
|
|
7
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
8
8
|
import { arch, platform as platform2, release } from "os";
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin, stdout } from "process";
|
|
@@ -1214,6 +1214,31 @@ function websocketProtocolError(envelope) {
|
|
|
1214
1214
|
}
|
|
1215
1215
|
return null;
|
|
1216
1216
|
}
|
|
1217
|
+
function errorFrameBelongsToAiResponse(envelope, userMessageId, chatId, recoveryTurnId) {
|
|
1218
|
+
if (envelope.type === "client_update_required") return true;
|
|
1219
|
+
if (envelope.type !== "error") return true;
|
|
1220
|
+
const payload = envelope.payload && typeof envelope.payload === "object" ? envelope.payload : {};
|
|
1221
|
+
let scoped = false;
|
|
1222
|
+
const errorChatId = typeof payload.chat_id === "string" ? payload.chat_id : null;
|
|
1223
|
+
if (errorChatId) {
|
|
1224
|
+
scoped = true;
|
|
1225
|
+
if (errorChatId !== chatId) return false;
|
|
1226
|
+
}
|
|
1227
|
+
const errorMessageId = typeof payload.user_message_id === "string" ? payload.user_message_id : typeof payload.userMessageId === "string" ? payload.userMessageId : typeof payload.message_id === "string" ? payload.message_id : null;
|
|
1228
|
+
if (errorMessageId) {
|
|
1229
|
+
scoped = true;
|
|
1230
|
+
if (errorMessageId !== userMessageId) return false;
|
|
1231
|
+
}
|
|
1232
|
+
const errorTurnId = typeof payload.turn_id === "string" ? payload.turn_id : null;
|
|
1233
|
+
if (errorTurnId) {
|
|
1234
|
+
scoped = true;
|
|
1235
|
+
if (recoveryTurnId !== errorTurnId) return false;
|
|
1236
|
+
}
|
|
1237
|
+
if (typeof payload.job_id === "string") {
|
|
1238
|
+
scoped = true;
|
|
1239
|
+
}
|
|
1240
|
+
return !scoped || Boolean(errorChatId || errorMessageId || errorTurnId);
|
|
1241
|
+
}
|
|
1217
1242
|
function parseTaskProposals(value) {
|
|
1218
1243
|
if (!Array.isArray(value)) return [];
|
|
1219
1244
|
return value.flatMap((item) => {
|
|
@@ -1241,14 +1266,69 @@ function parseTaskUpdateProposals(value) {
|
|
|
1241
1266
|
return [proposal];
|
|
1242
1267
|
});
|
|
1243
1268
|
}
|
|
1269
|
+
function parseTaskEvent(value) {
|
|
1270
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1271
|
+
const raw = value;
|
|
1272
|
+
if (typeof raw.event_id !== "string" || typeof raw.chat_id !== "string") return null;
|
|
1273
|
+
if (typeof raw.task_id !== "string" || typeof raw.event_type !== "string") return null;
|
|
1274
|
+
const event = {
|
|
1275
|
+
event_id: raw.event_id,
|
|
1276
|
+
chat_id: raw.chat_id,
|
|
1277
|
+
task_id: raw.task_id,
|
|
1278
|
+
event_type: raw.event_type
|
|
1279
|
+
};
|
|
1280
|
+
if (typeof raw.short_id === "string" || raw.short_id === null) event.short_id = raw.short_id;
|
|
1281
|
+
if (typeof raw.title === "string" || raw.title === null) event.title = raw.title;
|
|
1282
|
+
if (typeof raw.status === "string" || raw.status === null) event.status = raw.status;
|
|
1283
|
+
if (typeof raw.reason === "string" || raw.reason === null) event.reason = raw.reason;
|
|
1284
|
+
if (typeof raw.created_at === "number" || raw.created_at === null) event.created_at = raw.created_at;
|
|
1285
|
+
if (typeof raw.task_update_job_id === "string" || raw.task_update_job_id === null) event.task_update_job_id = raw.task_update_job_id;
|
|
1286
|
+
return event;
|
|
1287
|
+
}
|
|
1288
|
+
function parsePendingTaskUpdateJobs(value) {
|
|
1289
|
+
if (!Array.isArray(value)) return [];
|
|
1290
|
+
return value.flatMap((item) => {
|
|
1291
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
1292
|
+
const raw = item;
|
|
1293
|
+
if (typeof raw.job_id !== "string" || typeof raw.task_id !== "string") return [];
|
|
1294
|
+
if (typeof raw.revision !== "number" || typeof raw.task_key_version !== "number" || typeof raw.expires_at !== "number") return [];
|
|
1295
|
+
const job = {
|
|
1296
|
+
job_id: raw.job_id,
|
|
1297
|
+
task_id: raw.task_id,
|
|
1298
|
+
revision: raw.revision,
|
|
1299
|
+
task_key_version: raw.task_key_version,
|
|
1300
|
+
expires_at: raw.expires_at
|
|
1301
|
+
};
|
|
1302
|
+
if (typeof raw.chat_id === "string" || raw.chat_id === null) job.chat_id = raw.chat_id;
|
|
1303
|
+
return [job];
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
function parseAvailableRecoveryJobs(value) {
|
|
1307
|
+
if (!Array.isArray(value)) return [];
|
|
1308
|
+
return value.flatMap((item) => {
|
|
1309
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
1310
|
+
const raw = item;
|
|
1311
|
+
if (typeof raw.job_id !== "string" || typeof raw.chat_id !== "string" || typeof raw.turn_id !== "string" || typeof raw.assistant_message_id !== "string" || typeof raw.chat_key_version !== "number") return [];
|
|
1312
|
+
return [{
|
|
1313
|
+
job_id: raw.job_id,
|
|
1314
|
+
chat_id: raw.chat_id,
|
|
1315
|
+
turn_id: raw.turn_id,
|
|
1316
|
+
assistant_message_id: raw.assistant_message_id,
|
|
1317
|
+
chat_key_version: raw.chat_key_version
|
|
1318
|
+
}];
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1244
1321
|
var OpenMatesWsClient = class {
|
|
1245
1322
|
socket;
|
|
1323
|
+
passiveTaskUpdateJobs = /* @__PURE__ */ new Map();
|
|
1324
|
+
activeResponseCollectors = 0;
|
|
1246
1325
|
constructor(options) {
|
|
1247
1326
|
const wsBase = options.apiUrl.replace(/^http/, "ws").replace(/\/$/, "");
|
|
1248
1327
|
const token = options.wsToken || options.refreshToken || "";
|
|
1249
1328
|
const query = new URLSearchParams({
|
|
1250
1329
|
sessionId: options.sessionId,
|
|
1251
|
-
token
|
|
1330
|
+
token,
|
|
1331
|
+
client_capabilities: "task_update_jobs"
|
|
1252
1332
|
});
|
|
1253
1333
|
const wsHeaders = {};
|
|
1254
1334
|
if (options.userAgent) {
|
|
@@ -1263,6 +1343,10 @@ var OpenMatesWsClient = class {
|
|
|
1263
1343
|
this.socket = new WebSocket(`${wsBase}/v1/ws?${query.toString()}`, {
|
|
1264
1344
|
headers: wsHeaders
|
|
1265
1345
|
});
|
|
1346
|
+
this.socket.on("message", (rawData) => {
|
|
1347
|
+
if (this.activeResponseCollectors > 0) return;
|
|
1348
|
+
this.bufferPassiveTaskUpdateJobs(rawData);
|
|
1349
|
+
});
|
|
1266
1350
|
}
|
|
1267
1351
|
async open(timeoutMs = 1e4) {
|
|
1268
1352
|
await new Promise((resolve7, reject) => {
|
|
@@ -1308,6 +1392,22 @@ var OpenMatesWsClient = class {
|
|
|
1308
1392
|
});
|
|
1309
1393
|
});
|
|
1310
1394
|
}
|
|
1395
|
+
bufferPassiveTaskUpdateJobs(rawData) {
|
|
1396
|
+
try {
|
|
1397
|
+
const parsed = JSON.parse(rawData.toString());
|
|
1398
|
+
if (parsed.type !== "task_update_jobs_available") return;
|
|
1399
|
+
const payload = parsed.payload ?? {};
|
|
1400
|
+
for (const job of parsePendingTaskUpdateJobs(payload.jobs)) {
|
|
1401
|
+
this.passiveTaskUpdateJobs.set(job.job_id, job);
|
|
1402
|
+
}
|
|
1403
|
+
} catch {
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
drainPassiveTaskUpdateJobs() {
|
|
1407
|
+
const jobs = [...this.passiveTaskUpdateJobs.values()];
|
|
1408
|
+
this.passiveTaskUpdateJobs.clear();
|
|
1409
|
+
return jobs;
|
|
1410
|
+
}
|
|
1311
1411
|
waitForMessage(expectedType, predicate, timeoutMs = 2e4) {
|
|
1312
1412
|
return new Promise((resolve7, reject) => {
|
|
1313
1413
|
const seenTypes = /* @__PURE__ */ new Set();
|
|
@@ -1443,6 +1543,14 @@ var OpenMatesWsClient = class {
|
|
|
1443
1543
|
let newChatSuggestions = [];
|
|
1444
1544
|
let taskProposals = [];
|
|
1445
1545
|
let taskUpdateProposals = [];
|
|
1546
|
+
const taskEvents = [];
|
|
1547
|
+
this.activeResponseCollectors += 1;
|
|
1548
|
+
let pendingTaskUpdateJobs = this.drainPassiveTaskUpdateJobs();
|
|
1549
|
+
const mergePendingTaskUpdateJobs = (jobs) => {
|
|
1550
|
+
const byId = new Map(pendingTaskUpdateJobs.map((job) => [job.job_id, job]));
|
|
1551
|
+
for (const job of jobs) byId.set(job.job_id, job);
|
|
1552
|
+
pendingTaskUpdateJobs = [...byId.values()];
|
|
1553
|
+
};
|
|
1446
1554
|
const subChatEvents = [];
|
|
1447
1555
|
const pendingSubChatHandlers = /* @__PURE__ */ new Set();
|
|
1448
1556
|
const pendingMemoryRequestHandlers = /* @__PURE__ */ new Set();
|
|
@@ -1505,6 +1613,8 @@ var OpenMatesWsClient = class {
|
|
|
1505
1613
|
newChatSuggestions,
|
|
1506
1614
|
taskProposals,
|
|
1507
1615
|
taskUpdateProposals,
|
|
1616
|
+
taskEvents,
|
|
1617
|
+
pendingTaskUpdateJobs,
|
|
1508
1618
|
embeds: [...embeds.values()],
|
|
1509
1619
|
subChatEvents,
|
|
1510
1620
|
recoveryJobId
|
|
@@ -1512,6 +1622,7 @@ var OpenMatesWsClient = class {
|
|
|
1512
1622
|
return;
|
|
1513
1623
|
}
|
|
1514
1624
|
if (!aiResponseDone || !postProcessingDone) return;
|
|
1625
|
+
if (options?.recoveryTurnId && !recoveryJobId) return;
|
|
1515
1626
|
if (pendingSubChatHandlers.size > 0) return;
|
|
1516
1627
|
if (pendingMemoryRequestHandlers.size > 0) return;
|
|
1517
1628
|
if (processingEmbedIds.size > 0 && !asyncEmbedTimer) {
|
|
@@ -1528,6 +1639,8 @@ var OpenMatesWsClient = class {
|
|
|
1528
1639
|
newChatSuggestions,
|
|
1529
1640
|
taskProposals,
|
|
1530
1641
|
taskUpdateProposals,
|
|
1642
|
+
taskEvents,
|
|
1643
|
+
pendingTaskUpdateJobs,
|
|
1531
1644
|
embeds: [...embeds.values()],
|
|
1532
1645
|
subChatEvents,
|
|
1533
1646
|
recoveryJobId
|
|
@@ -1548,6 +1661,8 @@ var OpenMatesWsClient = class {
|
|
|
1548
1661
|
newChatSuggestions,
|
|
1549
1662
|
taskProposals,
|
|
1550
1663
|
taskUpdateProposals,
|
|
1664
|
+
taskEvents,
|
|
1665
|
+
pendingTaskUpdateJobs,
|
|
1551
1666
|
embeds: [...embeds.values()],
|
|
1552
1667
|
subChatEvents,
|
|
1553
1668
|
recoveryJobId
|
|
@@ -1624,6 +1739,9 @@ var OpenMatesWsClient = class {
|
|
|
1624
1739
|
const type = parsed.type;
|
|
1625
1740
|
const protocolError = websocketProtocolError(parsed);
|
|
1626
1741
|
if (protocolError) {
|
|
1742
|
+
if (!errorFrameBelongsToAiResponse(parsed, userMessageId, chatId, options?.recoveryTurnId)) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1627
1745
|
cleanup();
|
|
1628
1746
|
reject(protocolError);
|
|
1629
1747
|
return;
|
|
@@ -1654,6 +1772,29 @@ var OpenMatesWsClient = class {
|
|
|
1654
1772
|
}
|
|
1655
1773
|
return;
|
|
1656
1774
|
}
|
|
1775
|
+
if (type === "task_event") {
|
|
1776
|
+
const event = parseTaskEvent(p);
|
|
1777
|
+
if (!event || event.chat_id !== chatId) return;
|
|
1778
|
+
taskEvents.push(event);
|
|
1779
|
+
maybeResolve();
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
if (type === "task_update_jobs_available") {
|
|
1783
|
+
mergePendingTaskUpdateJobs(parsePendingTaskUpdateJobs(p.jobs));
|
|
1784
|
+
maybeResolve();
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
if (type === "recovery_jobs_available") {
|
|
1788
|
+
const job = parseAvailableRecoveryJobs(p.jobs).find(
|
|
1789
|
+
(candidate) => candidate.chat_id === chatId && (!options?.recoveryTurnId || candidate.turn_id === options.recoveryTurnId)
|
|
1790
|
+
);
|
|
1791
|
+
if (!job) return;
|
|
1792
|
+
recoveryJobId = job.job_id;
|
|
1793
|
+
messageId = job.assistant_message_id;
|
|
1794
|
+
if (aiResponseDone) maybeResolve();
|
|
1795
|
+
else scheduleResolve(latestContent);
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1657
1798
|
if (type === "ai_message_update") {
|
|
1658
1799
|
const msgId = p.user_message_id ?? p.userMessageId;
|
|
1659
1800
|
if (msgId !== userMessageId && p.chat_id !== chatId) return;
|
|
@@ -1764,6 +1905,8 @@ var OpenMatesWsClient = class {
|
|
|
1764
1905
|
newChatSuggestions,
|
|
1765
1906
|
taskProposals,
|
|
1766
1907
|
taskUpdateProposals,
|
|
1908
|
+
taskEvents,
|
|
1909
|
+
pendingTaskUpdateJobs,
|
|
1767
1910
|
embeds: [...embeds.values()],
|
|
1768
1911
|
subChatEvents,
|
|
1769
1912
|
recoveryJobId
|
|
@@ -1786,6 +1929,7 @@ var OpenMatesWsClient = class {
|
|
|
1786
1929
|
this.socket.off("message", onMessage);
|
|
1787
1930
|
this.socket.off("error", onError);
|
|
1788
1931
|
this.socket.off("close", onClose);
|
|
1932
|
+
this.activeResponseCollectors = Math.max(0, this.activeResponseCollectors - 1);
|
|
1789
1933
|
};
|
|
1790
1934
|
this.socket.on("message", onMessage);
|
|
1791
1935
|
this.socket.on("error", onError);
|
|
@@ -2514,6 +2658,227 @@ function toArrayBuffer3(input) {
|
|
|
2514
2658
|
return output;
|
|
2515
2659
|
}
|
|
2516
2660
|
|
|
2661
|
+
// src/tasksCli.ts
|
|
2662
|
+
import { createHash as createHash5, randomBytes as randomBytes2, randomUUID as randomUUID3 } from "crypto";
|
|
2663
|
+
var TASK_STATUSES2 = ["backlog", "todo", "in_progress", "blocked", "done"];
|
|
2664
|
+
var DEFAULT_STANDALONE_PREFIX = "TASK";
|
|
2665
|
+
function normalizeTaskStatus(value) {
|
|
2666
|
+
if (value === void 0) return void 0;
|
|
2667
|
+
if (TASK_STATUSES2.includes(value)) return value;
|
|
2668
|
+
throw new Error(`Unknown task status '${value}'. Expected one of: ${TASK_STATUSES2.join(", ")}`);
|
|
2669
|
+
}
|
|
2670
|
+
function parseAssignee(value) {
|
|
2671
|
+
if (!value || value === "user") return { assigneeType: "user", assigneeHash: null };
|
|
2672
|
+
if (["ai", "openmates", "OpenMates"].includes(value)) return { assigneeType: "ai", assigneeHash: null };
|
|
2673
|
+
return { assigneeType: "user", assigneeHash: value };
|
|
2674
|
+
}
|
|
2675
|
+
function splitCsvFlag(value) {
|
|
2676
|
+
if (typeof value !== "string") return [];
|
|
2677
|
+
return value.split(/[,\n]/).map((item) => item.trim()).filter(Boolean);
|
|
2678
|
+
}
|
|
2679
|
+
function parseDueAt(value) {
|
|
2680
|
+
if (value === void 0) return void 0;
|
|
2681
|
+
if (value === false || value === true) throw new Error("--due requires a timestamp or date value.");
|
|
2682
|
+
const numeric = Number(value);
|
|
2683
|
+
if (Number.isFinite(numeric) && numeric > 0) return Math.floor(numeric);
|
|
2684
|
+
const parsed = Date.parse(value);
|
|
2685
|
+
if (Number.isNaN(parsed)) throw new Error(`Invalid --due value '${value}'.`);
|
|
2686
|
+
return Math.floor(parsed / 1e3);
|
|
2687
|
+
}
|
|
2688
|
+
async function buildCreateUserTaskInput(masterKey, input) {
|
|
2689
|
+
const taskKey = randomBytes2(32);
|
|
2690
|
+
const encryptedTaskKey = await encryptBytesWithAesGcm(taskKey, masterKey);
|
|
2691
|
+
const timestamp = nowSeconds();
|
|
2692
|
+
const assignee = parseAssignee(input.assign);
|
|
2693
|
+
const linkedProjectIds = input.projectIds ?? [];
|
|
2694
|
+
const status = input.status ?? (assignee.assigneeType === "ai" && !input.dueAt ? "in_progress" : "todo");
|
|
2695
|
+
return {
|
|
2696
|
+
task_id: randomUUIDCompat(),
|
|
2697
|
+
short_id: void 0,
|
|
2698
|
+
version: 1,
|
|
2699
|
+
encrypted_task_key: encryptedTaskKey,
|
|
2700
|
+
encrypted_title: await encryptWithAesGcmCombined(input.title, taskKey),
|
|
2701
|
+
encrypted_description: await encryptWithAesGcmCombined(input.description ?? "", taskKey),
|
|
2702
|
+
encrypted_tags: await encryptWithAesGcmCombined("[]", taskKey),
|
|
2703
|
+
encrypted_linked_project_ids: await encryptWithAesGcmCombined(JSON.stringify(linkedProjectIds), taskKey),
|
|
2704
|
+
status,
|
|
2705
|
+
assignee_type: assignee.assigneeType,
|
|
2706
|
+
assignee_hash: assignee.assigneeHash,
|
|
2707
|
+
primary_chat_id: input.chatId ?? null,
|
|
2708
|
+
linked_project_ids: linkedProjectIds,
|
|
2709
|
+
plan_id: input.planId ?? null,
|
|
2710
|
+
due_at: input.dueAt ?? null,
|
|
2711
|
+
priority: 0,
|
|
2712
|
+
position: timestamp,
|
|
2713
|
+
created_at: timestamp,
|
|
2714
|
+
updated_at: timestamp
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
async function buildUpdateUserTaskInput(task, masterKey, input) {
|
|
2718
|
+
const taskKey = await taskKeyFromRecord(task.encrypted, masterKey);
|
|
2719
|
+
const patch = { version: task.version, updated_at: nowSeconds() };
|
|
2720
|
+
if (input.title !== void 0) patch.encrypted_title = await encryptWithAesGcmCombined(input.title, taskKey);
|
|
2721
|
+
if (input.description !== void 0) patch.encrypted_description = await encryptWithAesGcmCombined(input.description, taskKey);
|
|
2722
|
+
if (input.status !== void 0) patch.status = input.status;
|
|
2723
|
+
if (input.assign !== void 0) {
|
|
2724
|
+
const assignee = parseAssignee(input.assign);
|
|
2725
|
+
patch.assignee_type = assignee.assigneeType;
|
|
2726
|
+
patch.assignee_hash = assignee.assigneeHash;
|
|
2727
|
+
}
|
|
2728
|
+
if (input.chatId !== void 0) patch.primary_chat_id = input.chatId;
|
|
2729
|
+
if (input.projectIds !== void 0) {
|
|
2730
|
+
patch.linked_project_ids = input.projectIds;
|
|
2731
|
+
patch.encrypted_linked_project_ids = await encryptWithAesGcmCombined(JSON.stringify(input.projectIds), taskKey);
|
|
2732
|
+
}
|
|
2733
|
+
if (input.planId !== void 0) patch.plan_id = input.planId;
|
|
2734
|
+
return patch;
|
|
2735
|
+
}
|
|
2736
|
+
async function decryptUserTask(record, masterKey) {
|
|
2737
|
+
if (typeof record.version !== "number") throw new Error(`Task ${record.task_id} is missing version.`);
|
|
2738
|
+
const taskKey = await taskKeyFromRecord(record, masterKey);
|
|
2739
|
+
const tags = parseStringArray(await decryptOptional(record.encrypted_tags, taskKey));
|
|
2740
|
+
const linkedProjectIds = parseStringArray(await decryptOptional(record.encrypted_linked_project_ids, taskKey));
|
|
2741
|
+
return {
|
|
2742
|
+
taskId: record.task_id,
|
|
2743
|
+
shortId: record.short_id || deriveShortId(record),
|
|
2744
|
+
title: await decryptOptional(record.encrypted_title, taskKey) || "(untitled task)",
|
|
2745
|
+
description: await decryptOptional(record.encrypted_description, taskKey),
|
|
2746
|
+
tags,
|
|
2747
|
+
latestInstruction: await decryptOptional(record.encrypted_latest_instruction, taskKey),
|
|
2748
|
+
status: record.status,
|
|
2749
|
+
assigneeType: record.assignee_type,
|
|
2750
|
+
assigneeHash: record.assignee_hash ?? null,
|
|
2751
|
+
primaryChatId: record.primary_chat_id ?? null,
|
|
2752
|
+
linkedProjectIds: linkedProjectIds.length > 0 ? linkedProjectIds : record.linked_project_ids ?? [],
|
|
2753
|
+
planId: record.plan_id ?? null,
|
|
2754
|
+
dueAt: record.due_at ?? null,
|
|
2755
|
+
priority: record.priority ?? 0,
|
|
2756
|
+
position: record.position ?? 0,
|
|
2757
|
+
queueState: record.queue_state ?? "none",
|
|
2758
|
+
blockedReasonCode: record.blocked_reason_code ?? null,
|
|
2759
|
+
aiExecutionState: record.ai_execution_state ?? null,
|
|
2760
|
+
version: record.version,
|
|
2761
|
+
encrypted: record
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
async function decryptUserTasks(records, masterKey) {
|
|
2765
|
+
const output = [];
|
|
2766
|
+
for (const record of records) output.push(await decryptUserTask(record, masterKey));
|
|
2767
|
+
return output;
|
|
2768
|
+
}
|
|
2769
|
+
function findTask(tasks, id) {
|
|
2770
|
+
const taskIdMatch = tasks.find((candidate) => candidate.taskId === id);
|
|
2771
|
+
if (taskIdMatch) return taskIdMatch;
|
|
2772
|
+
const shortIdMatches = tasks.filter((candidate) => candidate.shortId === id);
|
|
2773
|
+
if (shortIdMatches.length > 1) throw new Error(`Task '${id}' is ambiguous in the current task list. Use the full task ID.`);
|
|
2774
|
+
const task = shortIdMatches[0];
|
|
2775
|
+
if (!task) throw new Error(`Task '${id}' was not found in the current task list.`);
|
|
2776
|
+
return task;
|
|
2777
|
+
}
|
|
2778
|
+
function renderTaskList(tasks) {
|
|
2779
|
+
if (tasks.length === 0) return "No tasks found.";
|
|
2780
|
+
const lines = ["Tasks", "ID Status Assignee Queue Title"];
|
|
2781
|
+
for (const task of tasks) {
|
|
2782
|
+
lines.push(`${pad(task.shortId, 9)} ${pad(task.status, 12)} ${pad(assigneeLabel(task), 11)} ${pad(task.queueState, 11)} ${task.title}`);
|
|
2783
|
+
}
|
|
2784
|
+
return lines.join("\n");
|
|
2785
|
+
}
|
|
2786
|
+
function renderTaskDetail(task) {
|
|
2787
|
+
const lines = [
|
|
2788
|
+
`Task ${task.shortId}`,
|
|
2789
|
+
`Title: ${task.title}`,
|
|
2790
|
+
`Status: ${task.status}`,
|
|
2791
|
+
`Assignee: ${assigneeLabel(task)}`,
|
|
2792
|
+
`Queue: ${task.queueState}`,
|
|
2793
|
+
`Task ID: ${task.taskId}`
|
|
2794
|
+
];
|
|
2795
|
+
if (task.description) lines.push(`Description: ${task.description}`);
|
|
2796
|
+
if (task.primaryChatId) lines.push(`Chat: ${task.primaryChatId}`);
|
|
2797
|
+
if (task.linkedProjectIds.length > 0) lines.push(`Projects: ${task.linkedProjectIds.join(", ")}`);
|
|
2798
|
+
if (task.planId) lines.push(`Plan: ${task.planId}`);
|
|
2799
|
+
if (task.blockedReasonCode) lines.push(`Blocked reason: ${task.blockedReasonCode}`);
|
|
2800
|
+
if (task.aiExecutionState) lines.push(`AI state: ${task.aiExecutionState}`);
|
|
2801
|
+
return lines.join("\n");
|
|
2802
|
+
}
|
|
2803
|
+
function renderTaskBoard(tasks, width = process.stdout.columns || 100) {
|
|
2804
|
+
const columns = TASK_STATUSES2.map((status) => ({ status, tasks: tasks.filter((task) => task.status === status).sort(compareTasks) }));
|
|
2805
|
+
if (width < 96) {
|
|
2806
|
+
const lines2 = ["OpenMates Tasks Board"];
|
|
2807
|
+
for (const column of columns) {
|
|
2808
|
+
lines2.push("", `${columnTitle(column.status)} (${column.tasks.length})`, "-".repeat(24));
|
|
2809
|
+
lines2.push(...boardColumnLines(column.tasks, 8));
|
|
2810
|
+
}
|
|
2811
|
+
return lines2.join("\n");
|
|
2812
|
+
}
|
|
2813
|
+
const perColumn = 6;
|
|
2814
|
+
const columnWidth = 22;
|
|
2815
|
+
const lines = ["OpenMates Tasks Board", ""];
|
|
2816
|
+
lines.push(columns.map((column) => pad(`${columnTitle(column.status)} (${column.tasks.length})`, columnWidth)).join(" "));
|
|
2817
|
+
lines.push(columns.map(() => "-".repeat(columnWidth)).join(" "));
|
|
2818
|
+
const renderedColumns = columns.map((column) => boardColumnLines(column.tasks, perColumn).map((line) => truncate(line, columnWidth)));
|
|
2819
|
+
const maxRows = Math.max(...renderedColumns.map((column) => column.length));
|
|
2820
|
+
for (let row = 0; row < maxRows; row += 1) {
|
|
2821
|
+
lines.push(renderedColumns.map((column) => pad(column[row] ?? "", columnWidth)).join(" "));
|
|
2822
|
+
}
|
|
2823
|
+
return lines.join("\n");
|
|
2824
|
+
}
|
|
2825
|
+
function boardColumnLines(tasks, limit) {
|
|
2826
|
+
if (tasks.length === 0) return ["No tasks here."];
|
|
2827
|
+
const visible = tasks.slice(0, limit).flatMap((task) => [
|
|
2828
|
+
`[${task.shortId}] ${task.title}`,
|
|
2829
|
+
` ${assigneeLabel(task)} ${task.queueState === "none" ? "" : task.queueState}`.trimEnd()
|
|
2830
|
+
]);
|
|
2831
|
+
if (tasks.length > limit) visible.push(`... ${tasks.length - limit} more`);
|
|
2832
|
+
return visible;
|
|
2833
|
+
}
|
|
2834
|
+
function compareTasks(a, b) {
|
|
2835
|
+
return a.position - b.position || a.title.localeCompare(b.title);
|
|
2836
|
+
}
|
|
2837
|
+
function columnTitle(status) {
|
|
2838
|
+
if (status === "in_progress") return "In progress";
|
|
2839
|
+
return status.slice(0, 1).toUpperCase() + status.slice(1).replace("_", " ");
|
|
2840
|
+
}
|
|
2841
|
+
function assigneeLabel(task) {
|
|
2842
|
+
return task.assigneeType === "ai" ? "OpenMates" : task.assigneeHash ?? "user";
|
|
2843
|
+
}
|
|
2844
|
+
async function taskKeyFromRecord(record, masterKey) {
|
|
2845
|
+
if (!record.encrypted_task_key) throw new Error(`Task ${record.task_id} is missing encrypted task key.`);
|
|
2846
|
+
const taskKey = await decryptBytesWithAesGcm(record.encrypted_task_key, masterKey);
|
|
2847
|
+
if (!taskKey) throw new Error(`Failed to decrypt task key for ${record.task_id}.`);
|
|
2848
|
+
return taskKey;
|
|
2849
|
+
}
|
|
2850
|
+
async function decryptOptional(value, key) {
|
|
2851
|
+
if (!value) return "";
|
|
2852
|
+
return await decryptWithAesGcmCombined(value, key) ?? "";
|
|
2853
|
+
}
|
|
2854
|
+
function parseStringArray(value) {
|
|
2855
|
+
if (!value) return [];
|
|
2856
|
+
try {
|
|
2857
|
+
const parsed = JSON.parse(value);
|
|
2858
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
2859
|
+
} catch {
|
|
2860
|
+
return [];
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
function deriveShortId(record) {
|
|
2864
|
+
const prefix = record.short_id_prefix || DEFAULT_STANDALONE_PREFIX;
|
|
2865
|
+
const source = record.task_id || `${record.created_at ?? ""}-${record.position ?? ""}`;
|
|
2866
|
+
const digest = createHash5("sha256").update(source).digest("hex").slice(0, 4).toUpperCase();
|
|
2867
|
+
return `${prefix}-${parseInt(digest, 16) % 1e4}`;
|
|
2868
|
+
}
|
|
2869
|
+
function nowSeconds() {
|
|
2870
|
+
return Math.floor(Date.now() / 1e3);
|
|
2871
|
+
}
|
|
2872
|
+
function randomUUIDCompat() {
|
|
2873
|
+
return randomUUID3();
|
|
2874
|
+
}
|
|
2875
|
+
function pad(value, length) {
|
|
2876
|
+
return truncate(value, length).padEnd(length);
|
|
2877
|
+
}
|
|
2878
|
+
function truncate(value, length) {
|
|
2879
|
+
return value.length <= length ? value : `${value.slice(0, Math.max(0, length - 3))}...`;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2517
2882
|
// src/client.ts
|
|
2518
2883
|
function normalizeUnixSeconds(value, fallback) {
|
|
2519
2884
|
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
@@ -2681,6 +3046,83 @@ function buildAppSettingsMemoryResponseSystemMessage(params) {
|
|
|
2681
3046
|
user_message_id: params.userMessageId
|
|
2682
3047
|
};
|
|
2683
3048
|
}
|
|
3049
|
+
async function buildTaskEventSystemMessage(params) {
|
|
3050
|
+
const content = formatTaskEventSystemContent(params.event);
|
|
3051
|
+
const message = {
|
|
3052
|
+
message_id: `task-event-${params.event.event_id}`,
|
|
3053
|
+
role: "system",
|
|
3054
|
+
encrypted_content: await encryptWithAesGcmCombined(content, params.chatKey),
|
|
3055
|
+
created_at: params.event.created_at ?? Math.floor(Date.now() / 1e3),
|
|
3056
|
+
user_message_id: params.userMessageId
|
|
3057
|
+
};
|
|
3058
|
+
if (params.event.task_update_job_id) message.task_update_job_id = params.event.task_update_job_id;
|
|
3059
|
+
return message;
|
|
3060
|
+
}
|
|
3061
|
+
function taskUpdateJobBelongsToActiveTurn(job, activeChatId, taskEvents) {
|
|
3062
|
+
void activeChatId;
|
|
3063
|
+
return taskEvents.some((event) => event.task_update_job_id === job.job_id);
|
|
3064
|
+
}
|
|
3065
|
+
function buildTaskUpdateJobPersistPayload(params) {
|
|
3066
|
+
const encryptedTaskPayload = pruneAbsentTaskPersistFields(params.encryptedTaskPayload);
|
|
3067
|
+
assertTaskPersistPayloadEncrypted(encryptedTaskPayload);
|
|
3068
|
+
return {
|
|
3069
|
+
protocol_version: 1,
|
|
3070
|
+
job_id: params.jobId,
|
|
3071
|
+
lease_token: params.leaseToken,
|
|
3072
|
+
lease_generation: params.leaseGeneration,
|
|
3073
|
+
expected_task_version: params.expectedTaskVersion,
|
|
3074
|
+
encrypted_task_payload: encryptedTaskPayload,
|
|
3075
|
+
encrypted_task_event_message: params.encryptedTaskEventMessage ?? null
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
function pruneAbsentTaskPersistFields(payload) {
|
|
3079
|
+
return Object.fromEntries(
|
|
3080
|
+
Object.entries(payload).filter(([, value]) => value !== void 0 && value !== null)
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
function formatTaskEventSystemContent(event) {
|
|
3084
|
+
const taskLabel = event.short_id || event.task_id;
|
|
3085
|
+
const title = event.title ? ` "${event.title}"` : "";
|
|
3086
|
+
const status = event.status ? ` (${event.status})` : "";
|
|
3087
|
+
const reason = event.reason ? `: ${event.reason}` : "";
|
|
3088
|
+
switch (event.event_type) {
|
|
3089
|
+
case "created":
|
|
3090
|
+
return `${taskLabel} created${title}${status}`;
|
|
3091
|
+
case "updated":
|
|
3092
|
+
return `${taskLabel} updated${title}${status}`;
|
|
3093
|
+
case "blocked":
|
|
3094
|
+
return `${taskLabel} blocked${reason}`;
|
|
3095
|
+
case "completed":
|
|
3096
|
+
return `${taskLabel} completed${title}`;
|
|
3097
|
+
case "unblocked":
|
|
3098
|
+
return `${taskLabel} unblocked`;
|
|
3099
|
+
default:
|
|
3100
|
+
return `${taskLabel} ${event.event_type}${title}${status}${reason}`;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
function assertTaskPersistPayloadEncrypted(payload) {
|
|
3104
|
+
const allowedSafeKeys = /* @__PURE__ */ new Set([
|
|
3105
|
+
"assignee_hash",
|
|
3106
|
+
"assignee_type",
|
|
3107
|
+
"blocked_reason_code",
|
|
3108
|
+
"created_at",
|
|
3109
|
+
"key_wrappers",
|
|
3110
|
+
"linked_project_ids",
|
|
3111
|
+
"position",
|
|
3112
|
+
"primary_chat_id",
|
|
3113
|
+
"priority",
|
|
3114
|
+
"status",
|
|
3115
|
+
"task_id",
|
|
3116
|
+
"updated_at",
|
|
3117
|
+
"version"
|
|
3118
|
+
]);
|
|
3119
|
+
for (const key of Object.keys(payload)) {
|
|
3120
|
+
if (key.startsWith("encrypted_") || allowedSafeKeys.has(key)) {
|
|
3121
|
+
continue;
|
|
3122
|
+
}
|
|
3123
|
+
throw new Error("Task update job payload contains plaintext or unsupported field");
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
2684
3126
|
async function buildSubChatEncryptedMetadataPayloads(params) {
|
|
2685
3127
|
const createdAt = params.createdAt ?? Math.floor(Date.now() / 1e3);
|
|
2686
3128
|
const payloads = [];
|
|
@@ -3354,11 +3796,11 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3354
3796
|
}
|
|
3355
3797
|
let anonymousId = loadAnonymousId();
|
|
3356
3798
|
if (!anonymousId) {
|
|
3357
|
-
anonymousId =
|
|
3799
|
+
anonymousId = randomUUID4();
|
|
3358
3800
|
saveAnonymousId(anonymousId);
|
|
3359
3801
|
}
|
|
3360
|
-
const chatId = `anonymous-${
|
|
3361
|
-
const messageId = `anonymous-message-${
|
|
3802
|
+
const chatId = `anonymous-${randomUUID4()}`;
|
|
3803
|
+
const messageId = `anonymous-message-${randomUUID4()}`;
|
|
3362
3804
|
const requestBody = {
|
|
3363
3805
|
anonymous_id: anonymousId,
|
|
3364
3806
|
client_chat_id: chatId,
|
|
@@ -3552,7 +3994,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3552
3994
|
pin: pin.trim().toUpperCase(),
|
|
3553
3995
|
token
|
|
3554
3996
|
});
|
|
3555
|
-
const sessionId =
|
|
3997
|
+
const sessionId = randomUUID4();
|
|
3556
3998
|
const login = await this.http.post(
|
|
3557
3999
|
"/v1/auth/login",
|
|
3558
4000
|
{
|
|
@@ -3727,7 +4169,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3727
4169
|
}
|
|
3728
4170
|
const session = {
|
|
3729
4171
|
apiUrl: this.apiUrl,
|
|
3730
|
-
sessionId:
|
|
4172
|
+
sessionId: randomUUID4(),
|
|
3731
4173
|
wsToken: null,
|
|
3732
4174
|
cookies: this.http.getCookieMap(),
|
|
3733
4175
|
masterKeyExportedB64: material.masterKeyB64,
|
|
@@ -3887,7 +4329,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3887
4329
|
if (!markdown) throw new Error("Draft markdown must not be empty.");
|
|
3888
4330
|
const masterKey = this.getMasterKeyBytes();
|
|
3889
4331
|
const encrypted = await this.saveEncryptedDraft({
|
|
3890
|
-
chatId: params.chatId ??
|
|
4332
|
+
chatId: params.chatId ?? randomUUID4(),
|
|
3891
4333
|
encryptedDraftMd: await encryptWithAesGcmCombined(markdown, masterKey),
|
|
3892
4334
|
encryptedDraftPreview: params.preview ? await encryptWithAesGcmCombined(params.preview, masterKey) : null
|
|
3893
4335
|
});
|
|
@@ -4443,7 +4885,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4443
4885
|
async sendMessage(params) {
|
|
4444
4886
|
let chatId;
|
|
4445
4887
|
if (!params.chatId) {
|
|
4446
|
-
chatId =
|
|
4888
|
+
chatId = randomUUID4();
|
|
4447
4889
|
} else if (params.chatId.length < 36) {
|
|
4448
4890
|
const resolved = await this.resolveFullChatId(params.chatId);
|
|
4449
4891
|
if (!resolved) {
|
|
@@ -4477,7 +4919,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4477
4919
|
ws.close();
|
|
4478
4920
|
throw new Error("Authenticated user identity is required for saved chat recovery.");
|
|
4479
4921
|
}
|
|
4480
|
-
const messageId =
|
|
4922
|
+
const messageId = randomUUID4();
|
|
4481
4923
|
const createdAt = Math.floor(Date.now() / 1e3);
|
|
4482
4924
|
const isNewChat = !params.chatId;
|
|
4483
4925
|
ws.send("set_active_chat", { chat_id: chatId });
|
|
@@ -4528,6 +4970,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4528
4970
|
}
|
|
4529
4971
|
const messagePayload = {
|
|
4530
4972
|
chat_id: chatId,
|
|
4973
|
+
client_capabilities: ["task_update_jobs"],
|
|
4531
4974
|
is_incognito: Boolean(params.incognito),
|
|
4532
4975
|
message: {
|
|
4533
4976
|
message_id: messageId,
|
|
@@ -4611,11 +5054,11 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4611
5054
|
if (encryptedEmbeds.length > 0) {
|
|
4612
5055
|
messagePayload.encrypted_embeds = encryptedEmbeds;
|
|
4613
5056
|
}
|
|
4614
|
-
let precollectedResponse = params.precollectResponse && params.incognito ? ws.collectAiResponse(messageId, chatId, { onStream: params.onStream }) : null;
|
|
5057
|
+
let precollectedResponse = params.precollectResponse && params.incognito ? ws.collectAiResponse(messageId, chatId, { onStream: params.onStream, timeoutMs: params.responseTimeoutMs }) : null;
|
|
4615
5058
|
if (!params.incognito && chatKeyBytes && encryptedChatKey) {
|
|
4616
5059
|
const protocolVersion = 1;
|
|
4617
5060
|
const chatKeyVersion = 1;
|
|
4618
|
-
const turnId =
|
|
5061
|
+
const turnId = randomUUID4();
|
|
4619
5062
|
savedTurnId = turnId;
|
|
4620
5063
|
terminalExpectedMessagesV = baselineMessagesV + 1;
|
|
4621
5064
|
const recoveryKeypair = await deriveChatCompletionRecoveryKeypair(
|
|
@@ -4689,7 +5132,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4689
5132
|
}
|
|
4690
5133
|
if (params.precollectResponse && !params.incognito) {
|
|
4691
5134
|
precollectedResponse = ws.collectAiResponse(messageId, chatId, {
|
|
4692
|
-
onStream: params.onStream
|
|
5135
|
+
onStream: params.onStream,
|
|
5136
|
+
timeoutMs: params.responseTimeoutMs,
|
|
5137
|
+
recoveryTurnId: savedTurnId
|
|
4693
5138
|
});
|
|
4694
5139
|
}
|
|
4695
5140
|
const confirmed = ws.waitForMessage(
|
|
@@ -4712,6 +5157,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4712
5157
|
let followUpSuggestions = [];
|
|
4713
5158
|
let taskProposals = [];
|
|
4714
5159
|
let taskUpdateProposals = [];
|
|
5160
|
+
let taskEvents = [];
|
|
5161
|
+
let pendingTaskUpdateJobs = [];
|
|
4715
5162
|
let subChatEvents = [];
|
|
4716
5163
|
const appSettingsMemoryRequests = [];
|
|
4717
5164
|
const numberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
@@ -4874,6 +5321,225 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4874
5321
|
`The assistant requested memories (${event.requestedKeys.join(", ")}). Rerun with --auto-approve-memories to explicitly approve requested memory categories from the CLI, or continue this chat in the web app to approve or reject the request.`
|
|
4875
5322
|
);
|
|
4876
5323
|
};
|
|
5324
|
+
const persistEncryptedSystemMessage = async (systemMessage, targetChatId = chatId) => {
|
|
5325
|
+
await ws.sendAsync("chat_system_message_added", {
|
|
5326
|
+
chat_id: targetChatId,
|
|
5327
|
+
message: systemMessage
|
|
5328
|
+
});
|
|
5329
|
+
await ws.waitForMessage(
|
|
5330
|
+
"system_message_confirmed",
|
|
5331
|
+
(payload) => payload.message_id === systemMessage.message_id,
|
|
5332
|
+
2e4
|
|
5333
|
+
);
|
|
5334
|
+
};
|
|
5335
|
+
const persistedTaskEventIds = /* @__PURE__ */ new Set();
|
|
5336
|
+
const persistTaskEventSystemMessages = async (events) => {
|
|
5337
|
+
if (!chatKeyBytes || events.length === 0) return;
|
|
5338
|
+
for (const event of events) {
|
|
5339
|
+
if (persistedTaskEventIds.has(event.event_id)) continue;
|
|
5340
|
+
await persistEncryptedSystemMessage(await buildTaskEventSystemMessage({
|
|
5341
|
+
chatKey: chatKeyBytes,
|
|
5342
|
+
userMessageId: messageId,
|
|
5343
|
+
event
|
|
5344
|
+
}));
|
|
5345
|
+
persistedTaskEventIds.add(event.event_id);
|
|
5346
|
+
}
|
|
5347
|
+
};
|
|
5348
|
+
const persistPendingTaskUpdateJobs = async (jobs, events) => {
|
|
5349
|
+
const handledJobIds = /* @__PURE__ */ new Set();
|
|
5350
|
+
if (jobs.length === 0) return handledJobIds;
|
|
5351
|
+
const masterKey = this.getMasterKeyBytes();
|
|
5352
|
+
let decryptedTasksCache = null;
|
|
5353
|
+
const eventByJobId = new Map(events.map((event) => [event.task_update_job_id, event]));
|
|
5354
|
+
const buildTaskKeyWrappersForChat = async (task, targetChatId, createdAt2) => {
|
|
5355
|
+
const encryptedTaskKey = task.encrypted.encrypted_task_key;
|
|
5356
|
+
if (!encryptedTaskKey) throw new Error(`Task ${task.taskId} is missing encrypted task key.`);
|
|
5357
|
+
const taskKey = await decryptBytesWithAesGcm(encryptedTaskKey, masterKey);
|
|
5358
|
+
if (!taskKey) throw new Error(`Failed to decrypt task key for ${task.taskId}.`);
|
|
5359
|
+
const targetChatKey = await resolveChatKey(targetChatId);
|
|
5360
|
+
const existingProjectWrappers = await listProjectTaskKeyWrappers(task.taskId, createdAt2);
|
|
5361
|
+
const linkedProjectIds = task.linkedProjectIds ?? [];
|
|
5362
|
+
if (linkedProjectIds.length > existingProjectWrappers.length) {
|
|
5363
|
+
throw new Error(`Task ${task.taskId} is missing project key wrappers required for move persistence.`);
|
|
5364
|
+
}
|
|
5365
|
+
return [
|
|
5366
|
+
{
|
|
5367
|
+
key_type: "master",
|
|
5368
|
+
encrypted_task_key: await encryptBytesWithAesGcm(taskKey, masterKey),
|
|
5369
|
+
created_at: createdAt2
|
|
5370
|
+
},
|
|
5371
|
+
{
|
|
5372
|
+
key_type: "chat",
|
|
5373
|
+
hashed_chat_id: computeSHA256(targetChatId),
|
|
5374
|
+
encrypted_task_key: await encryptBytesWithAesGcm(taskKey, targetChatKey),
|
|
5375
|
+
created_at: createdAt2
|
|
5376
|
+
},
|
|
5377
|
+
...existingProjectWrappers
|
|
5378
|
+
];
|
|
5379
|
+
};
|
|
5380
|
+
const listProjectTaskKeyWrappers = async (taskId, createdAt2) => {
|
|
5381
|
+
const response = await this.http.get(
|
|
5382
|
+
`/v1/user-tasks/${encodeURIComponent(taskId)}/key-wrappers`,
|
|
5383
|
+
this.getCliRequestHeaders()
|
|
5384
|
+
);
|
|
5385
|
+
if (!response.ok) {
|
|
5386
|
+
throw new Error(`User task key wrapper list failed with HTTP ${response.status}`);
|
|
5387
|
+
}
|
|
5388
|
+
return (response.data.key_wrappers ?? []).filter((wrapper) => wrapper.key_type === "project").map((wrapper) => ({
|
|
5389
|
+
key_type: "project",
|
|
5390
|
+
hashed_project_id: wrapper.hashed_project_id,
|
|
5391
|
+
encrypted_task_key: wrapper.encrypted_task_key,
|
|
5392
|
+
created_at: typeof wrapper.created_at === "number" ? wrapper.created_at : createdAt2,
|
|
5393
|
+
expires_at: wrapper.expires_at ?? null
|
|
5394
|
+
}));
|
|
5395
|
+
};
|
|
5396
|
+
const resolveChatKey = async (targetChatId) => {
|
|
5397
|
+
if (targetChatId === chatId && chatKeyBytes) return chatKeyBytes;
|
|
5398
|
+
const cache = loadSyncCache() ?? await this.ensureSynced();
|
|
5399
|
+
const targetChat = cache.chats.find((chat) => String(chat.details.id ?? "") === targetChatId);
|
|
5400
|
+
const encryptedTargetChatKey = typeof targetChat?.details.encrypted_chat_key === "string" ? targetChat.details.encrypted_chat_key : null;
|
|
5401
|
+
if (!encryptedTargetChatKey) {
|
|
5402
|
+
throw new Error(`Encrypted chat key not found for task move target '${targetChatId}'. Sync and try again.`);
|
|
5403
|
+
}
|
|
5404
|
+
const targetChatKey = await decryptBytesWithAesGcm(encryptedTargetChatKey, masterKey);
|
|
5405
|
+
if (!targetChatKey) {
|
|
5406
|
+
throw new Error(`Failed to decrypt chat key for task move target '${targetChatId}'.`);
|
|
5407
|
+
}
|
|
5408
|
+
return targetChatKey;
|
|
5409
|
+
};
|
|
5410
|
+
const taskEventTypeForOperation = (operation) => {
|
|
5411
|
+
switch (operation) {
|
|
5412
|
+
case "create":
|
|
5413
|
+
return "created";
|
|
5414
|
+
case "move":
|
|
5415
|
+
return "moved";
|
|
5416
|
+
case "update":
|
|
5417
|
+
return "updated";
|
|
5418
|
+
default:
|
|
5419
|
+
return operation || "updated";
|
|
5420
|
+
}
|
|
5421
|
+
};
|
|
5422
|
+
for (const job of jobs) {
|
|
5423
|
+
if (!taskUpdateJobBelongsToActiveTurn(job, chatId, events)) {
|
|
5424
|
+
handledJobIds.add(job.job_id);
|
|
5425
|
+
continue;
|
|
5426
|
+
}
|
|
5427
|
+
const claimPromise = ws.waitForMessage(
|
|
5428
|
+
"task_update_job_claimed",
|
|
5429
|
+
(payload) => payload.job_id === job.job_id,
|
|
5430
|
+
2e4
|
|
5431
|
+
);
|
|
5432
|
+
await ws.sendAsync("task_update_job_claim", {
|
|
5433
|
+
protocol_version: 1,
|
|
5434
|
+
job_id: job.job_id
|
|
5435
|
+
});
|
|
5436
|
+
const claim = (await claimPromise).payload;
|
|
5437
|
+
const privatePatch = claim.private_patch ?? {};
|
|
5438
|
+
const safeMetadata = claim.safe_metadata ?? {};
|
|
5439
|
+
const sourceChatId = claim.chat_id ?? job.chat_id ?? chatId;
|
|
5440
|
+
const sourceChatKey = await resolveChatKey(sourceChatId);
|
|
5441
|
+
const event = eventByJobId.get(job.job_id) ?? {
|
|
5442
|
+
event_id: `task-event-${job.job_id}`,
|
|
5443
|
+
chat_id: sourceChatId,
|
|
5444
|
+
task_id: claim.task_id,
|
|
5445
|
+
event_type: taskEventTypeForOperation(claim.operation),
|
|
5446
|
+
title: typeof privatePatch.title === "string" ? privatePatch.title : null,
|
|
5447
|
+
status: typeof safeMetadata.status === "string" ? safeMetadata.status : null,
|
|
5448
|
+
created_at: typeof safeMetadata.updated_at === "number" ? safeMetadata.updated_at : Math.floor(Date.now() / 1e3),
|
|
5449
|
+
task_update_job_id: job.job_id
|
|
5450
|
+
};
|
|
5451
|
+
const eventMessage = await buildTaskEventSystemMessage({
|
|
5452
|
+
chatKey: sourceChatKey,
|
|
5453
|
+
userMessageId: claim.message_id ?? messageId,
|
|
5454
|
+
event
|
|
5455
|
+
});
|
|
5456
|
+
const confirmTaskEventPersisted = async () => {
|
|
5457
|
+
const confirmedPromise = ws.waitForMessage(
|
|
5458
|
+
"task_update_job_event_confirmed",
|
|
5459
|
+
(payload) => payload.job_id === job.job_id,
|
|
5460
|
+
2e4
|
|
5461
|
+
);
|
|
5462
|
+
await ws.sendAsync("task_update_job_event_confirmed", {
|
|
5463
|
+
protocol_version: 1,
|
|
5464
|
+
job_id: job.job_id,
|
|
5465
|
+
event_system_message_id: eventMessage.message_id
|
|
5466
|
+
});
|
|
5467
|
+
await confirmedPromise;
|
|
5468
|
+
};
|
|
5469
|
+
if (claim.state === "TASK_PERSISTED") {
|
|
5470
|
+
await persistEncryptedSystemMessage(eventMessage, sourceChatId);
|
|
5471
|
+
persistedTaskEventIds.add(event.event_id);
|
|
5472
|
+
await confirmTaskEventPersisted();
|
|
5473
|
+
handledJobIds.add(job.job_id);
|
|
5474
|
+
continue;
|
|
5475
|
+
}
|
|
5476
|
+
if (!claim.lease_token || !Number.isSafeInteger(claim.lease_generation)) {
|
|
5477
|
+
throw new Error("Task update job claim returned an invalid lease.");
|
|
5478
|
+
}
|
|
5479
|
+
let encryptedTaskPayload;
|
|
5480
|
+
if (claim.operation === "create") {
|
|
5481
|
+
const input = await buildCreateUserTaskInput(masterKey, {
|
|
5482
|
+
title: typeof privatePatch.title === "string" ? privatePatch.title : "Untitled task",
|
|
5483
|
+
description: typeof privatePatch.description === "string" ? privatePatch.description : "",
|
|
5484
|
+
status: typeof safeMetadata.status === "string" ? safeMetadata.status : "todo",
|
|
5485
|
+
assign: typeof safeMetadata.assignee_type === "string" ? safeMetadata.assignee_type : "user",
|
|
5486
|
+
chatId: typeof safeMetadata.primary_chat_id === "string" ? safeMetadata.primary_chat_id : claim.chat_id ?? chatId
|
|
5487
|
+
});
|
|
5488
|
+
encryptedTaskPayload = {
|
|
5489
|
+
...input,
|
|
5490
|
+
task_id: claim.task_id,
|
|
5491
|
+
position: typeof safeMetadata.position === "number" ? safeMetadata.position : input.position,
|
|
5492
|
+
created_at: typeof safeMetadata.created_at === "number" ? safeMetadata.created_at : input.created_at,
|
|
5493
|
+
updated_at: typeof safeMetadata.updated_at === "number" ? safeMetadata.updated_at : input.updated_at
|
|
5494
|
+
};
|
|
5495
|
+
} else {
|
|
5496
|
+
if (!decryptedTasksCache) {
|
|
5497
|
+
decryptedTasksCache = await decryptUserTasks(await this.listUserTasks(), masterKey);
|
|
5498
|
+
}
|
|
5499
|
+
const task = findTask(decryptedTasksCache, claim.task_id);
|
|
5500
|
+
const patch = await buildUpdateUserTaskInput(task, masterKey, {
|
|
5501
|
+
title: typeof privatePatch.title === "string" ? privatePatch.title : void 0,
|
|
5502
|
+
description: typeof privatePatch.description === "string" ? privatePatch.description : void 0,
|
|
5503
|
+
status: typeof safeMetadata.status === "string" ? safeMetadata.status : void 0,
|
|
5504
|
+
assign: typeof safeMetadata.assignee_type === "string" ? safeMetadata.assignee_type : void 0,
|
|
5505
|
+
chatId: typeof safeMetadata.primary_chat_id === "string" ? safeMetadata.primary_chat_id : void 0
|
|
5506
|
+
});
|
|
5507
|
+
encryptedTaskPayload = {
|
|
5508
|
+
...patch,
|
|
5509
|
+
version: claim.expected_task_version,
|
|
5510
|
+
updated_at: typeof safeMetadata.updated_at === "number" ? safeMetadata.updated_at : patch.updated_at
|
|
5511
|
+
};
|
|
5512
|
+
if (typeof safeMetadata.primary_chat_id === "string") {
|
|
5513
|
+
encryptedTaskPayload.key_wrappers = await buildTaskKeyWrappersForChat(
|
|
5514
|
+
task,
|
|
5515
|
+
safeMetadata.primary_chat_id,
|
|
5516
|
+
typeof safeMetadata.updated_at === "number" ? safeMetadata.updated_at : Math.floor(Date.now() / 1e3)
|
|
5517
|
+
);
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
const encryptedEventMessage = eventMessage.encrypted_content;
|
|
5521
|
+
const persistedPromise = ws.waitForMessage(
|
|
5522
|
+
"task_update_job_persisted",
|
|
5523
|
+
(payload) => payload.job_id === job.job_id,
|
|
5524
|
+
2e4
|
|
5525
|
+
);
|
|
5526
|
+
await ws.sendAsync("task_update_job_persist", buildTaskUpdateJobPersistPayload({
|
|
5527
|
+
jobId: job.job_id,
|
|
5528
|
+
leaseToken: claim.lease_token,
|
|
5529
|
+
leaseGeneration: claim.lease_generation,
|
|
5530
|
+
expectedTaskVersion: claim.expected_task_version,
|
|
5531
|
+
encryptedTaskPayload,
|
|
5532
|
+
encryptedTaskEventMessage: encryptedEventMessage
|
|
5533
|
+
}));
|
|
5534
|
+
await persistedPromise;
|
|
5535
|
+
await persistEncryptedSystemMessage(eventMessage, sourceChatId);
|
|
5536
|
+
persistedTaskEventIds.add(event.event_id);
|
|
5537
|
+
await confirmTaskEventPersisted();
|
|
5538
|
+
handledJobIds.add(job.job_id);
|
|
5539
|
+
}
|
|
5540
|
+
clearSyncCache();
|
|
5541
|
+
return handledJobIds;
|
|
5542
|
+
};
|
|
4877
5543
|
const streamOpts = {
|
|
4878
5544
|
onStream: params.onStream,
|
|
4879
5545
|
onSubChatEvent: handleSubChatEvent,
|
|
@@ -4881,11 +5547,17 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4881
5547
|
};
|
|
4882
5548
|
if (params.incognito) {
|
|
4883
5549
|
try {
|
|
4884
|
-
const resp = await (precollectedResponse ?? ws.collectAiResponse(messageId, chatId,
|
|
5550
|
+
const resp = await (precollectedResponse ?? ws.collectAiResponse(messageId, chatId, {
|
|
5551
|
+
...streamOpts,
|
|
5552
|
+
timeoutMs: params.responseTimeoutMs,
|
|
5553
|
+
recoveryTurnId: savedTurnId
|
|
5554
|
+
}));
|
|
4885
5555
|
assistantMessageId = resp.messageId;
|
|
4886
5556
|
assistant = resp.content;
|
|
4887
5557
|
category = resp.category;
|
|
4888
5558
|
modelName = resp.modelName;
|
|
5559
|
+
taskEvents = resp.taskEvents;
|
|
5560
|
+
pendingTaskUpdateJobs = resp.pendingTaskUpdateJobs;
|
|
4889
5561
|
subChatEvents = resp.subChatEvents;
|
|
4890
5562
|
if (resp.status === "waiting_for_user") {
|
|
4891
5563
|
return {
|
|
@@ -4899,6 +5571,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4899
5571
|
followUpSuggestions,
|
|
4900
5572
|
taskProposals,
|
|
4901
5573
|
taskUpdateProposals,
|
|
5574
|
+
taskEvents,
|
|
5575
|
+
pendingTaskUpdateJobs,
|
|
4902
5576
|
subChatEvents,
|
|
4903
5577
|
appSettingsMemoryRequests
|
|
4904
5578
|
};
|
|
@@ -4908,7 +5582,10 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4908
5582
|
}
|
|
4909
5583
|
} else {
|
|
4910
5584
|
try {
|
|
4911
|
-
const resp = await (precollectedResponse ?? ws.collectAiResponse(messageId, chatId,
|
|
5585
|
+
const resp = await (precollectedResponse ?? ws.collectAiResponse(messageId, chatId, {
|
|
5586
|
+
...streamOpts,
|
|
5587
|
+
timeoutMs: params.responseTimeoutMs
|
|
5588
|
+
}));
|
|
4912
5589
|
assistantMessageId = resp.messageId;
|
|
4913
5590
|
assistant = resp.content;
|
|
4914
5591
|
category = resp.category;
|
|
@@ -4916,6 +5593,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4916
5593
|
followUpSuggestions = resp.followUpSuggestions;
|
|
4917
5594
|
taskProposals = resp.taskProposals;
|
|
4918
5595
|
taskUpdateProposals = resp.taskUpdateProposals;
|
|
5596
|
+
taskEvents = resp.taskEvents;
|
|
5597
|
+
pendingTaskUpdateJobs = resp.pendingTaskUpdateJobs;
|
|
4919
5598
|
subChatEvents = resp.subChatEvents;
|
|
4920
5599
|
if (resp.status === "waiting_for_user") {
|
|
4921
5600
|
return {
|
|
@@ -4929,6 +5608,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4929
5608
|
followUpSuggestions,
|
|
4930
5609
|
taskProposals,
|
|
4931
5610
|
taskUpdateProposals,
|
|
5611
|
+
taskEvents,
|
|
5612
|
+
pendingTaskUpdateJobs,
|
|
4932
5613
|
subChatEvents,
|
|
4933
5614
|
appSettingsMemoryRequests
|
|
4934
5615
|
};
|
|
@@ -4994,12 +5675,15 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4994
5675
|
"model_name",
|
|
4995
5676
|
"turn_id"
|
|
4996
5677
|
];
|
|
4997
|
-
if (Object.keys(recovered).sort().join(",") !== expectedRecoveryFields.join(",") || recovered.assistant_message_id !== assistantId || recovered.chat_id !== chatId || recovered.turn_id !== savedTurnId || recovered.job_id !== recoveryJobId || recovered.key_version !== keyVersion || typeof recovered.content !== "string" || recovered.category !== null && typeof recovered.category !== "string" || recovered.model_name !== null && typeof recovered.model_name !== "string"
|
|
5678
|
+
if (Object.keys(recovered).sort().join(",") !== expectedRecoveryFields.join(",") || recovered.assistant_message_id !== assistantId || recovered.chat_id !== chatId || recovered.turn_id !== savedTurnId || recovered.job_id !== recoveryJobId || recovered.key_version !== keyVersion || typeof recovered.content !== "string" || recovered.category !== null && typeof recovered.category !== "string" || recovered.model_name !== null && typeof recovered.model_name !== "string") {
|
|
4998
5679
|
throw new Error("Recovery job plaintext did not match the terminal completion identity.");
|
|
4999
5680
|
}
|
|
5681
|
+
assistant = recovered.content;
|
|
5682
|
+
category = recovered.category;
|
|
5683
|
+
modelName = recovered.model_name;
|
|
5000
5684
|
const completedAt = Math.floor(Date.now() / 1e3);
|
|
5001
5685
|
const encryptedAssistantContent = await encryptWithAesGcmCombined(
|
|
5002
|
-
|
|
5686
|
+
assistant,
|
|
5003
5687
|
chatKeyBytes
|
|
5004
5688
|
);
|
|
5005
5689
|
const encryptedSenderName = await encryptWithAesGcmCombined("Assistant", chatKeyBytes);
|
|
@@ -5046,6 +5730,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
5046
5730
|
newChatSuggestions: resp.newChatSuggestions,
|
|
5047
5731
|
encryptedChatKey
|
|
5048
5732
|
});
|
|
5733
|
+
const persistedTaskJobIds = await persistPendingTaskUpdateJobs(pendingTaskUpdateJobs, taskEvents);
|
|
5734
|
+
pendingTaskUpdateJobs = pendingTaskUpdateJobs.filter((job) => !persistedTaskJobIds.has(job.job_id));
|
|
5735
|
+
await persistTaskEventSystemMessages(taskEvents);
|
|
5049
5736
|
clearSyncCache();
|
|
5050
5737
|
}
|
|
5051
5738
|
} finally {
|
|
@@ -5064,6 +5751,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
5064
5751
|
followUpSuggestions,
|
|
5065
5752
|
taskProposals,
|
|
5066
5753
|
taskUpdateProposals,
|
|
5754
|
+
taskEvents,
|
|
5755
|
+
pendingTaskUpdateJobs,
|
|
5067
5756
|
subChatEvents,
|
|
5068
5757
|
appSettingsMemoryRequests
|
|
5069
5758
|
};
|
|
@@ -6024,7 +6713,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6024
6713
|
}
|
|
6025
6714
|
return response.data.task;
|
|
6026
6715
|
}
|
|
6027
|
-
async startUserTaskWithAI(taskId, input
|
|
6716
|
+
async startUserTaskWithAI(taskId, input) {
|
|
6028
6717
|
this.requireSession();
|
|
6029
6718
|
const response = await this.http.post(
|
|
6030
6719
|
`/v1/user-tasks/${encodeURIComponent(taskId)}/start-ai`,
|
|
@@ -6036,10 +6725,10 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6036
6725
|
}
|
|
6037
6726
|
return response.data.task;
|
|
6038
6727
|
}
|
|
6039
|
-
async deleteUserTask(taskId) {
|
|
6728
|
+
async deleteUserTask(taskId, version) {
|
|
6040
6729
|
this.requireSession();
|
|
6041
6730
|
const response = await this.http.delete(
|
|
6042
|
-
`/v1/user-tasks/${encodeURIComponent(taskId)}`,
|
|
6731
|
+
`/v1/user-tasks/${encodeURIComponent(taskId)}?version=${encodeURIComponent(String(version))}`,
|
|
6043
6732
|
void 0,
|
|
6044
6733
|
this.getCliRequestHeaders()
|
|
6045
6734
|
);
|
|
@@ -6048,16 +6737,16 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6048
6737
|
}
|
|
6049
6738
|
return response.data;
|
|
6050
6739
|
}
|
|
6051
|
-
async completeUserTask(taskId, input
|
|
6740
|
+
async completeUserTask(taskId, input) {
|
|
6052
6741
|
return this.postUserTaskAction(taskId, "complete", input);
|
|
6053
6742
|
}
|
|
6054
|
-
async blockUserTask(taskId, input
|
|
6743
|
+
async blockUserTask(taskId, input) {
|
|
6055
6744
|
return this.postUserTaskAction(taskId, "block", input);
|
|
6056
6745
|
}
|
|
6057
|
-
async unblockUserTask(taskId, input
|
|
6746
|
+
async unblockUserTask(taskId, input) {
|
|
6058
6747
|
return this.postUserTaskAction(taskId, "unblock", input);
|
|
6059
6748
|
}
|
|
6060
|
-
async skipUserTask(taskId, input
|
|
6749
|
+
async skipUserTask(taskId, input) {
|
|
6061
6750
|
return this.postUserTaskAction(taskId, "skip", input);
|
|
6062
6751
|
}
|
|
6063
6752
|
async reorderUserTasks(input) {
|
|
@@ -6072,7 +6761,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6072
6761
|
}
|
|
6073
6762
|
return response.data.tasks ?? [];
|
|
6074
6763
|
}
|
|
6075
|
-
async postUserTaskAction(taskId, action, input
|
|
6764
|
+
async postUserTaskAction(taskId, action, input) {
|
|
6076
6765
|
this.requireSession();
|
|
6077
6766
|
const response = await this.http.post(
|
|
6078
6767
|
`/v1/user-tasks/${encodeURIComponent(taskId)}/${encodeURIComponent(action)}`,
|
|
@@ -6830,7 +7519,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
6830
7519
|
);
|
|
6831
7520
|
}
|
|
6832
7521
|
}
|
|
6833
|
-
const entryId = params.entryId ??
|
|
7522
|
+
const entryId = params.entryId ?? randomUUID4();
|
|
6834
7523
|
const now = Math.floor(Date.now() / 1e3);
|
|
6835
7524
|
const hashedKey = hashItemKey(params.appId, params.itemType);
|
|
6836
7525
|
const plaintextPayload = {
|
|
@@ -7305,7 +7994,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
7305
7994
|
makeWsClient(session) {
|
|
7306
7995
|
return new OpenMatesWsClient({
|
|
7307
7996
|
apiUrl: session.apiUrl,
|
|
7308
|
-
sessionId:
|
|
7997
|
+
sessionId: randomUUID4(),
|
|
7309
7998
|
wsToken: session.wsToken,
|
|
7310
7999
|
refreshToken: session.cookies.auth_refresh_token ?? null,
|
|
7311
8000
|
// Same User-Agent as login so OS-based device fingerprint hash matches.
|
|
@@ -8488,7 +9177,7 @@ var OutputRedactor = class {
|
|
|
8488
9177
|
import { readFileSync as readFileSync4, statSync, existsSync as existsSync4 } from "fs";
|
|
8489
9178
|
import { basename, extname, resolve as resolve2 } from "path";
|
|
8490
9179
|
import { homedir as homedir4 } from "os";
|
|
8491
|
-
import { createHash as
|
|
9180
|
+
import { createHash as createHash6 } from "crypto";
|
|
8492
9181
|
var MAX_PER_FILE_SIZE = 100 * 1024 * 1024;
|
|
8493
9182
|
var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8494
9183
|
".pem",
|
|
@@ -8798,7 +9487,7 @@ function processCodeFile(filePath, filename, redactor) {
|
|
|
8798
9487
|
line_count: lineCount
|
|
8799
9488
|
});
|
|
8800
9489
|
const textPreview = `${filename} (${language}, ${lineCount} lines)`;
|
|
8801
|
-
const contentHash =
|
|
9490
|
+
const contentHash = createHash6("sha256").update(content).digest("hex");
|
|
8802
9491
|
const embed = {
|
|
8803
9492
|
embedId,
|
|
8804
9493
|
embedRef,
|
|
@@ -10314,7 +11003,7 @@ function formatTs(ts) {
|
|
|
10314
11003
|
|
|
10315
11004
|
// src/server.ts
|
|
10316
11005
|
import { execFileSync as execFileSync2, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
|
|
10317
|
-
import { createHash as
|
|
11006
|
+
import { createHash as createHash7, randomBytes as randomBytes3 } from "crypto";
|
|
10318
11007
|
import { chmodSync as chmodSync2, closeSync, copyFileSync, cpSync, existsSync as existsSync5, mkdirSync as mkdirSync3, mkdtempSync, openSync, readFileSync as readFileSync5, readSync, readdirSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
10319
11008
|
import { createInterface as createInterface2 } from "readline";
|
|
10320
11009
|
import { createInterface as createPromptInterface } from "readline/promises";
|
|
@@ -10932,10 +11621,10 @@ function resolveTargetImageTag(flags, currentTag, packageVersion) {
|
|
|
10932
11621
|
return { tag: getDefaultImageTagForVersion(packageVersion) };
|
|
10933
11622
|
}
|
|
10934
11623
|
function randomHex(bytes) {
|
|
10935
|
-
return
|
|
11624
|
+
return randomBytes3(bytes).toString("hex");
|
|
10936
11625
|
}
|
|
10937
11626
|
function generateInviteCode() {
|
|
10938
|
-
const digits = Array.from(
|
|
11627
|
+
const digits = Array.from(randomBytes3(12), (byte) => String(byte % 10)).join("");
|
|
10939
11628
|
return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
|
|
10940
11629
|
}
|
|
10941
11630
|
function getEnvVar(content, name) {
|
|
@@ -10979,7 +11668,7 @@ function packagedCaddyTemplatePath(role) {
|
|
|
10979
11668
|
}
|
|
10980
11669
|
function fileHash(path) {
|
|
10981
11670
|
if (!existsSync5(path)) return null;
|
|
10982
|
-
return
|
|
11671
|
+
return createHash7("sha256").update(readFileSync5(path)).digest("hex");
|
|
10983
11672
|
}
|
|
10984
11673
|
async function loadSelfHostComposeTemplate(templateRef, role) {
|
|
10985
11674
|
const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
|
|
@@ -11365,7 +12054,7 @@ function formatSecretPreflight(preflight) {
|
|
|
11365
12054
|
return parts.join("; ");
|
|
11366
12055
|
}
|
|
11367
12056
|
function hashFile(path) {
|
|
11368
|
-
const hash =
|
|
12057
|
+
const hash = createHash7("sha256");
|
|
11369
12058
|
const fd = openSync(path, "r");
|
|
11370
12059
|
const buffer = Buffer.allocUnsafe(CHECKSUM_BUFFER_BYTES);
|
|
11371
12060
|
try {
|
|
@@ -29386,7 +30075,10 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
29386
30075
|
},
|
|
29387
30076
|
models3d: {
|
|
29388
30077
|
generate: {
|
|
29389
|
-
text: "Generate 3D model"
|
|
30078
|
+
text: "Generate 3D model",
|
|
30079
|
+
description: {
|
|
30080
|
+
text: "Create a 3D model from a text prompt or reference images."
|
|
30081
|
+
}
|
|
29390
30082
|
}
|
|
29391
30083
|
},
|
|
29392
30084
|
music: {
|
|
@@ -30241,6 +30933,12 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
30241
30933
|
text: "Create structured idea maps and brainstorm topic trees."
|
|
30242
30934
|
}
|
|
30243
30935
|
},
|
|
30936
|
+
models3d: {
|
|
30937
|
+
text: "3D Models",
|
|
30938
|
+
description: {
|
|
30939
|
+
text: "Generate 3D models from prompts or reference images."
|
|
30940
|
+
}
|
|
30941
|
+
},
|
|
30244
30942
|
math: {
|
|
30245
30943
|
text: "Math",
|
|
30246
30944
|
description: {
|
|
@@ -46753,7 +47451,7 @@ function buildAssistantFeedbackDecision(rating) {
|
|
|
46753
47451
|
}
|
|
46754
47452
|
|
|
46755
47453
|
// src/benchmark.ts
|
|
46756
|
-
import { randomUUID as
|
|
47454
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
46757
47455
|
import { existsSync as existsSync6, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
46758
47456
|
import { tmpdir } from "os";
|
|
46759
47457
|
import { dirname as dirname2, join as join4, resolve as resolve5 } from "path";
|
|
@@ -47053,7 +47751,7 @@ async function handleBenchmark(client, subcommand, rest, flags) {
|
|
|
47053
47751
|
const caseIds = parseCaseIds(flags.case);
|
|
47054
47752
|
const dryRun = flags["dry-run"] === true;
|
|
47055
47753
|
const output = typeof flags.output === "string" ? flags.output : void 0;
|
|
47056
|
-
const runId = typeof flags["run-id"] === "string" ? flags["run-id"] :
|
|
47754
|
+
const runId = typeof flags["run-id"] === "string" ? flags["run-id"] : randomUUID5();
|
|
47057
47755
|
const imagePath = typeof flags.image === "string" ? resolve5(flags.image) : defaultImageFixturePath();
|
|
47058
47756
|
if (!dryRun && flags["confirm-spend-credits"] !== true) {
|
|
47059
47757
|
throw new Error(
|
|
@@ -47463,7 +48161,7 @@ function buildLongContextHistory() {
|
|
|
47463
48161
|
}
|
|
47464
48162
|
function appendHistory(history, role, content) {
|
|
47465
48163
|
history.push({
|
|
47466
|
-
message_id:
|
|
48164
|
+
message_id: randomUUID5(),
|
|
47467
48165
|
role,
|
|
47468
48166
|
sender_name: role === "user" ? "User" : "Assistant",
|
|
47469
48167
|
content,
|
|
@@ -47998,7 +48696,7 @@ function renderBody(state, width) {
|
|
|
47998
48696
|
case "tasks":
|
|
47999
48697
|
return renderTasks(state, width);
|
|
48000
48698
|
case "task":
|
|
48001
|
-
return
|
|
48699
|
+
return renderTaskDetail2(state, width);
|
|
48002
48700
|
case "start":
|
|
48003
48701
|
default:
|
|
48004
48702
|
return renderStart(width);
|
|
@@ -48071,7 +48769,7 @@ function renderTasks(state, width) {
|
|
|
48071
48769
|
}
|
|
48072
48770
|
return lines.flatMap((line) => wrap(line, width));
|
|
48073
48771
|
}
|
|
48074
|
-
function
|
|
48772
|
+
function renderTaskDetail2(state, width) {
|
|
48075
48773
|
const task = state.activeTask;
|
|
48076
48774
|
if (!task) return renderTasks(state, width);
|
|
48077
48775
|
const assignee = task.assigneeType === "ai" ? "OpenMates" : task.assigneeHash ?? "user";
|
|
@@ -48388,221 +49086,6 @@ function boxed(line, width) {
|
|
|
48388
49086
|
return `\u2502 ${line.padEnd(width)} \u2502`;
|
|
48389
49087
|
}
|
|
48390
49088
|
|
|
48391
|
-
// src/tasksCli.ts
|
|
48392
|
-
import { createHash as createHash7, randomBytes as randomBytes3, randomUUID as randomUUID5 } from "crypto";
|
|
48393
|
-
var TASK_STATUSES2 = ["backlog", "todo", "in_progress", "blocked", "done"];
|
|
48394
|
-
var DEFAULT_STANDALONE_PREFIX = "TASK";
|
|
48395
|
-
function normalizeTaskStatus(value) {
|
|
48396
|
-
if (value === void 0) return void 0;
|
|
48397
|
-
if (TASK_STATUSES2.includes(value)) return value;
|
|
48398
|
-
throw new Error(`Unknown task status '${value}'. Expected one of: ${TASK_STATUSES2.join(", ")}`);
|
|
48399
|
-
}
|
|
48400
|
-
function parseAssignee(value) {
|
|
48401
|
-
if (!value || value === "user") return { assigneeType: "user", assigneeHash: null };
|
|
48402
|
-
if (["ai", "openmates", "OpenMates"].includes(value)) return { assigneeType: "ai", assigneeHash: null };
|
|
48403
|
-
return { assigneeType: "user", assigneeHash: value };
|
|
48404
|
-
}
|
|
48405
|
-
function splitCsvFlag(value) {
|
|
48406
|
-
if (typeof value !== "string") return [];
|
|
48407
|
-
return value.split(/[,\n]/).map((item) => item.trim()).filter(Boolean);
|
|
48408
|
-
}
|
|
48409
|
-
function parseDueAt(value) {
|
|
48410
|
-
if (value === void 0) return void 0;
|
|
48411
|
-
if (value === false || value === true) throw new Error("--due requires a timestamp or date value.");
|
|
48412
|
-
const numeric = Number(value);
|
|
48413
|
-
if (Number.isFinite(numeric) && numeric > 0) return Math.floor(numeric);
|
|
48414
|
-
const parsed = Date.parse(value);
|
|
48415
|
-
if (Number.isNaN(parsed)) throw new Error(`Invalid --due value '${value}'.`);
|
|
48416
|
-
return Math.floor(parsed / 1e3);
|
|
48417
|
-
}
|
|
48418
|
-
async function buildCreateUserTaskInput(masterKey, input) {
|
|
48419
|
-
const taskKey = randomBytes3(32);
|
|
48420
|
-
const encryptedTaskKey = await encryptBytesWithAesGcm(taskKey, masterKey);
|
|
48421
|
-
const timestamp = nowSeconds();
|
|
48422
|
-
const assignee = parseAssignee(input.assign);
|
|
48423
|
-
const linkedProjectIds = input.projectIds ?? [];
|
|
48424
|
-
const status = input.status ?? (assignee.assigneeType === "ai" && !input.dueAt ? "in_progress" : "todo");
|
|
48425
|
-
return {
|
|
48426
|
-
task_id: randomUUIDCompat(),
|
|
48427
|
-
short_id: void 0,
|
|
48428
|
-
encrypted_task_key: encryptedTaskKey,
|
|
48429
|
-
encrypted_title: await encryptWithAesGcmCombined(input.title, taskKey),
|
|
48430
|
-
encrypted_description: await encryptWithAesGcmCombined(input.description ?? "", taskKey),
|
|
48431
|
-
encrypted_tags: await encryptWithAesGcmCombined("[]", taskKey),
|
|
48432
|
-
encrypted_linked_project_ids: await encryptWithAesGcmCombined(JSON.stringify(linkedProjectIds), taskKey),
|
|
48433
|
-
status,
|
|
48434
|
-
assignee_type: assignee.assigneeType,
|
|
48435
|
-
assignee_hash: assignee.assigneeHash,
|
|
48436
|
-
primary_chat_id: input.chatId ?? null,
|
|
48437
|
-
linked_project_ids: linkedProjectIds,
|
|
48438
|
-
plan_id: input.planId ?? null,
|
|
48439
|
-
due_at: input.dueAt ?? null,
|
|
48440
|
-
priority: 0,
|
|
48441
|
-
position: timestamp,
|
|
48442
|
-
created_at: timestamp,
|
|
48443
|
-
updated_at: timestamp
|
|
48444
|
-
};
|
|
48445
|
-
}
|
|
48446
|
-
async function buildUpdateUserTaskInput(task, masterKey, input) {
|
|
48447
|
-
const taskKey = await taskKeyFromRecord(task.encrypted, masterKey);
|
|
48448
|
-
const patch = { version: task.version, updated_at: nowSeconds() };
|
|
48449
|
-
if (input.title !== void 0) patch.encrypted_title = await encryptWithAesGcmCombined(input.title, taskKey);
|
|
48450
|
-
if (input.description !== void 0) patch.encrypted_description = await encryptWithAesGcmCombined(input.description, taskKey);
|
|
48451
|
-
if (input.status !== void 0) patch.status = input.status;
|
|
48452
|
-
if (input.assign !== void 0) {
|
|
48453
|
-
const assignee = parseAssignee(input.assign);
|
|
48454
|
-
patch.assignee_type = assignee.assigneeType;
|
|
48455
|
-
patch.assignee_hash = assignee.assigneeHash;
|
|
48456
|
-
}
|
|
48457
|
-
if (input.chatId !== void 0) patch.primary_chat_id = input.chatId;
|
|
48458
|
-
if (input.projectIds !== void 0) {
|
|
48459
|
-
patch.linked_project_ids = input.projectIds;
|
|
48460
|
-
patch.encrypted_linked_project_ids = await encryptWithAesGcmCombined(JSON.stringify(input.projectIds), taskKey);
|
|
48461
|
-
}
|
|
48462
|
-
if (input.planId !== void 0) patch.plan_id = input.planId;
|
|
48463
|
-
return patch;
|
|
48464
|
-
}
|
|
48465
|
-
async function decryptUserTask(record, masterKey) {
|
|
48466
|
-
const taskKey = await taskKeyFromRecord(record, masterKey);
|
|
48467
|
-
const tags = parseStringArray(await decryptOptional(record.encrypted_tags, taskKey));
|
|
48468
|
-
const linkedProjectIds = parseStringArray(await decryptOptional(record.encrypted_linked_project_ids, taskKey));
|
|
48469
|
-
return {
|
|
48470
|
-
taskId: record.task_id,
|
|
48471
|
-
shortId: record.short_id || deriveShortId(record),
|
|
48472
|
-
title: await decryptOptional(record.encrypted_title, taskKey) || "(untitled task)",
|
|
48473
|
-
description: await decryptOptional(record.encrypted_description, taskKey),
|
|
48474
|
-
tags,
|
|
48475
|
-
latestInstruction: await decryptOptional(record.encrypted_latest_instruction, taskKey),
|
|
48476
|
-
status: record.status,
|
|
48477
|
-
assigneeType: record.assignee_type,
|
|
48478
|
-
assigneeHash: record.assignee_hash ?? null,
|
|
48479
|
-
primaryChatId: record.primary_chat_id ?? null,
|
|
48480
|
-
linkedProjectIds: linkedProjectIds.length > 0 ? linkedProjectIds : record.linked_project_ids ?? [],
|
|
48481
|
-
planId: record.plan_id ?? null,
|
|
48482
|
-
dueAt: record.due_at ?? null,
|
|
48483
|
-
priority: record.priority ?? 0,
|
|
48484
|
-
position: record.position ?? 0,
|
|
48485
|
-
queueState: record.queue_state ?? "none",
|
|
48486
|
-
blockedReasonCode: record.blocked_reason_code ?? null,
|
|
48487
|
-
aiExecutionState: record.ai_execution_state ?? null,
|
|
48488
|
-
version: record.version ?? 1,
|
|
48489
|
-
encrypted: record
|
|
48490
|
-
};
|
|
48491
|
-
}
|
|
48492
|
-
async function decryptUserTasks(records, masterKey) {
|
|
48493
|
-
const output = [];
|
|
48494
|
-
for (const record of records) output.push(await decryptUserTask(record, masterKey));
|
|
48495
|
-
return output;
|
|
48496
|
-
}
|
|
48497
|
-
function findTask(tasks, id) {
|
|
48498
|
-
const task = tasks.find((candidate) => candidate.taskId === id || candidate.shortId === id);
|
|
48499
|
-
if (!task) throw new Error(`Task '${id}' was not found in the current task list.`);
|
|
48500
|
-
return task;
|
|
48501
|
-
}
|
|
48502
|
-
function renderTaskList(tasks) {
|
|
48503
|
-
if (tasks.length === 0) return "No tasks found.";
|
|
48504
|
-
const lines = ["Tasks", "ID Status Assignee Queue Title"];
|
|
48505
|
-
for (const task of tasks) {
|
|
48506
|
-
lines.push(`${pad(task.shortId, 9)} ${pad(task.status, 12)} ${pad(assigneeLabel(task), 11)} ${pad(task.queueState, 11)} ${task.title}`);
|
|
48507
|
-
}
|
|
48508
|
-
return lines.join("\n");
|
|
48509
|
-
}
|
|
48510
|
-
function renderTaskDetail2(task) {
|
|
48511
|
-
const lines = [
|
|
48512
|
-
`Task ${task.shortId}`,
|
|
48513
|
-
`Title: ${task.title}`,
|
|
48514
|
-
`Status: ${task.status}`,
|
|
48515
|
-
`Assignee: ${assigneeLabel(task)}`,
|
|
48516
|
-
`Queue: ${task.queueState}`,
|
|
48517
|
-
`Task ID: ${task.taskId}`
|
|
48518
|
-
];
|
|
48519
|
-
if (task.description) lines.push(`Description: ${task.description}`);
|
|
48520
|
-
if (task.primaryChatId) lines.push(`Chat: ${task.primaryChatId}`);
|
|
48521
|
-
if (task.linkedProjectIds.length > 0) lines.push(`Projects: ${task.linkedProjectIds.join(", ")}`);
|
|
48522
|
-
if (task.planId) lines.push(`Plan: ${task.planId}`);
|
|
48523
|
-
if (task.blockedReasonCode) lines.push(`Blocked reason: ${task.blockedReasonCode}`);
|
|
48524
|
-
if (task.aiExecutionState) lines.push(`AI state: ${task.aiExecutionState}`);
|
|
48525
|
-
return lines.join("\n");
|
|
48526
|
-
}
|
|
48527
|
-
function renderTaskBoard(tasks, width = process.stdout.columns || 100) {
|
|
48528
|
-
const columns = TASK_STATUSES2.map((status) => ({ status, tasks: tasks.filter((task) => task.status === status).sort(compareTasks) }));
|
|
48529
|
-
if (width < 96) {
|
|
48530
|
-
const lines2 = ["OpenMates Tasks Board"];
|
|
48531
|
-
for (const column of columns) {
|
|
48532
|
-
lines2.push("", `${columnTitle(column.status)} (${column.tasks.length})`, "-".repeat(24));
|
|
48533
|
-
lines2.push(...boardColumnLines(column.tasks, 8));
|
|
48534
|
-
}
|
|
48535
|
-
return lines2.join("\n");
|
|
48536
|
-
}
|
|
48537
|
-
const perColumn = 6;
|
|
48538
|
-
const columnWidth = 22;
|
|
48539
|
-
const lines = ["OpenMates Tasks Board", ""];
|
|
48540
|
-
lines.push(columns.map((column) => pad(`${columnTitle(column.status)} (${column.tasks.length})`, columnWidth)).join(" "));
|
|
48541
|
-
lines.push(columns.map(() => "-".repeat(columnWidth)).join(" "));
|
|
48542
|
-
const renderedColumns = columns.map((column) => boardColumnLines(column.tasks, perColumn).map((line) => truncate(line, columnWidth)));
|
|
48543
|
-
const maxRows = Math.max(...renderedColumns.map((column) => column.length));
|
|
48544
|
-
for (let row = 0; row < maxRows; row += 1) {
|
|
48545
|
-
lines.push(renderedColumns.map((column) => pad(column[row] ?? "", columnWidth)).join(" "));
|
|
48546
|
-
}
|
|
48547
|
-
return lines.join("\n");
|
|
48548
|
-
}
|
|
48549
|
-
function boardColumnLines(tasks, limit) {
|
|
48550
|
-
if (tasks.length === 0) return ["No tasks here."];
|
|
48551
|
-
const visible = tasks.slice(0, limit).flatMap((task) => [
|
|
48552
|
-
`[${task.shortId}] ${task.title}`,
|
|
48553
|
-
` ${assigneeLabel(task)} ${task.queueState === "none" ? "" : task.queueState}`.trimEnd()
|
|
48554
|
-
]);
|
|
48555
|
-
if (tasks.length > limit) visible.push(`... ${tasks.length - limit} more`);
|
|
48556
|
-
return visible;
|
|
48557
|
-
}
|
|
48558
|
-
function compareTasks(a, b) {
|
|
48559
|
-
return a.position - b.position || a.title.localeCompare(b.title);
|
|
48560
|
-
}
|
|
48561
|
-
function columnTitle(status) {
|
|
48562
|
-
if (status === "in_progress") return "In progress";
|
|
48563
|
-
return status.slice(0, 1).toUpperCase() + status.slice(1).replace("_", " ");
|
|
48564
|
-
}
|
|
48565
|
-
function assigneeLabel(task) {
|
|
48566
|
-
return task.assigneeType === "ai" ? "OpenMates" : task.assigneeHash ?? "user";
|
|
48567
|
-
}
|
|
48568
|
-
async function taskKeyFromRecord(record, masterKey) {
|
|
48569
|
-
if (!record.encrypted_task_key) throw new Error(`Task ${record.task_id} is missing encrypted task key.`);
|
|
48570
|
-
const taskKey = await decryptBytesWithAesGcm(record.encrypted_task_key, masterKey);
|
|
48571
|
-
if (!taskKey) throw new Error(`Failed to decrypt task key for ${record.task_id}.`);
|
|
48572
|
-
return taskKey;
|
|
48573
|
-
}
|
|
48574
|
-
async function decryptOptional(value, key) {
|
|
48575
|
-
if (!value) return "";
|
|
48576
|
-
return await decryptWithAesGcmCombined(value, key) ?? "";
|
|
48577
|
-
}
|
|
48578
|
-
function parseStringArray(value) {
|
|
48579
|
-
if (!value) return [];
|
|
48580
|
-
try {
|
|
48581
|
-
const parsed = JSON.parse(value);
|
|
48582
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
48583
|
-
} catch {
|
|
48584
|
-
return [];
|
|
48585
|
-
}
|
|
48586
|
-
}
|
|
48587
|
-
function deriveShortId(record) {
|
|
48588
|
-
const prefix = record.short_id_prefix || DEFAULT_STANDALONE_PREFIX;
|
|
48589
|
-
const source = record.task_id || `${record.created_at ?? ""}-${record.position ?? ""}`;
|
|
48590
|
-
const digest = createHash7("sha256").update(source).digest("hex").slice(0, 4).toUpperCase();
|
|
48591
|
-
return `${prefix}-${parseInt(digest, 16) % 1e4}`;
|
|
48592
|
-
}
|
|
48593
|
-
function nowSeconds() {
|
|
48594
|
-
return Math.floor(Date.now() / 1e3);
|
|
48595
|
-
}
|
|
48596
|
-
function randomUUIDCompat() {
|
|
48597
|
-
return randomUUID5();
|
|
48598
|
-
}
|
|
48599
|
-
function pad(value, length) {
|
|
48600
|
-
return truncate(value, length).padEnd(length);
|
|
48601
|
-
}
|
|
48602
|
-
function truncate(value, length) {
|
|
48603
|
-
return value.length <= length ? value : `${value.slice(0, Math.max(0, length - 3))}...`;
|
|
48604
|
-
}
|
|
48605
|
-
|
|
48606
49089
|
// src/tui.ts
|
|
48607
49090
|
function defaultModeForStreams(input, output) {
|
|
48608
49091
|
return input.isTTY === true && output.isTTY === true ? "tui" : "quickstart";
|
|
@@ -49036,7 +49519,7 @@ async function handleActiveTaskAction(params) {
|
|
|
49036
49519
|
render();
|
|
49037
49520
|
return;
|
|
49038
49521
|
}
|
|
49039
|
-
await client.deleteUserTask(task.taskId);
|
|
49522
|
+
await client.deleteUserTask(task.taskId, task.version);
|
|
49040
49523
|
state.tasks = state.tasks.filter((candidate) => candidate.taskId !== task.taskId);
|
|
49041
49524
|
state.activeTask = null;
|
|
49042
49525
|
state.selectedIndex = Math.min(state.selectedIndex, Math.max(0, state.tasks.length - 1));
|
|
@@ -49053,7 +49536,7 @@ async function handleActiveTaskAction(params) {
|
|
|
49053
49536
|
render();
|
|
49054
49537
|
return;
|
|
49055
49538
|
}
|
|
49056
|
-
const updated2 = await client.reorderUserTasks({ moves: [{ task_id: task.taskId, position }] });
|
|
49539
|
+
const updated2 = await client.reorderUserTasks({ moves: [{ task_id: task.taskId, version: task.version, position }] });
|
|
49057
49540
|
const [decrypted2] = await decryptUserTasks(updated2, client.getMasterKeyBytes());
|
|
49058
49541
|
if (decrypted2) {
|
|
49059
49542
|
replaceTask(state, decrypted2);
|
|
@@ -50145,7 +50628,7 @@ async function handleTasks(client, subcommand, rest, flags) {
|
|
|
50145
50628
|
if (subcommand === "delete") {
|
|
50146
50629
|
const task = await requiredResolvedTask(client, masterKey, rest[0], scope, "delete");
|
|
50147
50630
|
if (flags.confirm !== true) throw new Error("Deleting a task requires --confirm.");
|
|
50148
|
-
const result = await client.deleteUserTask(task.taskId);
|
|
50631
|
+
const result = await client.deleteUserTask(task.taskId, task.version);
|
|
50149
50632
|
if (flags.json === true) printJson2(result);
|
|
50150
50633
|
else console.log(`Task deleted: ${task.shortId}`);
|
|
50151
50634
|
return;
|
|
@@ -50176,7 +50659,7 @@ async function handleTasks(client, subcommand, rest, flags) {
|
|
|
50176
50659
|
}
|
|
50177
50660
|
if (subcommand === "reorder") {
|
|
50178
50661
|
const task = await requiredResolvedTask(client, masterKey, rest[0], scope, "reorder");
|
|
50179
|
-
const move = { task_id: task.taskId };
|
|
50662
|
+
const move = { task_id: task.taskId, version: task.version };
|
|
50180
50663
|
if (typeof flags.before === "string") move.before_task_id = (await resolveTask(client, masterKey, flags.before, scope)).taskId;
|
|
50181
50664
|
if (typeof flags.after === "string") move.after_task_id = (await resolveTask(client, masterKey, flags.after, scope)).taskId;
|
|
50182
50665
|
if (typeof flags.status === "string") move.status = normalizeTaskStatus(flags.status);
|
|
@@ -50211,7 +50694,7 @@ async function requiredResolvedTask(client, masterKey, id, scope, action) {
|
|
|
50211
50694
|
}
|
|
50212
50695
|
function printTaskOutput(task, flags) {
|
|
50213
50696
|
if (flags.json === true) printJson2({ task: taskToJson(task) });
|
|
50214
|
-
else console.log(
|
|
50697
|
+
else console.log(renderTaskDetail(task));
|
|
50215
50698
|
}
|
|
50216
50699
|
function taskToJson(task) {
|
|
50217
50700
|
return {
|
|
@@ -50316,6 +50799,10 @@ function parsePositiveIntegerFlag(value, flagName) {
|
|
|
50316
50799
|
}
|
|
50317
50800
|
return parsed;
|
|
50318
50801
|
}
|
|
50802
|
+
function parseResponseTimeoutMs(flags) {
|
|
50803
|
+
const seconds = parsePositiveIntegerFlag(flags["response-timeout-seconds"], "--response-timeout-seconds");
|
|
50804
|
+
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
50805
|
+
}
|
|
50319
50806
|
function parseRemoteAccessSourceType(value) {
|
|
50320
50807
|
if (value === void 0) return "local_folder";
|
|
50321
50808
|
if (value === "local_folder" || value === "local_git_repository") {
|
|
@@ -50459,6 +50946,7 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
|
|
|
50459
50946
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
50460
50947
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
50461
50948
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
50949
|
+
responseTimeoutMs: parseResponseTimeoutMs(flags),
|
|
50462
50950
|
anonymousLearningMode: client.hasSession() ? void 0 : parseAnonymousLearningModeFlags(flags)
|
|
50463
50951
|
},
|
|
50464
50952
|
redactor
|
|
@@ -50534,7 +51022,8 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
50534
51022
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
50535
51023
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
50536
51024
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
50537
|
-
piiDetection: flags["no-pii-detection"] !== true
|
|
51025
|
+
piiDetection: flags["no-pii-detection"] !== true,
|
|
51026
|
+
responseTimeoutMs: parseResponseTimeoutMs(flags)
|
|
50538
51027
|
},
|
|
50539
51028
|
redactor
|
|
50540
51029
|
);
|
|
@@ -50563,7 +51052,8 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
50563
51052
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
50564
51053
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
50565
51054
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
50566
|
-
piiDetection: flags["no-pii-detection"] !== true
|
|
51055
|
+
piiDetection: flags["no-pii-detection"] !== true,
|
|
51056
|
+
responseTimeoutMs: parseResponseTimeoutMs(flags)
|
|
50567
51057
|
},
|
|
50568
51058
|
redactor
|
|
50569
51059
|
);
|
|
@@ -50584,7 +51074,8 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
50584
51074
|
json: flags.json === true,
|
|
50585
51075
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
50586
51076
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
50587
|
-
piiDetection: flags["no-pii-detection"] !== true
|
|
51077
|
+
piiDetection: flags["no-pii-detection"] !== true,
|
|
51078
|
+
responseTimeoutMs: parseResponseTimeoutMs(flags)
|
|
50588
51079
|
},
|
|
50589
51080
|
redactor
|
|
50590
51081
|
);
|
|
@@ -53971,7 +54462,7 @@ async function sendMessageStreaming(client, params, redactor) {
|
|
|
53971
54462
|
process.stdout.write(`${result2.assistant}
|
|
53972
54463
|
`);
|
|
53973
54464
|
}
|
|
53974
|
-
return { ...result2, acceptedTaskProposals: [] };
|
|
54465
|
+
return { ...result2, taskEvents: [], pendingTaskUpdateJobs: [], acceptedTaskProposals: [] };
|
|
53975
54466
|
}
|
|
53976
54467
|
const urlResult = prepareUrlEmbeds(finalMessage);
|
|
53977
54468
|
finalMessage = urlResult.message;
|
|
@@ -53986,6 +54477,7 @@ async function sendMessageStreaming(client, params, redactor) {
|
|
|
53986
54477
|
onSubChatApprovalRequest,
|
|
53987
54478
|
autoApproveSubChats: params.autoApproveSubChats,
|
|
53988
54479
|
autoApproveMemories: params.autoApproveMemories,
|
|
54480
|
+
responseTimeoutMs: params.responseTimeoutMs,
|
|
53989
54481
|
learningMode,
|
|
53990
54482
|
preparedEmbeds: preparedEmbeds.length > 0 ? preparedEmbeds : void 0,
|
|
53991
54483
|
piiMappings: piiResult.mappings.map((mapping) => ({
|
|
@@ -54077,6 +54569,24 @@ ${result.assistant}`) : [];
|
|
|
54077
54569
|
process.stdout.write(`${SEP}
|
|
54078
54570
|
`);
|
|
54079
54571
|
}
|
|
54572
|
+
if (result.taskEvents.length > 0) {
|
|
54573
|
+
process.stdout.write(`\x1B[2mTask updates:\x1B[0m
|
|
54574
|
+
`);
|
|
54575
|
+
for (const event of result.taskEvents) {
|
|
54576
|
+
process.stdout.write(` \x1B[2m- ${formatTaskEvent(event)}\x1B[0m
|
|
54577
|
+
`);
|
|
54578
|
+
}
|
|
54579
|
+
process.stdout.write(`${SEP}
|
|
54580
|
+
`);
|
|
54581
|
+
}
|
|
54582
|
+
if (result.pendingTaskUpdateJobs.length > 0) {
|
|
54583
|
+
const count = result.pendingTaskUpdateJobs.length;
|
|
54584
|
+
process.stdout.write(
|
|
54585
|
+
`\x1B[2mPending encrypted task update${count === 1 ? "" : "s"}: ${count}\x1B[0m
|
|
54586
|
+
${SEP}
|
|
54587
|
+
`
|
|
54588
|
+
);
|
|
54589
|
+
}
|
|
54080
54590
|
process.stdout.write(
|
|
54081
54591
|
`\x1B[2mContinue: openmates chats send --chat ${shortId} "your message"\x1B[0m
|
|
54082
54592
|
\x1B[2mHistory: openmates chats show ${shortId}\x1B[0m
|
|
@@ -54085,6 +54595,26 @@ ${result.assistant}`) : [];
|
|
|
54085
54595
|
}
|
|
54086
54596
|
return { ...result, acceptedTaskProposals };
|
|
54087
54597
|
}
|
|
54598
|
+
function formatTaskEvent(event) {
|
|
54599
|
+
const taskLabel = event.short_id || event.task_id;
|
|
54600
|
+
const title = event.title ? ` "${event.title}"` : "";
|
|
54601
|
+
const status = event.status ? ` (${event.status})` : "";
|
|
54602
|
+
const reason = event.reason ? `: ${event.reason}` : "";
|
|
54603
|
+
switch (event.event_type) {
|
|
54604
|
+
case "created":
|
|
54605
|
+
return `${taskLabel} created${title}${status}`;
|
|
54606
|
+
case "updated":
|
|
54607
|
+
return `${taskLabel} updated${title}${status}`;
|
|
54608
|
+
case "blocked":
|
|
54609
|
+
return `${taskLabel} blocked${reason}`;
|
|
54610
|
+
case "completed":
|
|
54611
|
+
return `${taskLabel} completed${title}`;
|
|
54612
|
+
case "unblocked":
|
|
54613
|
+
return `${taskLabel} unblocked`;
|
|
54614
|
+
default:
|
|
54615
|
+
return `${taskLabel} ${event.event_type}${title}${status}${reason}`;
|
|
54616
|
+
}
|
|
54617
|
+
}
|
|
54088
54618
|
async function acceptChatTaskProposals(client, chatId, proposals, fallbackText) {
|
|
54089
54619
|
let proposalsToAccept = proposals;
|
|
54090
54620
|
if (proposalsToAccept.length === 0 && fallbackText.trim()) {
|