@mirrowel/opencode-openshare 0.1.0 → 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/README.md +36 -30
- package/ci/openshare.config.json +7 -2
- package/ci/openshare.js +625 -197
- package/dist/config.d.ts +5 -1
- package/dist/config.js +9 -2
- 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/share-flow.js +2 -2
- package/dist/sidebar.d.ts +3 -1
- package/dist/sidebar.js +3 -17
- package/dist/tui.js +73 -121
- package/dist/v2-server.d.ts +0 -27
- package/dist/v2-server.js +6 -121
- package/openshare.config.json +1 -1
- package/package.json +4 -2
- package/src/banner.txt +7 -4
- package/src/config.ts +8 -2
- package/src/contract.ts +320 -0
- package/src/core.ts +403 -56
- package/src/dialogs.tsx +34 -0
- package/src/share-flow.ts +2 -2
- package/src/sidebar.tsx +4 -21
- package/src/tui.tsx +1 -1
- package/src/v2-server.ts +7 -110
package/ci/openshare.js
CHANGED
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
// lands in the log and optionally in `urlFile`).
|
|
14
14
|
// - Interactive opt-in: install the package's TUI part (tui.json) too —
|
|
15
15
|
// the server detects its presence and switches to manual mode
|
|
16
|
-
// (`/share
|
|
16
|
+
// (`/share` + `/openshare` in the TUI).
|
|
17
17
|
//
|
|
18
18
|
// Config: env (OPENSHARE_*) > openshare.config.json (next to this file)
|
|
19
|
-
// > code defaults. Keys:
|
|
20
|
-
//
|
|
21
|
-
// shareMode ("all" | "manual"; default = TUI presence)
|
|
19
|
+
// > code defaults. Keys: servers (multi-server: url -> {token, code,
|
|
20
|
+
// label, active, adminToken}), workerUrl, createToken (legacy pair),
|
|
21
|
+
// shareMode ("all" | "manual"; default = TUI presence), debounceMs,
|
|
22
|
+
// completedDelayMs, modelLabel, ttlDays, openTasksInNewTab,
|
|
23
|
+
// authLogin, authPassword, spoolDir, urlFile ("none" = off),
|
|
24
|
+
// announceStderr.
|
|
22
25
|
//
|
|
23
26
|
// src/config.ts
|
|
24
27
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -117,8 +120,8 @@ function resolveServer(cfg) {
|
|
|
117
120
|
if (cfg.workerUrl)
|
|
118
121
|
servers[cfg.workerUrl] = { token: cfg.createToken || "", adminToken: cfg.createToken || "", active: true };
|
|
119
122
|
if (cfg.servers)
|
|
120
|
-
for (const [
|
|
121
|
-
servers[
|
|
123
|
+
for (const [url, entry] of Object.entries(cfg.servers))
|
|
124
|
+
servers[url] = entry;
|
|
122
125
|
const urls = Object.keys(servers);
|
|
123
126
|
if (!urls.length)
|
|
124
127
|
return;
|
|
@@ -245,7 +248,9 @@ function specPath(spec) {
|
|
|
245
248
|
function isOpenShareSpec(spec) {
|
|
246
249
|
if (typeof spec !== "string" || spec.length === 0)
|
|
247
250
|
return false;
|
|
248
|
-
if (spec === NPM_NAME
|
|
251
|
+
if (spec === NPM_NAME)
|
|
252
|
+
return true;
|
|
253
|
+
if (spec.startsWith(`${NPM_NAME}@`) || spec.startsWith(`${NPM_NAME}/`))
|
|
249
254
|
return true;
|
|
250
255
|
const path = specPath(spec);
|
|
251
256
|
if (path) {
|
|
@@ -341,6 +346,215 @@ function resolveShareMode(cfg, input) {
|
|
|
341
346
|
// src/core.ts
|
|
342
347
|
import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
343
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
|
|
344
558
|
var DEFAULT_POLICY = {
|
|
345
559
|
mode: "persistent",
|
|
346
560
|
turnGraceMs: 120000,
|
|
@@ -376,11 +590,9 @@ function itemFor(type, data, sessionID) {
|
|
|
376
590
|
if (type === "prefs")
|
|
377
591
|
return { key: "prefs", item: { type, data } };
|
|
378
592
|
if (type === "session")
|
|
379
|
-
return { key: `session/${data?.id ?? ""}`, item: { type, data } };
|
|
593
|
+
return { key: `session/${data?.id ?? ""}`, item: { type, sessionID, data } };
|
|
380
594
|
if (type === "message")
|
|
381
|
-
return { key: `message/${data?.id ?? ""}`, item: { type, data } };
|
|
382
|
-
if (type === "part")
|
|
383
|
-
return { key: `part/${data?.messageID ?? ""}/${data?.id ?? ""}`, item: { type, data } };
|
|
595
|
+
return { key: `message/${data?.id ?? ""}`, item: { type, sessionID, data } };
|
|
384
596
|
if (type === "session_diff")
|
|
385
597
|
return { key: `session_diff/${sessionID}`, item: { type, sessionID, data } };
|
|
386
598
|
return;
|
|
@@ -568,19 +780,19 @@ function createEngine(deps) {
|
|
|
568
780
|
if (!raw || typeof raw !== "object")
|
|
569
781
|
return;
|
|
570
782
|
const p = raw;
|
|
571
|
-
const
|
|
783
|
+
const num = (v, d) => {
|
|
572
784
|
const n = Number(v);
|
|
573
785
|
return Number.isFinite(n) && n >= 0 ? n : d;
|
|
574
786
|
};
|
|
575
787
|
const prev = policyOf(url);
|
|
576
788
|
policyMap.set(url, {
|
|
577
789
|
mode: p.mode === "turn" ? "turn" : "persistent",
|
|
578
|
-
turnGraceMs:
|
|
579
|
-
idleDropMs:
|
|
580
|
-
finalizeAfterMs:
|
|
581
|
-
debounceMinMs:
|
|
582
|
-
debounceMaxMs: Math.max(
|
|
583
|
-
completedDelayMaxMs:
|
|
790
|
+
turnGraceMs: num(p.turnGraceMs, prev.turnGraceMs),
|
|
791
|
+
idleDropMs: num(p.idleDropMs, prev.idleDropMs),
|
|
792
|
+
finalizeAfterMs: num(p.finalizeAfterMs, prev.finalizeAfterMs),
|
|
793
|
+
debounceMinMs: num(p.debounceMinMs, 0),
|
|
794
|
+
debounceMaxMs: Math.max(num(p.debounceMaxMs, Infinity), num(p.debounceMinMs, 0)),
|
|
795
|
+
completedDelayMaxMs: num(p.completedDelayMaxMs, Infinity)
|
|
584
796
|
});
|
|
585
797
|
clampToPolicy();
|
|
586
798
|
if (debugEvents)
|
|
@@ -632,7 +844,8 @@ function createEngine(deps) {
|
|
|
632
844
|
opts: undefined,
|
|
633
845
|
activated: false,
|
|
634
846
|
wroteBinding: false,
|
|
635
|
-
everConnected: false
|
|
847
|
+
everConnected: false,
|
|
848
|
+
merged: new Map
|
|
636
849
|
});
|
|
637
850
|
const refreshConfig = () => {
|
|
638
851
|
const next = loadConfig(deps.pluginDir, deps.options);
|
|
@@ -844,6 +1057,14 @@ function createEngine(deps) {
|
|
|
844
1057
|
logError("spool save failed", e?.message || e);
|
|
845
1058
|
}
|
|
846
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
|
+
};
|
|
847
1068
|
const spoolLoad = (st, r) => {
|
|
848
1069
|
try {
|
|
849
1070
|
const meta = JSON.parse(readFileSync2(metaPath(st, r.url), "utf8"));
|
|
@@ -853,8 +1074,18 @@ function createEngine(deps) {
|
|
|
853
1074
|
r.ackedRev = meta.ackedRev ?? 0;
|
|
854
1075
|
}
|
|
855
1076
|
if (existsSync2(queuePath(st, r.url))) {
|
|
856
|
-
r.pending =
|
|
857
|
-
|
|
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
|
+
}
|
|
858
1089
|
}
|
|
859
1090
|
} catch {
|
|
860
1091
|
r.pending = [];
|
|
@@ -891,12 +1122,29 @@ function createEngine(deps) {
|
|
|
891
1122
|
}
|
|
892
1123
|
return true;
|
|
893
1124
|
};
|
|
894
|
-
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
|
+
};
|
|
895
1143
|
const sendSync = (r, f) => {
|
|
896
1144
|
if (!f.items.length)
|
|
897
1145
|
return wsSend(r, { op: "sync", items: [], status: f.status });
|
|
898
|
-
for (
|
|
899
|
-
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 });
|
|
900
1148
|
}
|
|
901
1149
|
return true;
|
|
902
1150
|
};
|
|
@@ -1309,6 +1557,7 @@ function createEngine(deps) {
|
|
|
1309
1557
|
queueConnect(st, r);
|
|
1310
1558
|
};
|
|
1311
1559
|
const frameFrom = (st) => {
|
|
1560
|
+
stampsPass();
|
|
1312
1561
|
const items = Array.from(st.queue.values());
|
|
1313
1562
|
st.queue.clear();
|
|
1314
1563
|
const statusChanged = st.status !== st.lastSentStatus;
|
|
@@ -1331,7 +1580,7 @@ function createEngine(deps) {
|
|
|
1331
1580
|
let httpWork;
|
|
1332
1581
|
for (const r of st.remotes.values()) {
|
|
1333
1582
|
r.pending.push(f);
|
|
1334
|
-
|
|
1583
|
+
spoolAppend(st, r, f);
|
|
1335
1584
|
if (r.ws && !r.wsReady)
|
|
1336
1585
|
continue;
|
|
1337
1586
|
if (r.wsReady) {
|
|
@@ -1358,18 +1607,20 @@ function createEngine(deps) {
|
|
|
1358
1607
|
status = f.status;
|
|
1359
1608
|
}
|
|
1360
1609
|
const cred = credOf(r.url);
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
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;
|
|
1368
1619
|
const body = await res.json().catch(() => ({}));
|
|
1369
1620
|
r.ackedRev = body.rev ?? r.ackedRev;
|
|
1370
|
-
r.pending = [];
|
|
1371
|
-
spoolSave(st, r);
|
|
1372
1621
|
}
|
|
1622
|
+
r.pending = [];
|
|
1623
|
+
spoolSave(st, r);
|
|
1373
1624
|
};
|
|
1374
1625
|
const scheduleFlush = (st) => {
|
|
1375
1626
|
if (st.timer)
|
|
@@ -1387,12 +1638,284 @@ function createEngine(deps) {
|
|
|
1387
1638
|
if (!st || !entry)
|
|
1388
1639
|
return;
|
|
1389
1640
|
st.queue.set(entry.key, entry.item);
|
|
1641
|
+
indexMessage(entry);
|
|
1390
1642
|
if (debugEvents)
|
|
1391
1643
|
log(`enqueue sid=${st.sid.slice(0, 18)} ${entry.item.type} queue=${st.queue.size}`);
|
|
1392
1644
|
touch(st);
|
|
1393
1645
|
ensureConnected(st);
|
|
1394
1646
|
scheduleFlush(st);
|
|
1395
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
|
+
};
|
|
1396
1919
|
const remove = (st) => {
|
|
1397
1920
|
st.closed = true;
|
|
1398
1921
|
st.activated = false;
|
|
@@ -1465,7 +1988,7 @@ function createEngine(deps) {
|
|
|
1465
1988
|
const PAGE_SIZE = 100;
|
|
1466
1989
|
const MAX_PAGES = 50;
|
|
1467
1990
|
const query = () => deps.directory ? { directory: deps.directory } : {};
|
|
1468
|
-
async function fetchMessages(client, sid) {
|
|
1991
|
+
async function fetchMessages(client, sid, onPage) {
|
|
1469
1992
|
if (typeof client.session.messages !== "function")
|
|
1470
1993
|
return [];
|
|
1471
1994
|
const out = [];
|
|
@@ -1481,11 +2004,21 @@ function createEngine(deps) {
|
|
|
1481
2004
|
if (!Array.isArray(result) || result.length === 0)
|
|
1482
2005
|
break;
|
|
1483
2006
|
out.push(...result);
|
|
2007
|
+
if (onPage) {
|
|
2008
|
+
try {
|
|
2009
|
+
onPage(result);
|
|
2010
|
+
} catch {}
|
|
2011
|
+
}
|
|
1484
2012
|
if (result.length < PAGE_SIZE)
|
|
1485
2013
|
break;
|
|
1486
2014
|
const last = result[result.length - 1];
|
|
1487
|
-
const
|
|
1488
|
-
|
|
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)
|
|
1489
2022
|
break;
|
|
1490
2023
|
before = nextBefore;
|
|
1491
2024
|
}
|
|
@@ -1495,27 +2028,31 @@ function createEngine(deps) {
|
|
|
1495
2028
|
const sessionInfoValue = info ?? await sessionInfo(client, sid);
|
|
1496
2029
|
if (sessionInfoValue)
|
|
1497
2030
|
enqueue(st, itemFor("session", redact(sessionInfoValue), sid));
|
|
1498
|
-
const messages = await fetchMessages(client, sid);
|
|
1499
2031
|
let modelSeen = false;
|
|
1500
|
-
|
|
1501
|
-
const
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
enqueue(st, itemFor("message", m, sid));
|
|
1505
|
-
if (m.role === "user" && m.model) {
|
|
1506
|
-
modelSeen = true;
|
|
1507
|
-
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, m.model);
|
|
1508
|
-
enqueue(st, itemFor("model", [{ id: m.model.modelID, providerID: m.model.providerID, name: label }], sid));
|
|
1509
|
-
}
|
|
1510
|
-
for (const part of message.parts ?? []) {
|
|
1511
|
-
const p = part;
|
|
1512
|
-
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")
|
|
1513
2036
|
continue;
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
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
|
+
}
|
|
1517
2053
|
}
|
|
1518
|
-
}
|
|
2054
|
+
};
|
|
2055
|
+
const messages = await fetchMessages(client, sid, (page) => consume(page));
|
|
1519
2056
|
if (!modelSeen && sessionInfoValue?.model) {
|
|
1520
2057
|
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, sessionInfoValue.model);
|
|
1521
2058
|
enqueue(st, itemFor("model", [{ id: sessionInfoValue.model.modelID, providerID: sessionInfoValue.model.providerID, name: label }], sid));
|
|
@@ -1605,31 +2142,31 @@ function createEngine(deps) {
|
|
|
1605
2142
|
const parent = trackChild(sid, info);
|
|
1606
2143
|
if (mode === "manual") {
|
|
1607
2144
|
if (parent) {
|
|
1608
|
-
const
|
|
1609
|
-
if (
|
|
1610
|
-
if (!
|
|
1611
|
-
|
|
1612
|
-
enqueue(
|
|
2145
|
+
const st = sessions.get(parent);
|
|
2146
|
+
if (st?.activated && !st.closed) {
|
|
2147
|
+
if (!st.prefsSent) {
|
|
2148
|
+
st.prefsSent = true;
|
|
2149
|
+
enqueue(st, itemFor("prefs", { openTasksInNewTab: cfg.openTasksInNewTab !== false }, sid));
|
|
1613
2150
|
}
|
|
1614
2151
|
if (info)
|
|
1615
|
-
enqueue(
|
|
2152
|
+
enqueue(st, itemFor("session", redact(info), sid));
|
|
1616
2153
|
}
|
|
1617
2154
|
return;
|
|
1618
2155
|
}
|
|
1619
2156
|
const flag = shareFlag(info);
|
|
1620
|
-
const
|
|
2157
|
+
const st = sessions.get(sid);
|
|
1621
2158
|
if (flag.enabled) {
|
|
1622
|
-
if (
|
|
2159
|
+
if (st?.activated && !st.closed) {
|
|
1623
2160
|
if (flag.opts)
|
|
1624
|
-
|
|
1625
|
-
if (!
|
|
1626
|
-
|
|
1627
|
-
enqueue(
|
|
2161
|
+
st.opts = flag.opts;
|
|
2162
|
+
if (!st.prefsSent) {
|
|
2163
|
+
st.prefsSent = true;
|
|
2164
|
+
enqueue(st, itemFor("prefs", { openTasksInNewTab: cfg.openTasksInNewTab !== false }, sid));
|
|
1628
2165
|
}
|
|
1629
2166
|
if (info)
|
|
1630
|
-
enqueue(
|
|
2167
|
+
enqueue(st, itemFor("session", redact(info), sid));
|
|
1631
2168
|
if (t === "session.updated")
|
|
1632
|
-
chain = chain.then(() => refreshOptOuts(
|
|
2169
|
+
chain = chain.then(() => refreshOptOuts(st)).catch(() => {});
|
|
1633
2170
|
return;
|
|
1634
2171
|
}
|
|
1635
2172
|
activate(sid, flag.opts, info);
|
|
@@ -1640,9 +2177,9 @@ function createEngine(deps) {
|
|
|
1640
2177
|
}
|
|
1641
2178
|
return;
|
|
1642
2179
|
}
|
|
1643
|
-
if (
|
|
2180
|
+
if (st?.activated && !st.closed && t === "session.updated") {
|
|
1644
2181
|
log(`share disabled for ${sid} - removing`);
|
|
1645
|
-
chain = chain.then(() => remove(
|
|
2182
|
+
chain = chain.then(() => remove(st)).catch((e) => logError("remove error", e?.message || e));
|
|
1646
2183
|
return;
|
|
1647
2184
|
}
|
|
1648
2185
|
return;
|
|
@@ -1768,12 +2305,13 @@ function createEngine(deps) {
|
|
|
1768
2305
|
if (!messageID)
|
|
1769
2306
|
return;
|
|
1770
2307
|
if (t === "message.removed") {
|
|
1771
|
-
|
|
2308
|
+
dropMergedMessage(st, messageID);
|
|
2309
|
+
enqueue(st, { key: `message/${messageID}`, item: messageTombstone(st.sid, messageID) });
|
|
1772
2310
|
} else {
|
|
1773
2311
|
const partID = p.partID;
|
|
1774
2312
|
if (!partID)
|
|
1775
2313
|
return;
|
|
1776
|
-
|
|
2314
|
+
dropMergedPart(st, sid, messageID, partID);
|
|
1777
2315
|
}
|
|
1778
2316
|
} else if (t === "message.updated") {
|
|
1779
2317
|
const main = childToParent.get(sid) ?? sid;
|
|
@@ -1788,16 +2326,16 @@ function createEngine(deps) {
|
|
|
1788
2326
|
chain = chain.then(async () => {
|
|
1789
2327
|
if (created.closed)
|
|
1790
2328
|
return;
|
|
1791
|
-
const
|
|
2329
|
+
const info = await sessionInfo(created.client, main);
|
|
1792
2330
|
verifying.delete(main);
|
|
1793
2331
|
if (created.closed)
|
|
1794
2332
|
return;
|
|
1795
|
-
const flag = shareFlag(
|
|
1796
|
-
const hasBinding = !!
|
|
1797
|
-
const createdMs =
|
|
2333
|
+
const flag = shareFlag(info);
|
|
2334
|
+
const hasBinding = !!info?.metadata?.[METADATA_KEY];
|
|
2335
|
+
const createdMs = info?.time?.created;
|
|
1798
2336
|
const isPreExisting = typeof createdMs === "number" && createdMs < bootTime;
|
|
1799
2337
|
if (debugEvents) {
|
|
1800
|
-
log(`verify ${main}: created=${createdMs} boot=${bootTime} pre=${isPreExisting} flag=${flag.enabled} binding=${hasBinding} infoMissing=${!
|
|
2338
|
+
log(`verify ${main}: created=${createdMs} boot=${bootTime} pre=${isPreExisting} flag=${flag.enabled} binding=${hasBinding} infoMissing=${!info}`);
|
|
1801
2339
|
}
|
|
1802
2340
|
if (isPreExisting && !flag.enabled && !hasBinding) {
|
|
1803
2341
|
preExistingRetired.add(main);
|
|
@@ -1814,13 +2352,13 @@ function createEngine(deps) {
|
|
|
1814
2352
|
}
|
|
1815
2353
|
if (!st || !st.activated || st.closed)
|
|
1816
2354
|
return;
|
|
1817
|
-
|
|
2355
|
+
ingestMessageInfo(st, sid, p.info);
|
|
1818
2356
|
const info = p.info;
|
|
1819
2357
|
if (info?.role === "user" && info?.model) {
|
|
1820
2358
|
const label = formatLabel(st.opts?.modelLabel ?? cfg.modelLabel, info.model);
|
|
1821
2359
|
enqueue(st, itemFor("model", [{ id: info.model.modelID, providerID: info.model.providerID, name: label }], sid));
|
|
1822
2360
|
}
|
|
1823
|
-
if (info?.
|
|
2361
|
+
if (info?.finish === "error") {
|
|
1824
2362
|
st.badSignal = true;
|
|
1825
2363
|
}
|
|
1826
2364
|
} else if (t === "message.part.updated") {
|
|
@@ -1830,12 +2368,12 @@ function createEngine(deps) {
|
|
|
1830
2368
|
const part = p.part;
|
|
1831
2369
|
if (part?.state?.status === "error")
|
|
1832
2370
|
st.badSignal = true;
|
|
1833
|
-
|
|
2371
|
+
ingestPart(st, sid, part);
|
|
1834
2372
|
} else if (t === "session.diff") {
|
|
1835
2373
|
const st = stFor(sid);
|
|
1836
2374
|
if (!st || !st.activated || st.closed)
|
|
1837
2375
|
return;
|
|
1838
|
-
enqueue(st, itemFor("session_diff", p.diff, sid));
|
|
2376
|
+
enqueue(st, itemFor("session_diff", normalizeV1Diff(p.diff), sid));
|
|
1839
2377
|
} else if (t === "session.deleted") {
|
|
1840
2378
|
const isChild = childToParent.has(sid);
|
|
1841
2379
|
if (isChild) {
|
|
@@ -1899,116 +2437,6 @@ function createEngine(deps) {
|
|
|
1899
2437
|
// src/v2-server.ts
|
|
1900
2438
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
1901
2439
|
import { join as join3 } from "node:path";
|
|
1902
|
-
function toolContentText(content) {
|
|
1903
|
-
if (typeof content === "string")
|
|
1904
|
-
return content;
|
|
1905
|
-
if (Array.isArray(content)) {
|
|
1906
|
-
return content.map((item) => {
|
|
1907
|
-
if (typeof item === "string")
|
|
1908
|
-
return item;
|
|
1909
|
-
if (item && typeof item === "object") {
|
|
1910
|
-
const record = item;
|
|
1911
|
-
if (typeof record.text === "string")
|
|
1912
|
-
return record.text;
|
|
1913
|
-
}
|
|
1914
|
-
return "";
|
|
1915
|
-
}).join("");
|
|
1916
|
-
}
|
|
1917
|
-
return "";
|
|
1918
|
-
}
|
|
1919
|
-
function convertToolState(state) {
|
|
1920
|
-
if (!state || typeof state !== "object")
|
|
1921
|
-
return { status: "pending" };
|
|
1922
|
-
const record = state;
|
|
1923
|
-
const status = String(record.status ?? "pending");
|
|
1924
|
-
const mapped = {};
|
|
1925
|
-
if (status === "streaming" || status === "running")
|
|
1926
|
-
mapped.status = "running";
|
|
1927
|
-
else if (status === "completed")
|
|
1928
|
-
mapped.status = "completed";
|
|
1929
|
-
else if (status === "failed" || status === "error")
|
|
1930
|
-
mapped.status = "error";
|
|
1931
|
-
else
|
|
1932
|
-
mapped.status = "pending";
|
|
1933
|
-
if (record.input !== undefined)
|
|
1934
|
-
mapped.input = record.input;
|
|
1935
|
-
if (record.output !== undefined)
|
|
1936
|
-
mapped.output = toolContentText(record.output);
|
|
1937
|
-
if (record.error !== undefined)
|
|
1938
|
-
mapped.error = record.error;
|
|
1939
|
-
if (record.time !== undefined)
|
|
1940
|
-
mapped.time = record.time;
|
|
1941
|
-
return mapped;
|
|
1942
|
-
}
|
|
1943
|
-
function convertV2Message(message, sessionID) {
|
|
1944
|
-
if (!message || typeof message.id !== "string")
|
|
1945
|
-
return;
|
|
1946
|
-
const base = {
|
|
1947
|
-
id: message.id,
|
|
1948
|
-
sessionID
|
|
1949
|
-
};
|
|
1950
|
-
if (message.type === "user") {
|
|
1951
|
-
return {
|
|
1952
|
-
info: {
|
|
1953
|
-
...base,
|
|
1954
|
-
role: "user",
|
|
1955
|
-
time: { created: message.time?.created ?? 0 },
|
|
1956
|
-
text: message.text ?? ""
|
|
1957
|
-
},
|
|
1958
|
-
parts: []
|
|
1959
|
-
};
|
|
1960
|
-
}
|
|
1961
|
-
if (message.type === "assistant") {
|
|
1962
|
-
const parts = [];
|
|
1963
|
-
const content = Array.isArray(message.content) ? message.content : [];
|
|
1964
|
-
content.forEach((block, index) => {
|
|
1965
|
-
const record = block;
|
|
1966
|
-
const kind = String(record.type ?? "");
|
|
1967
|
-
if (kind === "text") {
|
|
1968
|
-
parts.push({
|
|
1969
|
-
id: `${message.id}_t${index}`,
|
|
1970
|
-
messageID: message.id,
|
|
1971
|
-
sessionID,
|
|
1972
|
-
type: "text",
|
|
1973
|
-
text: String(record.text ?? ""),
|
|
1974
|
-
synthetic: false,
|
|
1975
|
-
time: { start: message.time?.created ?? 0, end: message.time?.completed ?? 0 }
|
|
1976
|
-
});
|
|
1977
|
-
} else if (kind === "reasoning") {
|
|
1978
|
-
parts.push({
|
|
1979
|
-
id: `${message.id}_r${index}`,
|
|
1980
|
-
messageID: message.id,
|
|
1981
|
-
sessionID,
|
|
1982
|
-
type: "reasoning",
|
|
1983
|
-
text: String(record.text ?? ""),
|
|
1984
|
-
synthetic: false,
|
|
1985
|
-
time: { start: message.time?.created ?? 0, end: message.time?.completed ?? 0 }
|
|
1986
|
-
});
|
|
1987
|
-
} else if (kind === "tool") {
|
|
1988
|
-
parts.push({
|
|
1989
|
-
id: String(record.id ?? `${message.id}_tool${index}`),
|
|
1990
|
-
messageID: message.id,
|
|
1991
|
-
sessionID,
|
|
1992
|
-
type: "tool",
|
|
1993
|
-
tool: String(record.name ?? "unknown"),
|
|
1994
|
-
state: convertToolState(record.state),
|
|
1995
|
-
synthetic: false
|
|
1996
|
-
});
|
|
1997
|
-
}
|
|
1998
|
-
});
|
|
1999
|
-
return {
|
|
2000
|
-
info: {
|
|
2001
|
-
...base,
|
|
2002
|
-
role: "assistant",
|
|
2003
|
-
agent: message.agent,
|
|
2004
|
-
model: message.model ? { providerID: message.model.providerID, modelID: message.model.modelID ?? message.model.id } : undefined,
|
|
2005
|
-
time: { created: message.time?.created ?? 0, completed: message.time?.completed ?? 0 }
|
|
2006
|
-
},
|
|
2007
|
-
parts
|
|
2008
|
-
};
|
|
2009
|
-
}
|
|
2010
|
-
return;
|
|
2011
|
-
}
|
|
2012
2440
|
function adaptSessionClient(session, message, directory) {
|
|
2013
2441
|
if (!session)
|
|
2014
2442
|
return;
|
|
@@ -2039,9 +2467,9 @@ function adaptSessionClient(session, message, directory) {
|
|
|
2039
2467
|
if (!Array.isArray(data))
|
|
2040
2468
|
break;
|
|
2041
2469
|
for (const raw of data) {
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2470
|
+
if (raw && typeof raw === "object" && typeof raw.id === "string") {
|
|
2471
|
+
out.push({ info: raw, parts: [] });
|
|
2472
|
+
}
|
|
2045
2473
|
}
|
|
2046
2474
|
const next = res?.cursor?.next ?? undefined;
|
|
2047
2475
|
if (!next || data.length === 0)
|
|
@@ -2259,7 +2687,7 @@ var server_default = {
|
|
|
2259
2687
|
setup: createV2ServerSetup()
|
|
2260
2688
|
};
|
|
2261
2689
|
export {
|
|
2262
|
-
|
|
2690
|
+
OpenSharePlugin,
|
|
2263
2691
|
PLUGIN_ID,
|
|
2264
|
-
|
|
2692
|
+
server_default as default
|
|
2265
2693
|
};
|