@mirrowel/opencode-openshare 0.1.1 → 0.2.0-dev.1
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/ci/openshare.js +583 -160
- package/dist/contract.d.ts +156 -0
- package/dist/contract.js +263 -0
- package/dist/core.js +411 -55
- package/dist/dialogs.js +38 -0
- package/dist/sidebar.d.ts +3 -1
- package/dist/sidebar.js +3 -17
- package/dist/tui.js +31 -36
- package/dist/v2-server.d.ts +0 -27
- package/dist/v2-server.js +6 -121
- package/package.json +3 -1
- package/src/contract.ts +320 -0
- package/src/core.ts +403 -56
- package/src/dialogs.tsx +34 -0
- package/src/sidebar.tsx +4 -21
- package/src/v2-server.ts +7 -110
package/ci/openshare.js
CHANGED
|
@@ -346,6 +346,215 @@ function resolveShareMode(cfg, input) {
|
|
|
346
346
|
// src/core.ts
|
|
347
347
|
import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
348
348
|
import { join as join2 } from "node:path";
|
|
349
|
+
|
|
350
|
+
// src/contract.ts
|
|
351
|
+
function messageTombstone(sessionID, messageID) {
|
|
352
|
+
return { type: "message", del: true, sessionID, data: { id: messageID } };
|
|
353
|
+
}
|
|
354
|
+
function isV2NativeMessage(x) {
|
|
355
|
+
if (!x || typeof x !== "object")
|
|
356
|
+
return false;
|
|
357
|
+
const r = x;
|
|
358
|
+
return typeof r.type === "string" && r.type !== "" && r.role === undefined && typeof r.id === "string";
|
|
359
|
+
}
|
|
360
|
+
var TOOL_NAME_MAP = {
|
|
361
|
+
bash: "shell",
|
|
362
|
+
task: "subagent"
|
|
363
|
+
};
|
|
364
|
+
var INPUT_KEY_MAP = {
|
|
365
|
+
filePath: "path",
|
|
366
|
+
subagent_type: "agent"
|
|
367
|
+
};
|
|
368
|
+
function normalizeToolInput(input, title) {
|
|
369
|
+
const out = {};
|
|
370
|
+
for (const [k, v] of Object.entries(input ?? {}))
|
|
371
|
+
out[INPUT_KEY_MAP[k] ?? k] = v;
|
|
372
|
+
if (title && out.description === undefined)
|
|
373
|
+
out.description = title;
|
|
374
|
+
return out;
|
|
375
|
+
}
|
|
376
|
+
function normalizeToolMetadata(meta) {
|
|
377
|
+
const out = {};
|
|
378
|
+
for (const [k, v] of Object.entries(meta ?? {})) {
|
|
379
|
+
if (k === "filediff")
|
|
380
|
+
out.files = [v];
|
|
381
|
+
else if (k === "sessionId")
|
|
382
|
+
out.sessionID = v;
|
|
383
|
+
else
|
|
384
|
+
out[k] = v;
|
|
385
|
+
}
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
function toolContent(output) {
|
|
389
|
+
const text = typeof output === "string" ? output : "";
|
|
390
|
+
return [{ type: "text", text: text || " " }];
|
|
391
|
+
}
|
|
392
|
+
function normalizeToolState(state) {
|
|
393
|
+
const status = String(state?.status ?? "pending");
|
|
394
|
+
if (status === "pending") {
|
|
395
|
+
return { status: "streaming", input: typeof state.raw === "string" ? state.raw : JSON.stringify(state.input ?? {}) };
|
|
396
|
+
}
|
|
397
|
+
if (status === "running") {
|
|
398
|
+
return { status: "running", input: normalizeToolInput(state.input ?? {}, state.title), metadata: normalizeToolMetadata(state.metadata) };
|
|
399
|
+
}
|
|
400
|
+
if (status === "completed") {
|
|
401
|
+
return { status: "completed", input: normalizeToolInput(state.input ?? {}, state.title), content: toolContent(state.output), metadata: normalizeToolMetadata(state.metadata) };
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
status: "error",
|
|
405
|
+
input: normalizeToolInput(state.input ?? {}),
|
|
406
|
+
error: { type: "unknown", message: typeof state.error === "string" ? state.error : "tool failed" },
|
|
407
|
+
metadata: normalizeToolMetadata(state.metadata)
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function normalizeFilePart(p) {
|
|
411
|
+
const url = typeof p.url === "string" ? p.url : "";
|
|
412
|
+
if (!url)
|
|
413
|
+
return;
|
|
414
|
+
const inline = url.startsWith("data:");
|
|
415
|
+
const comma = url.indexOf(",");
|
|
416
|
+
return {
|
|
417
|
+
data: inline ? url.slice(comma + 1) : url,
|
|
418
|
+
mime: typeof p.mime === "string" ? p.mime : "application/octet-stream",
|
|
419
|
+
source: inline ? { type: "inline" } : { type: "uri", uri: url },
|
|
420
|
+
...typeof p.filename === "string" ? { name: p.filename } : {}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function normalizeV1Message(info, parts) {
|
|
424
|
+
if (!info || typeof info.id !== "string")
|
|
425
|
+
return;
|
|
426
|
+
const id = info.id;
|
|
427
|
+
const created = info.time?.created ?? 0;
|
|
428
|
+
if (info.role === "user") {
|
|
429
|
+
const compaction = parts.find((p) => p?.type === "compaction");
|
|
430
|
+
if (compaction) {
|
|
431
|
+
const message = {
|
|
432
|
+
id,
|
|
433
|
+
type: "compaction",
|
|
434
|
+
status: "completed",
|
|
435
|
+
reason: compaction.auto === false ? "manual" : "auto",
|
|
436
|
+
summary: "",
|
|
437
|
+
recent: "",
|
|
438
|
+
time: { created }
|
|
439
|
+
};
|
|
440
|
+
return { message };
|
|
441
|
+
}
|
|
442
|
+
const text = parts.filter((p) => p?.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
|
|
443
|
+
`);
|
|
444
|
+
const files = parts.filter((p) => p?.type === "file").map(normalizeFilePart).filter((f) => f !== undefined);
|
|
445
|
+
const agents = parts.filter((p) => p?.type === "agent").map((p) => ({ name: String(p.name ?? "") })).filter((a) => a.name);
|
|
446
|
+
const model = info.model;
|
|
447
|
+
const message = {
|
|
448
|
+
id,
|
|
449
|
+
type: "user",
|
|
450
|
+
time: { created },
|
|
451
|
+
text,
|
|
452
|
+
...files.length ? { files } : {},
|
|
453
|
+
...agents.length ? { agents } : {},
|
|
454
|
+
metadata: {
|
|
455
|
+
...info.agent !== undefined ? { agent: String(info.agent) } : {},
|
|
456
|
+
...model ? { model: { id: String(model.modelID ?? ""), providerID: String(model.providerID ?? "") } } : {}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
return { message };
|
|
460
|
+
}
|
|
461
|
+
if (info.role === "assistant") {
|
|
462
|
+
const time = info.time;
|
|
463
|
+
if (info.summary === true && info.mode === "compaction") {
|
|
464
|
+
const summary = parts.filter((p) => p?.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
|
|
465
|
+
`);
|
|
466
|
+
return { refill: String(info.parentID ?? ""), summary };
|
|
467
|
+
}
|
|
468
|
+
const content = [];
|
|
469
|
+
let cost = typeof info.cost === "number" ? info.cost : 0;
|
|
470
|
+
let tokens;
|
|
471
|
+
let retry;
|
|
472
|
+
for (const p of parts) {
|
|
473
|
+
switch (p?.type) {
|
|
474
|
+
case "text":
|
|
475
|
+
content.push({ type: "text", text: String(p.text ?? "") });
|
|
476
|
+
break;
|
|
477
|
+
case "reasoning": {
|
|
478
|
+
const rt = p.time;
|
|
479
|
+
content.push({ type: "reasoning", text: String(p.text ?? ""), time: { created: rt?.start ?? undefined, completed: rt?.end ?? undefined } });
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case "tool": {
|
|
483
|
+
const st = p.state;
|
|
484
|
+
const tt = st?.time ?? {};
|
|
485
|
+
content.push({
|
|
486
|
+
type: "tool",
|
|
487
|
+
id: String(p.id ?? ""),
|
|
488
|
+
name: TOOL_NAME_MAP[String(p.tool ?? "unknown")] ?? String(p.tool ?? "unknown"),
|
|
489
|
+
state: normalizeToolState(st ?? {}),
|
|
490
|
+
time: { created: tt.start ?? undefined, completed: tt.end ?? undefined }
|
|
491
|
+
});
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
case "retry": {
|
|
495
|
+
const err = p.error;
|
|
496
|
+
retry = {
|
|
497
|
+
attempt: Number(p.attempt ?? 0),
|
|
498
|
+
at: p.time?.created ?? 0,
|
|
499
|
+
error: { type: "unknown", message: String(err?.message ?? err?.name ?? "retry") }
|
|
500
|
+
};
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
case "step-finish": {
|
|
504
|
+
if (typeof p.cost === "number")
|
|
505
|
+
cost += p.cost;
|
|
506
|
+
if (p.tokens && typeof p.tokens === "object")
|
|
507
|
+
tokens = p.tokens;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const finish = typeof info.finish === "string" ? info.finish : undefined;
|
|
513
|
+
const errObj = info.error;
|
|
514
|
+
const message = {
|
|
515
|
+
id,
|
|
516
|
+
type: "assistant",
|
|
517
|
+
agent: String(info.agent ?? "build"),
|
|
518
|
+
model: { id: String(info.modelID ?? ""), providerID: String(info.providerID ?? "") },
|
|
519
|
+
time: { created, streamed: time?.started ?? undefined, completed: time?.completed ?? undefined },
|
|
520
|
+
content,
|
|
521
|
+
cost,
|
|
522
|
+
...tokens ? { tokens } : {},
|
|
523
|
+
...finish ? { finish } : {},
|
|
524
|
+
...errObj ? { error: { type: String(errObj.name ?? "UnknownError"), message: String(errObj.message ?? "turn failed") } } : {},
|
|
525
|
+
...retry ? { retry } : {}
|
|
526
|
+
};
|
|
527
|
+
return { message };
|
|
528
|
+
}
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
function normalizeV1Diff(diff) {
|
|
532
|
+
if (!Array.isArray(diff))
|
|
533
|
+
return diff;
|
|
534
|
+
return diff.map((d) => {
|
|
535
|
+
if (!d || typeof d !== "object")
|
|
536
|
+
return d;
|
|
537
|
+
const r = d;
|
|
538
|
+
if (typeof r.additions === "number" && typeof r.deletions === "number")
|
|
539
|
+
return r;
|
|
540
|
+
const patch = typeof r.patch === "string" ? r.patch : "";
|
|
541
|
+
let additions = 0;
|
|
542
|
+
let deletions = 0;
|
|
543
|
+
for (const line of patch.split(`
|
|
544
|
+
`)) {
|
|
545
|
+
if (line.startsWith("+") && !line.startsWith("+++"))
|
|
546
|
+
additions++;
|
|
547
|
+
else if (line.startsWith("-") && !line.startsWith("---"))
|
|
548
|
+
deletions++;
|
|
549
|
+
}
|
|
550
|
+
return { ...r, additions: typeof r.additions === "number" ? r.additions : additions, deletions: typeof r.deletions === "number" ? r.deletions : deletions };
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
function cursorFor(id, time) {
|
|
554
|
+
return Buffer.from(JSON.stringify({ id, time })).toString("base64url");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/core.ts
|
|
349
558
|
var DEFAULT_POLICY = {
|
|
350
559
|
mode: "persistent",
|
|
351
560
|
turnGraceMs: 120000,
|
|
@@ -381,11 +590,9 @@ function itemFor(type, data, sessionID) {
|
|
|
381
590
|
if (type === "prefs")
|
|
382
591
|
return { key: "prefs", item: { type, data } };
|
|
383
592
|
if (type === "session")
|
|
384
|
-
return { key: `session/${data?.id ?? ""}`, item: { type, data } };
|
|
593
|
+
return { key: `session/${data?.id ?? ""}`, item: { type, sessionID, data } };
|
|
385
594
|
if (type === "message")
|
|
386
|
-
return { key: `message/${data?.id ?? ""}`, item: { type, data } };
|
|
387
|
-
if (type === "part")
|
|
388
|
-
return { key: `part/${data?.messageID ?? ""}/${data?.id ?? ""}`, item: { type, data } };
|
|
595
|
+
return { key: `message/${data?.id ?? ""}`, item: { type, sessionID, data } };
|
|
389
596
|
if (type === "session_diff")
|
|
390
597
|
return { key: `session_diff/${sessionID}`, item: { type, sessionID, data } };
|
|
391
598
|
return;
|
|
@@ -637,7 +844,8 @@ function createEngine(deps) {
|
|
|
637
844
|
opts: undefined,
|
|
638
845
|
activated: false,
|
|
639
846
|
wroteBinding: false,
|
|
640
|
-
everConnected: false
|
|
847
|
+
everConnected: false,
|
|
848
|
+
merged: new Map
|
|
641
849
|
});
|
|
642
850
|
const refreshConfig = () => {
|
|
643
851
|
const next = loadConfig(deps.pluginDir, deps.options);
|
|
@@ -849,6 +1057,14 @@ function createEngine(deps) {
|
|
|
849
1057
|
logError("spool save failed", e?.message || e);
|
|
850
1058
|
}
|
|
851
1059
|
};
|
|
1060
|
+
const spoolAppend = (st, r, f) => {
|
|
1061
|
+
try {
|
|
1062
|
+
appendFileSync(queuePath(st, r.url), JSON.stringify(f) + `
|
|
1063
|
+
`);
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
logError("spool append failed", e?.message || e);
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
852
1068
|
const spoolLoad = (st, r) => {
|
|
853
1069
|
try {
|
|
854
1070
|
const meta = JSON.parse(readFileSync2(metaPath(st, r.url), "utf8"));
|
|
@@ -858,8 +1074,18 @@ function createEngine(deps) {
|
|
|
858
1074
|
r.ackedRev = meta.ackedRev ?? 0;
|
|
859
1075
|
}
|
|
860
1076
|
if (existsSync2(queuePath(st, r.url))) {
|
|
861
|
-
r.pending =
|
|
862
|
-
|
|
1077
|
+
r.pending = [];
|
|
1078
|
+
for (const line of readFileSync2(queuePath(st, r.url), "utf8").split(`
|
|
1079
|
+
`)) {
|
|
1080
|
+
if (!line.trim())
|
|
1081
|
+
continue;
|
|
1082
|
+
try {
|
|
1083
|
+
r.pending.push(JSON.parse(line));
|
|
1084
|
+
} catch {
|
|
1085
|
+
log(`spool load: dropped torn tail line for ${st.sid.slice(0, 18)}`);
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
863
1089
|
}
|
|
864
1090
|
} catch {
|
|
865
1091
|
r.pending = [];
|
|
@@ -896,12 +1122,29 @@ function createEngine(deps) {
|
|
|
896
1122
|
}
|
|
897
1123
|
return true;
|
|
898
1124
|
};
|
|
899
|
-
const
|
|
1125
|
+
const chunkItems = (items, maxItems, maxBytes) => {
|
|
1126
|
+
const chunks = [];
|
|
1127
|
+
let cur = [];
|
|
1128
|
+
let curBytes = 0;
|
|
1129
|
+
for (const it of items) {
|
|
1130
|
+
const n = (JSON.stringify(it) ?? "null").length;
|
|
1131
|
+
if (cur.length && (cur.length >= maxItems || curBytes + n > maxBytes)) {
|
|
1132
|
+
chunks.push(cur);
|
|
1133
|
+
cur = [];
|
|
1134
|
+
curBytes = 0;
|
|
1135
|
+
}
|
|
1136
|
+
cur.push(it);
|
|
1137
|
+
curBytes += n;
|
|
1138
|
+
}
|
|
1139
|
+
if (cur.length)
|
|
1140
|
+
chunks.push(cur);
|
|
1141
|
+
return chunks;
|
|
1142
|
+
};
|
|
900
1143
|
const sendSync = (r, f) => {
|
|
901
1144
|
if (!f.items.length)
|
|
902
1145
|
return wsSend(r, { op: "sync", items: [], status: f.status });
|
|
903
|
-
for (
|
|
904
|
-
wsSend(r, { op: "sync", items:
|
|
1146
|
+
for (const chunk of chunkItems(f.items, 200, 512 * 1024)) {
|
|
1147
|
+
wsSend(r, { op: "sync", items: chunk, status: f.status });
|
|
905
1148
|
}
|
|
906
1149
|
return true;
|
|
907
1150
|
};
|
|
@@ -1314,6 +1557,7 @@ function createEngine(deps) {
|
|
|
1314
1557
|
queueConnect(st, r);
|
|
1315
1558
|
};
|
|
1316
1559
|
const frameFrom = (st) => {
|
|
1560
|
+
stampsPass();
|
|
1317
1561
|
const items = Array.from(st.queue.values());
|
|
1318
1562
|
st.queue.clear();
|
|
1319
1563
|
const statusChanged = st.status !== st.lastSentStatus;
|
|
@@ -1336,7 +1580,7 @@ function createEngine(deps) {
|
|
|
1336
1580
|
let httpWork;
|
|
1337
1581
|
for (const r of st.remotes.values()) {
|
|
1338
1582
|
r.pending.push(f);
|
|
1339
|
-
|
|
1583
|
+
spoolAppend(st, r, f);
|
|
1340
1584
|
if (r.ws && !r.wsReady)
|
|
1341
1585
|
continue;
|
|
1342
1586
|
if (r.wsReady) {
|
|
@@ -1363,18 +1607,20 @@ function createEngine(deps) {
|
|
|
1363
1607
|
status = f.status;
|
|
1364
1608
|
}
|
|
1365
1609
|
const cred = credOf(r.url);
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1610
|
+
for (const chunk of chunkItems(items, 500, 4 * 1024 * 1024)) {
|
|
1611
|
+
const res = await fetch(`${r.url}/api/share/${r.shareId}/sync`, {
|
|
1612
|
+
method: "POST",
|
|
1613
|
+
headers: { "content-type": "application/json", ...cred.credential ? { authorization: `Bearer ${cred.credential}` } : {} },
|
|
1614
|
+
body: JSON.stringify({ secret: r.secret ?? "", data: chunk, status }),
|
|
1615
|
+
signal: AbortSignal.timeout(15000)
|
|
1616
|
+
}).catch(() => null);
|
|
1617
|
+
if (!res || !res.ok)
|
|
1618
|
+
return;
|
|
1373
1619
|
const body = await res.json().catch(() => ({}));
|
|
1374
1620
|
r.ackedRev = body.rev ?? r.ackedRev;
|
|
1375
|
-
r.pending = [];
|
|
1376
|
-
spoolSave(st, r);
|
|
1377
1621
|
}
|
|
1622
|
+
r.pending = [];
|
|
1623
|
+
spoolSave(st, r);
|
|
1378
1624
|
};
|
|
1379
1625
|
const scheduleFlush = (st) => {
|
|
1380
1626
|
if (st.timer)
|
|
@@ -1392,12 +1638,284 @@ function createEngine(deps) {
|
|
|
1392
1638
|
if (!st || !entry)
|
|
1393
1639
|
return;
|
|
1394
1640
|
st.queue.set(entry.key, entry.item);
|
|
1641
|
+
indexMessage(entry);
|
|
1395
1642
|
if (debugEvents)
|
|
1396
1643
|
log(`enqueue sid=${st.sid.slice(0, 18)} ${entry.item.type} queue=${st.queue.size}`);
|
|
1397
1644
|
touch(st);
|
|
1398
1645
|
ensureConnected(st);
|
|
1399
1646
|
scheduleFlush(st);
|
|
1400
1647
|
};
|
|
1648
|
+
const msgIndex = new Map;
|
|
1649
|
+
const indexMessage = (entry) => {
|
|
1650
|
+
if (entry.item.type !== "message")
|
|
1651
|
+
return;
|
|
1652
|
+
const d = entry.item.data;
|
|
1653
|
+
if (!d || typeof d.id !== "string")
|
|
1654
|
+
return;
|
|
1655
|
+
const role = String(d.type ?? "");
|
|
1656
|
+
if (role !== "user" && role !== "assistant")
|
|
1657
|
+
return;
|
|
1658
|
+
const key = `${entry.item.sessionID}:${d.id}`;
|
|
1659
|
+
const prev = msgIndex.get(key);
|
|
1660
|
+
msgIndex.set(key, {
|
|
1661
|
+
sid: String(entry.item.sessionID ?? ""),
|
|
1662
|
+
role,
|
|
1663
|
+
agent: role === "assistant" ? typeof d.agent === "string" ? d.agent : prev?.agent : typeof d.metadata?.agent === "string" ? d.metadata.agent : prev?.agent,
|
|
1664
|
+
created: Number(d.time?.created ?? prev?.created ?? 0),
|
|
1665
|
+
data: d
|
|
1666
|
+
});
|
|
1667
|
+
};
|
|
1668
|
+
const sessionStateOf = (sid) => sessions.get(childToParent.get(sid) ?? sid);
|
|
1669
|
+
const reEnqueueMessage = (m) => {
|
|
1670
|
+
const st = sessionStateOf(m.sid);
|
|
1671
|
+
if (st)
|
|
1672
|
+
enqueue(st, itemFor("message", m.data, m.sid));
|
|
1673
|
+
};
|
|
1674
|
+
const segmentAgent = (ordered, at) => {
|
|
1675
|
+
const own = ordered[at]?.agent;
|
|
1676
|
+
if (own)
|
|
1677
|
+
return own;
|
|
1678
|
+
for (let i = at + 1;i < ordered.length; i++) {
|
|
1679
|
+
if (ordered[i].role === "assistant" && ordered[i].agent)
|
|
1680
|
+
return ordered[i].agent;
|
|
1681
|
+
if (ordered[i].role === "user")
|
|
1682
|
+
break;
|
|
1683
|
+
}
|
|
1684
|
+
return;
|
|
1685
|
+
};
|
|
1686
|
+
const stampAgentSwitches = () => {
|
|
1687
|
+
const bySession = new Map;
|
|
1688
|
+
for (const m of msgIndex.values()) {
|
|
1689
|
+
const list = bySession.get(m.sid);
|
|
1690
|
+
if (list)
|
|
1691
|
+
list.push(m);
|
|
1692
|
+
else
|
|
1693
|
+
bySession.set(m.sid, [m]);
|
|
1694
|
+
}
|
|
1695
|
+
for (const [, list] of bySession) {
|
|
1696
|
+
list.sort((a, b) => a.created - b.created);
|
|
1697
|
+
const users = list.map((m, i) => m.role === "user" ? i : -1).filter((i) => i >= 0);
|
|
1698
|
+
let prev;
|
|
1699
|
+
for (let k = 0;k < users.length; k++) {
|
|
1700
|
+
const agent = segmentAgent(list, users[k]);
|
|
1701
|
+
const d = list[users[k]].data;
|
|
1702
|
+
const meta = d.metadata ??= {};
|
|
1703
|
+
const want = k > 0 && agent && prev && agent !== prev ? agent : undefined;
|
|
1704
|
+
if (want !== meta.agentSwitch) {
|
|
1705
|
+
if (want)
|
|
1706
|
+
meta.agentSwitch = want;
|
|
1707
|
+
else
|
|
1708
|
+
delete meta.agentSwitch;
|
|
1709
|
+
reEnqueueMessage(list[users[k]]);
|
|
1710
|
+
}
|
|
1711
|
+
if (agent)
|
|
1712
|
+
prev = agent;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
const stampTaskAgents = () => {
|
|
1717
|
+
const bySession = new Map;
|
|
1718
|
+
for (const m of msgIndex.values()) {
|
|
1719
|
+
const list = bySession.get(m.sid);
|
|
1720
|
+
if (list)
|
|
1721
|
+
list.push(m);
|
|
1722
|
+
else
|
|
1723
|
+
bySession.set(m.sid, [m]);
|
|
1724
|
+
}
|
|
1725
|
+
for (const list of bySession.values())
|
|
1726
|
+
list.sort((a, b) => a.created - b.created);
|
|
1727
|
+
for (const m of msgIndex.values()) {
|
|
1728
|
+
if (m.role !== "assistant")
|
|
1729
|
+
continue;
|
|
1730
|
+
const content = m.data.content;
|
|
1731
|
+
if (!Array.isArray(content))
|
|
1732
|
+
continue;
|
|
1733
|
+
let mutated = false;
|
|
1734
|
+
for (const part of content) {
|
|
1735
|
+
if (!part || part.type !== "tool")
|
|
1736
|
+
continue;
|
|
1737
|
+
if (part.name !== "task" && part.name !== "subagent")
|
|
1738
|
+
continue;
|
|
1739
|
+
const state = part.state;
|
|
1740
|
+
const meta = state?.metadata;
|
|
1741
|
+
const childSid = typeof meta?.sessionID === "string" ? meta.sessionID : undefined;
|
|
1742
|
+
if (!childSid || typeof meta.agent === "string")
|
|
1743
|
+
continue;
|
|
1744
|
+
const partStart = Number(part.time?.created ?? 0);
|
|
1745
|
+
const child = bySession.get(childSid);
|
|
1746
|
+
if (!child)
|
|
1747
|
+
continue;
|
|
1748
|
+
let agent;
|
|
1749
|
+
for (const c of child) {
|
|
1750
|
+
if (c.created < partStart)
|
|
1751
|
+
continue;
|
|
1752
|
+
if (c.role === "user" && c.agent) {
|
|
1753
|
+
agent = c.agent;
|
|
1754
|
+
break;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
if (!agent) {
|
|
1758
|
+
for (const c of child) {
|
|
1759
|
+
if (c.created < partStart)
|
|
1760
|
+
continue;
|
|
1761
|
+
if (c.role === "assistant" && c.agent) {
|
|
1762
|
+
agent = c.agent;
|
|
1763
|
+
break;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (agent) {
|
|
1768
|
+
meta.agent = agent;
|
|
1769
|
+
mutated = true;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
if (mutated)
|
|
1773
|
+
reEnqueueMessage(m);
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
const LANG_BY_EXT = {
|
|
1777
|
+
ts: "typescript",
|
|
1778
|
+
tsx: "tsx",
|
|
1779
|
+
mts: "typescript",
|
|
1780
|
+
cts: "typescript",
|
|
1781
|
+
js: "javascript",
|
|
1782
|
+
jsx: "jsx",
|
|
1783
|
+
mjs: "javascript",
|
|
1784
|
+
cjs: "javascript",
|
|
1785
|
+
json: "json",
|
|
1786
|
+
jsonc: "json",
|
|
1787
|
+
md: "markdown",
|
|
1788
|
+
mdx: "mdx",
|
|
1789
|
+
py: "python",
|
|
1790
|
+
rs: "rust",
|
|
1791
|
+
go: "go",
|
|
1792
|
+
java: "java",
|
|
1793
|
+
rb: "ruby",
|
|
1794
|
+
css: "css",
|
|
1795
|
+
scss: "scss",
|
|
1796
|
+
html: "html",
|
|
1797
|
+
xml: "xml",
|
|
1798
|
+
yaml: "yaml",
|
|
1799
|
+
yml: "yaml",
|
|
1800
|
+
toml: "toml",
|
|
1801
|
+
sh: "shellscript",
|
|
1802
|
+
bash: "shellscript",
|
|
1803
|
+
ps1: "powershell",
|
|
1804
|
+
sql: "sql",
|
|
1805
|
+
c: "c",
|
|
1806
|
+
h: "c",
|
|
1807
|
+
cpp: "cpp",
|
|
1808
|
+
hpp: "cpp",
|
|
1809
|
+
cs: "csharp",
|
|
1810
|
+
swift: "swift",
|
|
1811
|
+
kt: "kotlin",
|
|
1812
|
+
php: "php",
|
|
1813
|
+
lua: "lua",
|
|
1814
|
+
vue: "vue",
|
|
1815
|
+
svelte: "svelte"
|
|
1816
|
+
};
|
|
1817
|
+
const FILE_TOOLS = new Set(["read", "write", "edit", "patch"]);
|
|
1818
|
+
const stampFileLanguages = () => {
|
|
1819
|
+
for (const m of msgIndex.values()) {
|
|
1820
|
+
if (m.role !== "assistant")
|
|
1821
|
+
continue;
|
|
1822
|
+
const content = m.data.content;
|
|
1823
|
+
if (!Array.isArray(content))
|
|
1824
|
+
continue;
|
|
1825
|
+
let mutated = false;
|
|
1826
|
+
for (const part of content) {
|
|
1827
|
+
if (!part || part.type !== "tool")
|
|
1828
|
+
continue;
|
|
1829
|
+
if (!FILE_TOOLS.has(part.name))
|
|
1830
|
+
continue;
|
|
1831
|
+
const state = part.state;
|
|
1832
|
+
const meta = state?.metadata;
|
|
1833
|
+
if (!meta)
|
|
1834
|
+
continue;
|
|
1835
|
+
const raw = state.input?.path ?? state.input?.filePath;
|
|
1836
|
+
if (typeof meta.language !== "string" && typeof raw === "string") {
|
|
1837
|
+
const ext = raw.split(".").pop()?.toLowerCase() ?? "";
|
|
1838
|
+
const lang = LANG_BY_EXT[ext];
|
|
1839
|
+
if (lang) {
|
|
1840
|
+
meta.language = lang;
|
|
1841
|
+
mutated = true;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
if (typeof meta.lineStart !== "number" && part.name === "read") {
|
|
1845
|
+
const off = state.input?.offset;
|
|
1846
|
+
if (typeof off === "number" && off >= 0 && off > 0) {
|
|
1847
|
+
meta.lineStart = off + 1;
|
|
1848
|
+
mutated = true;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
if (mutated)
|
|
1853
|
+
reEnqueueMessage(m);
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
const stampsPass = () => {
|
|
1857
|
+
stampAgentSwitches();
|
|
1858
|
+
stampTaskAgents();
|
|
1859
|
+
stampFileLanguages();
|
|
1860
|
+
};
|
|
1861
|
+
const mergeEntry = (st, messageID) => {
|
|
1862
|
+
let e = st.merged.get(messageID);
|
|
1863
|
+
if (!e) {
|
|
1864
|
+
e = { parts: new Map };
|
|
1865
|
+
st.merged.set(messageID, e);
|
|
1866
|
+
}
|
|
1867
|
+
return e;
|
|
1868
|
+
};
|
|
1869
|
+
const emitMerged = (st, sid, messageID) => {
|
|
1870
|
+
const e = st.merged.get(messageID);
|
|
1871
|
+
if (!e?.info)
|
|
1872
|
+
return;
|
|
1873
|
+
const res = normalizeV1Message(e.info, [...e.parts.values()]);
|
|
1874
|
+
if (!res)
|
|
1875
|
+
return;
|
|
1876
|
+
if ("refill" in res) {
|
|
1877
|
+
const parent = st.merged.get(res.refill);
|
|
1878
|
+
if (parent?.info) {
|
|
1879
|
+
const compaction = normalizeV1Message(parent.info, [...parent.parts.values()]);
|
|
1880
|
+
if (compaction && "message" in compaction) {
|
|
1881
|
+
const msg = compaction.message;
|
|
1882
|
+
msg.summary = res.summary;
|
|
1883
|
+
enqueue(st, itemFor("message", msg, sid));
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
enqueue(st, itemFor("message", res.message, sid));
|
|
1889
|
+
};
|
|
1890
|
+
const ingestMessageInfo = (st, sid, info) => {
|
|
1891
|
+
if (isV2NativeMessage(info)) {
|
|
1892
|
+
enqueue(st, itemFor("message", info, sid));
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
if (typeof info?.id !== "string")
|
|
1896
|
+
return;
|
|
1897
|
+
const e = mergeEntry(st, info.id);
|
|
1898
|
+
e.info = info;
|
|
1899
|
+
emitMerged(st, sid, info.id);
|
|
1900
|
+
};
|
|
1901
|
+
const ingestPart = (st, sid, part) => {
|
|
1902
|
+
if (typeof part?.id !== "string" || typeof part.messageID !== "string")
|
|
1903
|
+
return;
|
|
1904
|
+
const e = mergeEntry(st, part.messageID);
|
|
1905
|
+
e.parts.set(part.id, part);
|
|
1906
|
+
emitMerged(st, sid, part.messageID);
|
|
1907
|
+
};
|
|
1908
|
+
const dropMergedMessage = (st, messageID) => {
|
|
1909
|
+
st.merged.delete(messageID);
|
|
1910
|
+
};
|
|
1911
|
+
const dropMergedPart = (st, sid, messageID, partID) => {
|
|
1912
|
+
const e = st.merged.get(messageID);
|
|
1913
|
+
if (!e)
|
|
1914
|
+
return;
|
|
1915
|
+
e.parts.delete(partID);
|
|
1916
|
+
if (e.info)
|
|
1917
|
+
emitMerged(st, sid, messageID);
|
|
1918
|
+
};
|
|
1401
1919
|
const remove = (st) => {
|
|
1402
1920
|
st.closed = true;
|
|
1403
1921
|
st.activated = false;
|
|
@@ -1470,7 +1988,7 @@ function createEngine(deps) {
|
|
|
1470
1988
|
const PAGE_SIZE = 100;
|
|
1471
1989
|
const MAX_PAGES = 50;
|
|
1472
1990
|
const query = () => deps.directory ? { directory: deps.directory } : {};
|
|
1473
|
-
async function fetchMessages(client, sid) {
|
|
1991
|
+
async function fetchMessages(client, sid, onPage) {
|
|
1474
1992
|
if (typeof client.session.messages !== "function")
|
|
1475
1993
|
return [];
|
|
1476
1994
|
const out = [];
|
|
@@ -1486,11 +2004,21 @@ function createEngine(deps) {
|
|
|
1486
2004
|
if (!Array.isArray(result) || result.length === 0)
|
|
1487
2005
|
break;
|
|
1488
2006
|
out.push(...result);
|
|
2007
|
+
if (onPage) {
|
|
2008
|
+
try {
|
|
2009
|
+
onPage(result);
|
|
2010
|
+
} catch {}
|
|
2011
|
+
}
|
|
1489
2012
|
if (result.length < PAGE_SIZE)
|
|
1490
2013
|
break;
|
|
1491
2014
|
const last = result[result.length - 1];
|
|
1492
|
-
const
|
|
1493
|
-
|
|
2015
|
+
const lastInfo = last?.info ?? last;
|
|
2016
|
+
const lastID = typeof lastInfo?.id === "string" ? lastInfo.id : undefined;
|
|
2017
|
+
const lastTime = lastInfo?.time?.created;
|
|
2018
|
+
if (!lastID || typeof lastTime !== "number")
|
|
2019
|
+
break;
|
|
2020
|
+
const nextBefore = cursorFor(lastID, lastTime);
|
|
2021
|
+
if (nextBefore === before)
|
|
1494
2022
|
break;
|
|
1495
2023
|
before = nextBefore;
|
|
1496
2024
|
}
|
|
@@ -1500,27 +2028,31 @@ function createEngine(deps) {
|
|
|
1500
2028
|
const sessionInfoValue = info ?? await sessionInfo(client, sid);
|
|
1501
2029
|
if (sessionInfoValue)
|
|
1502
2030
|
enqueue(st, itemFor("session", redact(sessionInfoValue), sid));
|
|
1503
|
-
const messages = await fetchMessages(client, sid);
|
|
1504
2031
|
let modelSeen = false;
|
|
1505
|
-
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
enqueue(st, itemFor("message", m, sid));
|
|
1510
|
-
if (m.role === "user" && m.model) {
|
|
1511
|
-
modelSeen = true;
|
|
1512
|
-
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, m.model);
|
|
1513
|
-
enqueue(st, itemFor("model", [{ id: m.model.modelID, providerID: m.model.providerID, name: label }], sid));
|
|
1514
|
-
}
|
|
1515
|
-
for (const part of message.parts ?? []) {
|
|
1516
|
-
const p = part;
|
|
1517
|
-
if (!p || typeof p.id !== "string" || typeof p.messageID !== "string")
|
|
2032
|
+
const consume = (messages) => {
|
|
2033
|
+
for (const message of messages) {
|
|
2034
|
+
const m = message.info ?? message;
|
|
2035
|
+
if (!m || typeof m.id !== "string")
|
|
1518
2036
|
continue;
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
2037
|
+
const e = mergeEntry(st, m.id);
|
|
2038
|
+
e.info = m;
|
|
2039
|
+
for (const rawPart of message.parts ?? []) {
|
|
2040
|
+
const p = rawPart;
|
|
2041
|
+
if (!p || typeof p.id !== "string" || typeof p.messageID !== "string")
|
|
2042
|
+
continue;
|
|
2043
|
+
if (p.state?.status === "error")
|
|
2044
|
+
st.badSignal = true;
|
|
2045
|
+
e.parts.set(p.id, p);
|
|
2046
|
+
}
|
|
2047
|
+
emitMerged(st, sid, m.id);
|
|
2048
|
+
if (m.role === "user" && m.model) {
|
|
2049
|
+
modelSeen = true;
|
|
2050
|
+
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, m.model);
|
|
2051
|
+
enqueue(st, itemFor("model", [{ id: m.model.modelID, providerID: m.model.providerID, name: label }], sid));
|
|
2052
|
+
}
|
|
1522
2053
|
}
|
|
1523
|
-
}
|
|
2054
|
+
};
|
|
2055
|
+
const messages = await fetchMessages(client, sid, (page) => consume(page));
|
|
1524
2056
|
if (!modelSeen && sessionInfoValue?.model) {
|
|
1525
2057
|
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, sessionInfoValue.model);
|
|
1526
2058
|
enqueue(st, itemFor("model", [{ id: sessionInfoValue.model.modelID, providerID: sessionInfoValue.model.providerID, name: label }], sid));
|
|
@@ -1773,12 +2305,13 @@ function createEngine(deps) {
|
|
|
1773
2305
|
if (!messageID)
|
|
1774
2306
|
return;
|
|
1775
2307
|
if (t === "message.removed") {
|
|
1776
|
-
|
|
2308
|
+
dropMergedMessage(st, messageID);
|
|
2309
|
+
enqueue(st, { key: `message/${messageID}`, item: messageTombstone(st.sid, messageID) });
|
|
1777
2310
|
} else {
|
|
1778
2311
|
const partID = p.partID;
|
|
1779
2312
|
if (!partID)
|
|
1780
2313
|
return;
|
|
1781
|
-
|
|
2314
|
+
dropMergedPart(st, sid, messageID, partID);
|
|
1782
2315
|
}
|
|
1783
2316
|
} else if (t === "message.updated") {
|
|
1784
2317
|
const main = childToParent.get(sid) ?? sid;
|
|
@@ -1819,13 +2352,13 @@ function createEngine(deps) {
|
|
|
1819
2352
|
}
|
|
1820
2353
|
if (!st || !st.activated || st.closed)
|
|
1821
2354
|
return;
|
|
1822
|
-
|
|
2355
|
+
ingestMessageInfo(st, sid, p.info);
|
|
1823
2356
|
const info = p.info;
|
|
1824
2357
|
if (info?.role === "user" && info?.model) {
|
|
1825
2358
|
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, info.model);
|
|
1826
2359
|
enqueue(st, itemFor("model", [{ id: info.model.modelID, providerID: info.model.providerID, name: label }], sid));
|
|
1827
2360
|
}
|
|
1828
|
-
if (info?.
|
|
2361
|
+
if (info?.finish === "error") {
|
|
1829
2362
|
st.badSignal = true;
|
|
1830
2363
|
}
|
|
1831
2364
|
} else if (t === "message.part.updated") {
|
|
@@ -1835,12 +2368,12 @@ function createEngine(deps) {
|
|
|
1835
2368
|
const part = p.part;
|
|
1836
2369
|
if (part?.state?.status === "error")
|
|
1837
2370
|
st.badSignal = true;
|
|
1838
|
-
|
|
2371
|
+
ingestPart(st, sid, part);
|
|
1839
2372
|
} else if (t === "session.diff") {
|
|
1840
2373
|
const st = stFor(sid);
|
|
1841
2374
|
if (!st || !st.activated || st.closed)
|
|
1842
2375
|
return;
|
|
1843
|
-
enqueue(st, itemFor("session_diff", p.diff, sid));
|
|
2376
|
+
enqueue(st, itemFor("session_diff", normalizeV1Diff(p.diff), sid));
|
|
1844
2377
|
} else if (t === "session.deleted") {
|
|
1845
2378
|
const isChild = childToParent.has(sid);
|
|
1846
2379
|
if (isChild) {
|
|
@@ -1904,116 +2437,6 @@ function createEngine(deps) {
|
|
|
1904
2437
|
// src/v2-server.ts
|
|
1905
2438
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
1906
2439
|
import { join as join3 } from "node:path";
|
|
1907
|
-
function toolContentText(content) {
|
|
1908
|
-
if (typeof content === "string")
|
|
1909
|
-
return content;
|
|
1910
|
-
if (Array.isArray(content)) {
|
|
1911
|
-
return content.map((item) => {
|
|
1912
|
-
if (typeof item === "string")
|
|
1913
|
-
return item;
|
|
1914
|
-
if (item && typeof item === "object") {
|
|
1915
|
-
const record = item;
|
|
1916
|
-
if (typeof record.text === "string")
|
|
1917
|
-
return record.text;
|
|
1918
|
-
}
|
|
1919
|
-
return "";
|
|
1920
|
-
}).join("");
|
|
1921
|
-
}
|
|
1922
|
-
return "";
|
|
1923
|
-
}
|
|
1924
|
-
function convertToolState(state) {
|
|
1925
|
-
if (!state || typeof state !== "object")
|
|
1926
|
-
return { status: "pending" };
|
|
1927
|
-
const record = state;
|
|
1928
|
-
const status = String(record.status ?? "pending");
|
|
1929
|
-
const mapped = {};
|
|
1930
|
-
if (status === "streaming" || status === "running")
|
|
1931
|
-
mapped.status = "running";
|
|
1932
|
-
else if (status === "completed")
|
|
1933
|
-
mapped.status = "completed";
|
|
1934
|
-
else if (status === "failed" || status === "error")
|
|
1935
|
-
mapped.status = "error";
|
|
1936
|
-
else
|
|
1937
|
-
mapped.status = "pending";
|
|
1938
|
-
if (record.input !== undefined)
|
|
1939
|
-
mapped.input = record.input;
|
|
1940
|
-
if (record.output !== undefined)
|
|
1941
|
-
mapped.output = toolContentText(record.output);
|
|
1942
|
-
if (record.error !== undefined)
|
|
1943
|
-
mapped.error = record.error;
|
|
1944
|
-
if (record.time !== undefined)
|
|
1945
|
-
mapped.time = record.time;
|
|
1946
|
-
return mapped;
|
|
1947
|
-
}
|
|
1948
|
-
function convertV2Message(message, sessionID) {
|
|
1949
|
-
if (!message || typeof message.id !== "string")
|
|
1950
|
-
return;
|
|
1951
|
-
const base = {
|
|
1952
|
-
id: message.id,
|
|
1953
|
-
sessionID
|
|
1954
|
-
};
|
|
1955
|
-
if (message.type === "user") {
|
|
1956
|
-
return {
|
|
1957
|
-
info: {
|
|
1958
|
-
...base,
|
|
1959
|
-
role: "user",
|
|
1960
|
-
time: { created: message.time?.created ?? 0 },
|
|
1961
|
-
text: message.text ?? ""
|
|
1962
|
-
},
|
|
1963
|
-
parts: []
|
|
1964
|
-
};
|
|
1965
|
-
}
|
|
1966
|
-
if (message.type === "assistant") {
|
|
1967
|
-
const parts = [];
|
|
1968
|
-
const content = Array.isArray(message.content) ? message.content : [];
|
|
1969
|
-
content.forEach((block, index) => {
|
|
1970
|
-
const record = block;
|
|
1971
|
-
const kind = String(record.type ?? "");
|
|
1972
|
-
if (kind === "text") {
|
|
1973
|
-
parts.push({
|
|
1974
|
-
id: `${message.id}_t${index}`,
|
|
1975
|
-
messageID: message.id,
|
|
1976
|
-
sessionID,
|
|
1977
|
-
type: "text",
|
|
1978
|
-
text: String(record.text ?? ""),
|
|
1979
|
-
synthetic: false,
|
|
1980
|
-
time: { start: message.time?.created ?? 0, end: message.time?.completed ?? 0 }
|
|
1981
|
-
});
|
|
1982
|
-
} else if (kind === "reasoning") {
|
|
1983
|
-
parts.push({
|
|
1984
|
-
id: `${message.id}_r${index}`,
|
|
1985
|
-
messageID: message.id,
|
|
1986
|
-
sessionID,
|
|
1987
|
-
type: "reasoning",
|
|
1988
|
-
text: String(record.text ?? ""),
|
|
1989
|
-
synthetic: false,
|
|
1990
|
-
time: { start: message.time?.created ?? 0, end: message.time?.completed ?? 0 }
|
|
1991
|
-
});
|
|
1992
|
-
} else if (kind === "tool") {
|
|
1993
|
-
parts.push({
|
|
1994
|
-
id: String(record.id ?? `${message.id}_tool${index}`),
|
|
1995
|
-
messageID: message.id,
|
|
1996
|
-
sessionID,
|
|
1997
|
-
type: "tool",
|
|
1998
|
-
tool: String(record.name ?? "unknown"),
|
|
1999
|
-
state: convertToolState(record.state),
|
|
2000
|
-
synthetic: false
|
|
2001
|
-
});
|
|
2002
|
-
}
|
|
2003
|
-
});
|
|
2004
|
-
return {
|
|
2005
|
-
info: {
|
|
2006
|
-
...base,
|
|
2007
|
-
role: "assistant",
|
|
2008
|
-
agent: message.agent,
|
|
2009
|
-
model: message.model ? { providerID: message.model.providerID, modelID: message.model.modelID ?? message.model.id } : undefined,
|
|
2010
|
-
time: { created: message.time?.created ?? 0, completed: message.time?.completed ?? 0 }
|
|
2011
|
-
},
|
|
2012
|
-
parts
|
|
2013
|
-
};
|
|
2014
|
-
}
|
|
2015
|
-
return;
|
|
2016
|
-
}
|
|
2017
2440
|
function adaptSessionClient(session, message, directory) {
|
|
2018
2441
|
if (!session)
|
|
2019
2442
|
return;
|
|
@@ -2044,9 +2467,9 @@ function adaptSessionClient(session, message, directory) {
|
|
|
2044
2467
|
if (!Array.isArray(data))
|
|
2045
2468
|
break;
|
|
2046
2469
|
for (const raw of data) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2470
|
+
if (raw && typeof raw === "object" && typeof raw.id === "string") {
|
|
2471
|
+
out.push({ info: raw, parts: [] });
|
|
2472
|
+
}
|
|
2050
2473
|
}
|
|
2051
2474
|
const next = res?.cursor?.next ?? undefined;
|
|
2052
2475
|
if (!next || data.length === 0)
|