@varo-ui/ai 1.2.0 → 2.1.0
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 +16 -1
- package/dist/index.d.mts +44 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +340 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/agent-stream.ts +17 -2
- package/src/event-channel.ts +35 -12
- package/src/index.ts +1 -0
- package/src/markdown-view.ts +126 -45
- package/src/sse.ts +34 -9
- package/src/text-stream.ts +126 -39
- package/src/thread.ts +160 -0
- package/src/types.ts +48 -17
package/dist/index.mjs
CHANGED
|
@@ -10,13 +10,51 @@ function splitGraphemes(value) {
|
|
|
10
10
|
function clamp(value, min, max) {
|
|
11
11
|
return Math.min(max, Math.max(min, value));
|
|
12
12
|
}
|
|
13
|
+
function hasUnclosedFence(value) {
|
|
14
|
+
let marker;
|
|
15
|
+
let markerLength = 0;
|
|
16
|
+
for (const line of value.split(/\r?\n/)) {
|
|
17
|
+
const match = /^ {0,3}(`{3,}|~{3,})/.exec(line);
|
|
18
|
+
if (!match) continue;
|
|
19
|
+
const nextMarker = match[1][0];
|
|
20
|
+
const nextLength = match[1].length;
|
|
21
|
+
if (!marker) {
|
|
22
|
+
marker = nextMarker;
|
|
23
|
+
markerLength = nextLength;
|
|
24
|
+
} else if (marker === nextMarker && nextLength >= markerLength) {
|
|
25
|
+
marker = void 0;
|
|
26
|
+
markerLength = 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return marker !== void 0;
|
|
30
|
+
}
|
|
31
|
+
function fenceSafeCommitCount(visible, source, segments) {
|
|
32
|
+
if (segments.length === 0) return 0;
|
|
33
|
+
const candidate = visible + segments.join("");
|
|
34
|
+
const lineStart = candidate.lastIndexOf("\n") + 1;
|
|
35
|
+
const candidateLine = candidate.slice(lineStart);
|
|
36
|
+
const sourceLineEnd = source.indexOf("\n", lineStart);
|
|
37
|
+
const sourceLine = source.slice(lineStart, sourceLineEnd < 0 ? source.length : sourceLineEnd);
|
|
38
|
+
const match = /^ {0,3}(`+|~+)/.exec(sourceLine);
|
|
39
|
+
if (!match) return segments.length;
|
|
40
|
+
const marker = match[1];
|
|
41
|
+
const unresolvedCandidate = marker.length < 3 && sourceLine.trim() === marker;
|
|
42
|
+
const completeFence = marker.length >= 3;
|
|
43
|
+
if ((unresolvedCandidate || completeFence) && /^ {0,3}(?:`+|~+)/.test(candidateLine)) return 0;
|
|
44
|
+
return segments.length;
|
|
45
|
+
}
|
|
13
46
|
function createTimerTextStream(options = {}) {
|
|
14
47
|
const minCharsPerSecond = Math.max(1, options.minCharsPerSecond ?? 40);
|
|
15
|
-
const maxCharsPerSecond = Math.max(minCharsPerSecond, options.maxCharsPerSecond ??
|
|
16
|
-
const targetLatencyMs = Math.max(
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
48
|
+
const maxCharsPerSecond = Math.max(minCharsPerSecond, options.maxCharsPerSecond ?? 1e3);
|
|
49
|
+
const targetLatencyMs = Math.max(1, options.targetLatencyMs ?? 900);
|
|
50
|
+
const catchUpLatencyMs = Math.max(1, options.catchUpLatencyMs ?? 350);
|
|
51
|
+
const maxCommitFps = Math.max(1, Math.min(30, options.maxCommitFps ?? 30));
|
|
52
|
+
const maxCharsPerCommit = Math.max(1, options.maxCharsPerCommit ?? 80);
|
|
53
|
+
const catchUpThreshold = Math.max(0, options.catchUpThreshold ?? 600);
|
|
54
|
+
const startDelayMs = Math.max(0, options.startDelayMs ?? 80);
|
|
55
|
+
const flushOnFinish = options.flushOnFinish ?? false;
|
|
56
|
+
const burstInitialContent = options.burstInitialContent ?? false;
|
|
57
|
+
const burstRevealThresholdChars = Math.max(1, options.burstRevealThresholdChars ?? 2048);
|
|
20
58
|
const listeners = /* @__PURE__ */ new Set();
|
|
21
59
|
const pending = [];
|
|
22
60
|
let pendingOffset = 0;
|
|
@@ -27,16 +65,17 @@ function createTimerTextStream(options = {}) {
|
|
|
27
65
|
let destroyed = false;
|
|
28
66
|
let timer;
|
|
29
67
|
let lastTick = Date.now();
|
|
68
|
+
let firstPendingAt = 0;
|
|
30
69
|
let budget = 0;
|
|
31
70
|
function pendingCount() {
|
|
32
71
|
return pending.length - pendingOffset;
|
|
33
72
|
}
|
|
34
73
|
function snapshot() {
|
|
35
|
-
const pendingChars =
|
|
74
|
+
const pendingChars = Math.max(0, source.length - visible.length);
|
|
36
75
|
return {
|
|
37
|
-
caughtUp:
|
|
76
|
+
caughtUp: pendingCount() === 0,
|
|
38
77
|
done,
|
|
39
|
-
final: done &&
|
|
78
|
+
final: done && pendingCount() === 0,
|
|
40
79
|
paused,
|
|
41
80
|
pendingChars,
|
|
42
81
|
source,
|
|
@@ -59,6 +98,11 @@ function createTimerTextStream(options = {}) {
|
|
|
59
98
|
timer = void 0;
|
|
60
99
|
if (destroyed || paused) return;
|
|
61
100
|
const now = Date.now();
|
|
101
|
+
if (firstPendingAt && now - firstPendingAt < startDelayMs) {
|
|
102
|
+
schedule();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
firstPendingAt = 0;
|
|
62
106
|
const deltaMs = Math.max(1, now - lastTick);
|
|
63
107
|
lastTick = now;
|
|
64
108
|
const backlog = pendingCount();
|
|
@@ -66,12 +110,27 @@ function createTimerTextStream(options = {}) {
|
|
|
66
110
|
emit();
|
|
67
111
|
return;
|
|
68
112
|
}
|
|
69
|
-
const
|
|
113
|
+
const latencyMs = backlog >= catchUpThreshold ? catchUpLatencyMs : targetLatencyMs;
|
|
114
|
+
const cps = clamp(backlog * 1e3 / latencyMs, minCharsPerSecond, maxCharsPerSecond);
|
|
70
115
|
budget += cps * deltaMs / 1e3;
|
|
71
116
|
const commitCount = Math.min(backlog, maxCharsPerCommit, Math.max(1, Math.floor(budget)));
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
117
|
+
let proposedSegments = pending.slice(pendingOffset, pendingOffset + commitCount);
|
|
118
|
+
let safeCommitCount = fenceSafeCommitCount(visible, source, proposedSegments);
|
|
119
|
+
if (safeCommitCount === 0) {
|
|
120
|
+
const newlineOffset = pending.slice(pendingOffset).findIndex((segment) => segment.includes("\n"));
|
|
121
|
+
if (newlineOffset >= 0) {
|
|
122
|
+
proposedSegments = pending.slice(pendingOffset, pendingOffset + newlineOffset + 1);
|
|
123
|
+
safeCommitCount = fenceSafeCommitCount(visible, source, proposedSegments);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (safeCommitCount === 0) {
|
|
127
|
+
emit();
|
|
128
|
+
schedule();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
budget = Math.max(0, budget - safeCommitCount);
|
|
132
|
+
visible += proposedSegments.slice(0, safeCommitCount).join("");
|
|
133
|
+
pendingOffset += safeCommitCount;
|
|
75
134
|
compactPending();
|
|
76
135
|
emit();
|
|
77
136
|
schedule();
|
|
@@ -80,27 +139,37 @@ function createTimerTextStream(options = {}) {
|
|
|
80
139
|
clearTimeout(timer);
|
|
81
140
|
timer = void 0;
|
|
82
141
|
}
|
|
142
|
+
function destroy() {
|
|
143
|
+
if (destroyed) return;
|
|
144
|
+
destroyed = true;
|
|
145
|
+
cancelTimer();
|
|
146
|
+
pending.length = 0;
|
|
147
|
+
listeners.clear();
|
|
148
|
+
}
|
|
83
149
|
return {
|
|
84
|
-
destroy
|
|
85
|
-
|
|
86
|
-
destroyed = true;
|
|
87
|
-
cancelTimer();
|
|
88
|
-
pending.length = 0;
|
|
89
|
-
listeners.clear();
|
|
90
|
-
},
|
|
150
|
+
destroy,
|
|
151
|
+
dispose: destroy,
|
|
91
152
|
enqueue(chunk) {
|
|
92
153
|
if (destroyed || !chunk) return;
|
|
93
154
|
if (done) done = false;
|
|
155
|
+
const initialChunk = source.length === 0;
|
|
94
156
|
source += chunk;
|
|
95
157
|
pending.push(...splitGraphemes(chunk));
|
|
158
|
+
if (initialChunk) firstPendingAt = Date.now();
|
|
96
159
|
lastTick = Date.now();
|
|
160
|
+
if (initialChunk && burstInitialContent && pendingCount() >= burstRevealThresholdChars && !hasUnclosedFence(source)) {
|
|
161
|
+
visible = source;
|
|
162
|
+
pending.length = 0;
|
|
163
|
+
pendingOffset = 0;
|
|
164
|
+
firstPendingAt = 0;
|
|
165
|
+
}
|
|
97
166
|
emit();
|
|
98
167
|
schedule();
|
|
99
168
|
},
|
|
100
169
|
finish(finishOptions = {}) {
|
|
101
170
|
if (destroyed) return;
|
|
102
171
|
done = true;
|
|
103
|
-
if (finishOptions.flush) {
|
|
172
|
+
if (finishOptions.flush ?? flushOnFinish) {
|
|
104
173
|
visible = source;
|
|
105
174
|
pending.length = 0;
|
|
106
175
|
pendingOffset = 0;
|
|
@@ -125,7 +194,7 @@ function createTimerTextStream(options = {}) {
|
|
|
125
194
|
cancelTimer();
|
|
126
195
|
emit();
|
|
127
196
|
},
|
|
128
|
-
reset(content = "") {
|
|
197
|
+
reset(content = "", _resetOptions = {}) {
|
|
129
198
|
cancelTimer();
|
|
130
199
|
source = content;
|
|
131
200
|
visible = content;
|
|
@@ -134,6 +203,7 @@ function createTimerTextStream(options = {}) {
|
|
|
134
203
|
done = false;
|
|
135
204
|
paused = false;
|
|
136
205
|
budget = 0;
|
|
206
|
+
firstPendingAt = 0;
|
|
137
207
|
lastTick = Date.now();
|
|
138
208
|
emit();
|
|
139
209
|
},
|
|
@@ -156,6 +226,7 @@ function createTextStream(options = {}) {
|
|
|
156
226
|
const controller = createSmoothMarkdownStream(options);
|
|
157
227
|
return {
|
|
158
228
|
destroy: controller.destroy,
|
|
229
|
+
dispose: controller.dispose,
|
|
159
230
|
enqueue: controller.enqueue,
|
|
160
231
|
finish: controller.finish,
|
|
161
232
|
flush: controller.flush,
|
|
@@ -168,6 +239,12 @@ function createTextStream(options = {}) {
|
|
|
168
239
|
}
|
|
169
240
|
//#endregion
|
|
170
241
|
//#region src/agent-stream.ts
|
|
242
|
+
function requestIteratorCleanup(iterator) {
|
|
243
|
+
try {
|
|
244
|
+
const cleanup = iterator.return?.();
|
|
245
|
+
if (cleanup) cleanup.catch(() => void 0);
|
|
246
|
+
} catch {}
|
|
247
|
+
}
|
|
171
248
|
function initialSnapshot() {
|
|
172
249
|
return {
|
|
173
250
|
data: [],
|
|
@@ -395,6 +472,7 @@ function createAgentStreamController(options = {}) {
|
|
|
395
472
|
resolveCancellation = resolve;
|
|
396
473
|
});
|
|
397
474
|
cancelActiveConnect = resolveCancellation;
|
|
475
|
+
let protocolTerminal = false;
|
|
398
476
|
try {
|
|
399
477
|
try {
|
|
400
478
|
while (!cancelled && !destroyed) {
|
|
@@ -403,7 +481,12 @@ function createAgentStreamController(options = {}) {
|
|
|
403
481
|
result
|
|
404
482
|
})), cancellation.then(() => ({ kind: "cancelled" }))]);
|
|
405
483
|
if (outcome.kind === "cancelled" || outcome.result.done) break;
|
|
406
|
-
|
|
484
|
+
const event = outcome.result.value;
|
|
485
|
+
push(event);
|
|
486
|
+
if (event.type === "done" || event.type === "error") {
|
|
487
|
+
protocolTerminal = true;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
407
490
|
}
|
|
408
491
|
if (!transportDone && !cancelled && !destroyed) push({ type: "done" });
|
|
409
492
|
} catch (error) {
|
|
@@ -413,7 +496,7 @@ function createAgentStreamController(options = {}) {
|
|
|
413
496
|
type: "error"
|
|
414
497
|
});
|
|
415
498
|
} finally {
|
|
416
|
-
if (cancelled || destroyed) iterator
|
|
499
|
+
if (protocolTerminal || cancelled || destroyed) requestIteratorCleanup(iterator);
|
|
417
500
|
}
|
|
418
501
|
return await waitForTerminal();
|
|
419
502
|
} finally {
|
|
@@ -444,20 +527,41 @@ function createAgentEventChannel() {
|
|
|
444
527
|
let ended = false;
|
|
445
528
|
let failure;
|
|
446
529
|
let wake;
|
|
530
|
+
function stopConsuming() {
|
|
531
|
+
ended = true;
|
|
532
|
+
queue.length = 0;
|
|
533
|
+
notify();
|
|
534
|
+
}
|
|
447
535
|
async function* iterate() {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
536
|
+
try {
|
|
537
|
+
while (!ended || queue.length > 0) {
|
|
538
|
+
if (failure) throw failure;
|
|
539
|
+
const event = queue.shift();
|
|
540
|
+
if (event) {
|
|
541
|
+
yield event;
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
await new Promise((resolve) => {
|
|
545
|
+
wake = resolve;
|
|
546
|
+
});
|
|
547
|
+
wake = void 0;
|
|
454
548
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
wake = void 0;
|
|
549
|
+
if (failure) throw failure;
|
|
550
|
+
} finally {
|
|
551
|
+
stopConsuming();
|
|
459
552
|
}
|
|
460
|
-
|
|
553
|
+
}
|
|
554
|
+
function createIterator() {
|
|
555
|
+
const iterator = iterate();
|
|
556
|
+
return {
|
|
557
|
+
next() {
|
|
558
|
+
return iterator.next();
|
|
559
|
+
},
|
|
560
|
+
return() {
|
|
561
|
+
stopConsuming();
|
|
562
|
+
return iterator.return(void 0);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
461
565
|
}
|
|
462
566
|
function notify() {
|
|
463
567
|
wake?.();
|
|
@@ -477,7 +581,7 @@ function createAgentEventChannel() {
|
|
|
477
581
|
queue.push(event);
|
|
478
582
|
notify();
|
|
479
583
|
},
|
|
480
|
-
source: { [Symbol.asyncIterator]:
|
|
584
|
+
source: { [Symbol.asyncIterator]: createIterator }
|
|
481
585
|
};
|
|
482
586
|
}
|
|
483
587
|
//#endregion
|
|
@@ -656,6 +760,68 @@ function createStreamingMarkdownParser(options = {}) {
|
|
|
656
760
|
}
|
|
657
761
|
//#endregion
|
|
658
762
|
//#region src/markdown-view.ts
|
|
763
|
+
function richTextChildren(children) {
|
|
764
|
+
const result = [];
|
|
765
|
+
for (const child of children ?? []) {
|
|
766
|
+
const normalized = richTextNode(child);
|
|
767
|
+
if (!normalized) return;
|
|
768
|
+
result.push(normalized);
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
function richTextNode(node) {
|
|
773
|
+
if (node.kind === "text") return {
|
|
774
|
+
text: node.text ?? "",
|
|
775
|
+
type: "text"
|
|
776
|
+
};
|
|
777
|
+
if (node.kind === "hardbreak") return { name: "br" };
|
|
778
|
+
if (node.kind === "checkbox") return {
|
|
779
|
+
text: node.checked ? "☑ " : "☐ ",
|
|
780
|
+
type: "text"
|
|
781
|
+
};
|
|
782
|
+
const name = {
|
|
783
|
+
emphasis: "em",
|
|
784
|
+
strikethrough: "del",
|
|
785
|
+
strong: "strong",
|
|
786
|
+
subscript: "sub",
|
|
787
|
+
superscript: "sup"
|
|
788
|
+
}[node.kind];
|
|
789
|
+
if (name) {
|
|
790
|
+
const children = richTextChildren(node.children);
|
|
791
|
+
return children ? {
|
|
792
|
+
children,
|
|
793
|
+
name
|
|
794
|
+
} : void 0;
|
|
795
|
+
}
|
|
796
|
+
if (node.kind === "highlight" || node.kind === "insert") {
|
|
797
|
+
const children = richTextChildren(node.children);
|
|
798
|
+
if (!children) return;
|
|
799
|
+
return {
|
|
800
|
+
attrs: { style: node.kind === "highlight" ? "background:rgba(250,146,0,.18);border-radius:2px;" : "text-decoration:underline;" },
|
|
801
|
+
children,
|
|
802
|
+
name: "span"
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
if (node.kind === "inline-code") return {
|
|
806
|
+
attrs: { style: "font-family:monospace;padding:1px 4px;border-radius:4px;background:rgba(127,127,127,.12);" },
|
|
807
|
+
children: [{
|
|
808
|
+
text: node.text ?? "",
|
|
809
|
+
type: "text"
|
|
810
|
+
}],
|
|
811
|
+
name: "code"
|
|
812
|
+
};
|
|
813
|
+
if (node.kind === "math-inline") return {
|
|
814
|
+
attrs: { style: "font-family:monospace;" },
|
|
815
|
+
children: [{
|
|
816
|
+
text: node.text ?? "",
|
|
817
|
+
type: "text"
|
|
818
|
+
}],
|
|
819
|
+
name: "span"
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
function toAgentRichTextNodes(nodes) {
|
|
823
|
+
return richTextChildren(nodes);
|
|
824
|
+
}
|
|
659
825
|
function stringValue(value, fallback = "") {
|
|
660
826
|
return typeof value === "string" ? value : fallback;
|
|
661
827
|
}
|
|
@@ -672,7 +838,7 @@ function nodeField(node, key) {
|
|
|
672
838
|
return key in node ? Reflect.get(node, key) : void 0;
|
|
673
839
|
}
|
|
674
840
|
function normalizeUnknownNode(value) {
|
|
675
|
-
if (!isUnknownRecord(value) || typeof value.type !== "string" || typeof value.raw !== "string") return
|
|
841
|
+
if (!isUnknownRecord(value) || typeof value.type !== "string" || typeof value.raw !== "string") return;
|
|
676
842
|
return normalizeMarkdownNode(value);
|
|
677
843
|
}
|
|
678
844
|
function normalizeChildren(value) {
|
|
@@ -917,19 +1083,40 @@ function createAgentSseEventSource() {
|
|
|
917
1083
|
const dataLines = [];
|
|
918
1084
|
let textBuffer = "";
|
|
919
1085
|
let ended = false;
|
|
1086
|
+
function clearBufferedInput() {
|
|
1087
|
+
dataLines.length = 0;
|
|
1088
|
+
textBuffer = "";
|
|
1089
|
+
}
|
|
1090
|
+
function closeSource() {
|
|
1091
|
+
if (ended) return;
|
|
1092
|
+
ended = true;
|
|
1093
|
+
clearBufferedInput();
|
|
1094
|
+
channel.end();
|
|
1095
|
+
}
|
|
1096
|
+
function failSource(error) {
|
|
1097
|
+
if (ended) return;
|
|
1098
|
+
ended = true;
|
|
1099
|
+
clearBufferedInput();
|
|
1100
|
+
channel.fail(error);
|
|
1101
|
+
}
|
|
1102
|
+
function finishWith(event) {
|
|
1103
|
+
channel.push(event);
|
|
1104
|
+
closeSource();
|
|
1105
|
+
}
|
|
920
1106
|
function dispatch() {
|
|
921
1107
|
if (dataLines.length === 0 || ended) return;
|
|
922
1108
|
const payload = dataLines.join("\n");
|
|
923
1109
|
dataLines.length = 0;
|
|
924
1110
|
if (payload === "[DONE]") {
|
|
925
|
-
|
|
1111
|
+
finishWith({ type: "done" });
|
|
926
1112
|
return;
|
|
927
1113
|
}
|
|
928
1114
|
try {
|
|
929
|
-
|
|
1115
|
+
const event = parseAgentStreamEvent(JSON.parse(payload));
|
|
1116
|
+
if (event.type === "done" || event.type === "error") finishWith(event);
|
|
1117
|
+
else channel.push(event);
|
|
930
1118
|
} catch (error) {
|
|
931
|
-
|
|
932
|
-
channel.fail(error);
|
|
1119
|
+
failSource(error);
|
|
933
1120
|
}
|
|
934
1121
|
}
|
|
935
1122
|
function processLine(line) {
|
|
@@ -949,10 +1136,12 @@ function createAgentSseEventSource() {
|
|
|
949
1136
|
}
|
|
950
1137
|
}
|
|
951
1138
|
function appendText(value) {
|
|
1139
|
+
if (ended) return;
|
|
952
1140
|
textBuffer += value;
|
|
953
1141
|
let lineEnd = textBuffer.indexOf("\n");
|
|
954
1142
|
while (lineEnd >= 0) {
|
|
955
1143
|
processLine(textBuffer.slice(0, lineEnd));
|
|
1144
|
+
if (ended) return;
|
|
956
1145
|
textBuffer = textBuffer.slice(lineEnd + 1);
|
|
957
1146
|
lineEnd = textBuffer.indexOf("\n");
|
|
958
1147
|
}
|
|
@@ -961,15 +1150,13 @@ function createAgentSseEventSource() {
|
|
|
961
1150
|
end() {
|
|
962
1151
|
if (ended) return;
|
|
963
1152
|
appendText(decoder.decode(/* @__PURE__ */ new Uint8Array(0), true));
|
|
1153
|
+
if (ended) return;
|
|
964
1154
|
if (textBuffer.length > 0) processLine(textBuffer);
|
|
965
1155
|
dispatch();
|
|
966
|
-
|
|
967
|
-
channel.end();
|
|
1156
|
+
closeSource();
|
|
968
1157
|
},
|
|
969
1158
|
fail(error) {
|
|
970
|
-
|
|
971
|
-
ended = true;
|
|
972
|
-
channel.fail(error);
|
|
1159
|
+
failSource(error);
|
|
973
1160
|
},
|
|
974
1161
|
feed(chunk) {
|
|
975
1162
|
if (ended) return;
|
|
@@ -979,6 +1166,113 @@ function createAgentSseEventSource() {
|
|
|
979
1166
|
};
|
|
980
1167
|
}
|
|
981
1168
|
//#endregion
|
|
982
|
-
|
|
1169
|
+
//#region src/thread.ts
|
|
1170
|
+
const DESTROYED_ERROR = "Agent thread controller is destroyed";
|
|
1171
|
+
function assertVersionId(id) {
|
|
1172
|
+
if (id.length === 0) throw new Error("Agent thread version id must not be empty");
|
|
1173
|
+
}
|
|
1174
|
+
function freezeVersion(version) {
|
|
1175
|
+
return Object.freeze({ ...version });
|
|
1176
|
+
}
|
|
1177
|
+
function freezeSnapshot(versions, activeVersionId) {
|
|
1178
|
+
if (activeVersionId === void 0) return Object.freeze({ versions });
|
|
1179
|
+
return Object.freeze({
|
|
1180
|
+
activeVersionId,
|
|
1181
|
+
versions
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
function validateSnapshot(initial) {
|
|
1185
|
+
const sourceVersions = initial?.versions ?? [];
|
|
1186
|
+
const versionsById = /* @__PURE__ */ new Map();
|
|
1187
|
+
for (const version of sourceVersions) {
|
|
1188
|
+
assertVersionId(version.id);
|
|
1189
|
+
if (versionsById.has(version.id)) throw new Error(`Agent thread version "${version.id}" is duplicated`);
|
|
1190
|
+
versionsById.set(version.id, version);
|
|
1191
|
+
}
|
|
1192
|
+
for (const version of sourceVersions) if (version.parentId !== void 0 && !versionsById.has(version.parentId)) throw new Error(`Agent thread parent version "${version.parentId}" does not exist`);
|
|
1193
|
+
const resolvedIds = /* @__PURE__ */ new Set();
|
|
1194
|
+
for (const version of sourceVersions) {
|
|
1195
|
+
if (resolvedIds.has(version.id)) continue;
|
|
1196
|
+
const path = [];
|
|
1197
|
+
const pathIds = /* @__PURE__ */ new Set();
|
|
1198
|
+
let current = version;
|
|
1199
|
+
while (current !== void 0 && !resolvedIds.has(current.id)) {
|
|
1200
|
+
if (pathIds.has(current.id)) throw new Error(`Agent thread version "${current.id}" has a cyclic parent chain`);
|
|
1201
|
+
path.push(current.id);
|
|
1202
|
+
pathIds.add(current.id);
|
|
1203
|
+
current = current.parentId === void 0 ? void 0 : versionsById.get(current.parentId);
|
|
1204
|
+
}
|
|
1205
|
+
path.forEach((id) => resolvedIds.add(id));
|
|
1206
|
+
}
|
|
1207
|
+
const activeVersionId = initial?.activeVersionId;
|
|
1208
|
+
if (activeVersionId !== void 0 && !versionsById.has(activeVersionId)) throw new Error(`Agent thread active version "${activeVersionId}" does not exist`);
|
|
1209
|
+
return freezeSnapshot(Object.freeze(sourceVersions.map(freezeVersion)), activeVersionId);
|
|
1210
|
+
}
|
|
1211
|
+
function createAgentThreadController(initial) {
|
|
1212
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1213
|
+
let snapshot = validateSnapshot(initial);
|
|
1214
|
+
let destroyed = false;
|
|
1215
|
+
function assertActive() {
|
|
1216
|
+
if (destroyed) throw new Error(DESTROYED_ERROR);
|
|
1217
|
+
}
|
|
1218
|
+
function findVersion(id) {
|
|
1219
|
+
return snapshot.versions.find((version) => version.id === id);
|
|
1220
|
+
}
|
|
1221
|
+
function replace(nextSnapshot) {
|
|
1222
|
+
snapshot = nextSnapshot;
|
|
1223
|
+
listeners.forEach((listener) => listener());
|
|
1224
|
+
}
|
|
1225
|
+
function append(version) {
|
|
1226
|
+
assertActive();
|
|
1227
|
+
assertVersionId(version.id);
|
|
1228
|
+
if (findVersion(version.id)) throw new Error(`Agent thread version "${version.id}" already exists`);
|
|
1229
|
+
if (version.parentId !== void 0 && !findVersion(version.parentId)) throw new Error(`Agent thread parent version "${version.parentId}" does not exist`);
|
|
1230
|
+
replace(freezeSnapshot(Object.freeze([...snapshot.versions, freezeVersion(version)]), version.id));
|
|
1231
|
+
}
|
|
1232
|
+
function fork(parentId, version) {
|
|
1233
|
+
assertActive();
|
|
1234
|
+
assertVersionId(version.id);
|
|
1235
|
+
if (findVersion(version.id)) throw new Error(`Agent thread version "${version.id}" already exists`);
|
|
1236
|
+
if (!findVersion(parentId)) throw new Error(`Agent thread parent version "${parentId}" does not exist`);
|
|
1237
|
+
const forkedVersion = freezeVersion({
|
|
1238
|
+
...version,
|
|
1239
|
+
parentId
|
|
1240
|
+
});
|
|
1241
|
+
replace(freezeSnapshot(Object.freeze([...snapshot.versions, forkedVersion]), forkedVersion.id));
|
|
1242
|
+
}
|
|
1243
|
+
function select(id) {
|
|
1244
|
+
assertActive();
|
|
1245
|
+
if (!findVersion(id)) throw new Error(`Agent thread version "${id}" does not exist`);
|
|
1246
|
+
replace(freezeSnapshot(snapshot.versions, id));
|
|
1247
|
+
}
|
|
1248
|
+
function reset(nextInitial) {
|
|
1249
|
+
assertActive();
|
|
1250
|
+
replace(validateSnapshot(nextInitial));
|
|
1251
|
+
}
|
|
1252
|
+
function subscribe(listener) {
|
|
1253
|
+
assertActive();
|
|
1254
|
+
listeners.add(listener);
|
|
1255
|
+
return () => {
|
|
1256
|
+
listeners.delete(listener);
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
return {
|
|
1260
|
+
append,
|
|
1261
|
+
destroy() {
|
|
1262
|
+
if (destroyed) return;
|
|
1263
|
+
destroyed = true;
|
|
1264
|
+
listeners.clear();
|
|
1265
|
+
},
|
|
1266
|
+
fork,
|
|
1267
|
+
getSnapshot() {
|
|
1268
|
+
return snapshot;
|
|
1269
|
+
},
|
|
1270
|
+
reset,
|
|
1271
|
+
select,
|
|
1272
|
+
subscribe
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
//#endregion
|
|
1276
|
+
export { createAgentEventChannel, createAgentSseEventSource, createAgentStreamController, createAgentThreadController, createStreamingMarkdownParser, createTextStream, normalizeMarkdownNode, normalizeMarkdownNodes, parseAgentStreamEvent, toAgentRichTextNodes };
|
|
983
1277
|
|
|
984
1278
|
//# sourceMappingURL=index.mjs.map
|