@my-life-buddies/cli 0.13.3 → 0.14.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 +11 -7
- package/dist/bin/buddy.js +1 -1
- package/dist/bin/buddy.js.map +1 -1
- package/dist/bin/core.js +233 -54
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +281 -19
- package/dist/bin/preview.js.map +4 -4
- package/dist/web/app.css +65 -6
- package/dist/web/app.js +92 -33
- package/dist/web/diagnostics.js +184 -0
- package/dist/web/index.html +34 -34
- package/package.json +3 -3
- package/resources/agent-template/package-lock.json +4 -4
- package/resources/agent-template/package.json +2 -2
package/dist/bin/preview.js
CHANGED
|
@@ -9,6 +9,175 @@ function createBuddyPreviewUrl(buddyId) {
|
|
|
9
9
|
return `mlb://preview?${new URLSearchParams({ buddyId })}`;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// apps/preview/src/runtime-diagnostics.ts
|
|
13
|
+
function parseInspectedData(text2) {
|
|
14
|
+
let at = 0;
|
|
15
|
+
const space = () => {
|
|
16
|
+
while (/\s/u.test(text2[at] ?? "") && at < text2.length) at++;
|
|
17
|
+
};
|
|
18
|
+
const fail = () => {
|
|
19
|
+
throw new Error("Unsupported log data");
|
|
20
|
+
};
|
|
21
|
+
function string() {
|
|
22
|
+
const quote = text2[at++];
|
|
23
|
+
let result = "";
|
|
24
|
+
while (at < text2.length) {
|
|
25
|
+
const char = text2[at++];
|
|
26
|
+
if (char === quote) return result;
|
|
27
|
+
if (char !== "\\") {
|
|
28
|
+
result += char;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const escaped = text2[at++];
|
|
32
|
+
const escapes = { n: "\n", r: "\r", t: " ", b: "\b", f: "\f", v: "\v", "0": "\0", "\\": "\\", "'": "'", '"': '"', "`": "`", "$": "$" };
|
|
33
|
+
if (escaped === "x" || escaped === "u") {
|
|
34
|
+
const length = escaped === "x" ? 2 : 4;
|
|
35
|
+
const hex = text2.slice(at, at + length);
|
|
36
|
+
if (!new RegExp(`^[0-9a-fA-F]{${length}}$`, "u").test(hex)) fail();
|
|
37
|
+
result += String.fromCharCode(parseInt(hex, 16));
|
|
38
|
+
at += length;
|
|
39
|
+
} else if (escaped !== void 0 && Object.hasOwn(escapes, escaped)) result += escapes[escaped];
|
|
40
|
+
else fail();
|
|
41
|
+
}
|
|
42
|
+
return fail();
|
|
43
|
+
}
|
|
44
|
+
function value(depth = 0) {
|
|
45
|
+
if (depth > 40) fail();
|
|
46
|
+
space();
|
|
47
|
+
const char = text2[at];
|
|
48
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
49
|
+
let result = string();
|
|
50
|
+
space();
|
|
51
|
+
while (text2[at] === "+") {
|
|
52
|
+
at++;
|
|
53
|
+
space();
|
|
54
|
+
if (!["'", '"', "`"].includes(text2[at] ?? "")) fail();
|
|
55
|
+
result += string();
|
|
56
|
+
space();
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
if (char === "{" || char === "[") {
|
|
61
|
+
const array = char === "[";
|
|
62
|
+
const end = array ? "]" : "}";
|
|
63
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
64
|
+
const items = [];
|
|
65
|
+
at++;
|
|
66
|
+
space();
|
|
67
|
+
while (text2[at] !== end) {
|
|
68
|
+
if (at >= text2.length) fail();
|
|
69
|
+
if (array) items.push(value(depth + 1));
|
|
70
|
+
else {
|
|
71
|
+
const quoted = ["'", '"', "`"].includes(text2[at] ?? "");
|
|
72
|
+
const key = quoted ? string() : /^[\w$]+/u.exec(text2.slice(at))?.[0];
|
|
73
|
+
if (key === void 0) return fail();
|
|
74
|
+
if (!quoted) at += key.length;
|
|
75
|
+
space();
|
|
76
|
+
if (text2[at++] !== ":" || Object.hasOwn(result, key)) fail();
|
|
77
|
+
result[key] = value(depth + 1);
|
|
78
|
+
}
|
|
79
|
+
space();
|
|
80
|
+
if (text2[at] === end) break;
|
|
81
|
+
if (text2[at++] !== ",") fail();
|
|
82
|
+
space();
|
|
83
|
+
}
|
|
84
|
+
at++;
|
|
85
|
+
return array ? items : result;
|
|
86
|
+
}
|
|
87
|
+
const literal = /^(true|false|null|undefined|[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(?=[\s,\]}]|$)/iu.exec(text2.slice(at))?.[0];
|
|
88
|
+
if (!literal) return fail();
|
|
89
|
+
at += literal.length;
|
|
90
|
+
if (literal === "true") return true;
|
|
91
|
+
if (literal === "false") return false;
|
|
92
|
+
if (literal === "null" || literal === "undefined") return null;
|
|
93
|
+
const number = Number(literal);
|
|
94
|
+
return Number.isFinite(number) ? number : fail();
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
if (text2.length > 98304) return void 0;
|
|
98
|
+
const result = value();
|
|
99
|
+
space();
|
|
100
|
+
return at === text2.length && result !== null && typeof result === "object" && !Array.isArray(result) ? result : void 0;
|
|
101
|
+
} catch {
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
var HEADER = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[[^\]\r\n]+\] (INFO|WARN|ERROR)\s+(.+)$/u;
|
|
106
|
+
var MAX_RECORD = 98304;
|
|
107
|
+
var MAX_BYTES = 15e5;
|
|
108
|
+
var RuntimeDiagnostics = class {
|
|
109
|
+
#sequence = 0;
|
|
110
|
+
#records = [];
|
|
111
|
+
#active = /* @__PURE__ */ new Map();
|
|
112
|
+
#sizes = /* @__PURE__ */ new Map();
|
|
113
|
+
#bytes = 0;
|
|
114
|
+
#dropped = 0;
|
|
115
|
+
write(source, text2, observedAt) {
|
|
116
|
+
for (const line of text2.split(/\r?\n/u)) {
|
|
117
|
+
if (!line) continue;
|
|
118
|
+
const header = HEADER.exec(line);
|
|
119
|
+
const previousIndex = this.#records.findIndex((record3) => record3.id === this.#active.get(source));
|
|
120
|
+
const previous = this.#records[previousIndex];
|
|
121
|
+
const continuation = !header && previous && (/^\s/u.test(line) || line === "}");
|
|
122
|
+
let record2;
|
|
123
|
+
if (continuation) {
|
|
124
|
+
const raw = `${previous.raw}
|
|
125
|
+
${line}`;
|
|
126
|
+
record2 = { ...previous, raw: raw.slice(0, MAX_RECORD), truncated: previous.truncated || raw.length > MAX_RECORD || line.endsWith(" \u2026[truncated]") };
|
|
127
|
+
} else {
|
|
128
|
+
const title = header ? header[3].replace(/ \{.*$/u, "") : line.slice(0, 180);
|
|
129
|
+
const time = header ? Date.parse(`${header[1].replace(" ", "T")}+08:00`) : NaN;
|
|
130
|
+
record2 = {
|
|
131
|
+
id: `runtime-${++this.#sequence}`,
|
|
132
|
+
timestamp: Number.isFinite(time) ? new Date(time).toISOString() : observedAt,
|
|
133
|
+
source,
|
|
134
|
+
level: header ? header[2].toLowerCase() : source === "agent.stderr" ? "error" : "info",
|
|
135
|
+
title,
|
|
136
|
+
kind: header && title === "\u6A21\u578B\u8BF7\u6C42\u4F53" ? "request" : header && title === "\u6A21\u578B\u54CD\u5E94" ? "response" : "log",
|
|
137
|
+
raw: line.slice(0, MAX_RECORD),
|
|
138
|
+
truncated: line.length > MAX_RECORD || line.endsWith(" \u2026[truncated]")
|
|
139
|
+
};
|
|
140
|
+
this.#active.set(source, record2.id);
|
|
141
|
+
}
|
|
142
|
+
if (!record2.truncated && line.trimEnd().endsWith("}")) {
|
|
143
|
+
const head = HEADER.exec(record2.raw.split("\n")[0]);
|
|
144
|
+
const offset = head ? record2.raw.indexOf(" {", record2.raw.indexOf(head[3])) : -1;
|
|
145
|
+
const data = offset < 0 ? void 0 : parseInspectedData(record2.raw.slice(offset + 1));
|
|
146
|
+
if (data) record2 = { ...record2, data, ...typeof data.convoKey === "string" ? { convoKey: data.convoKey } : {} };
|
|
147
|
+
}
|
|
148
|
+
if (continuation && previous.data) {
|
|
149
|
+
const { data: _data, convoKey: _convoKey, ...rawRecord } = record2;
|
|
150
|
+
record2 = rawRecord;
|
|
151
|
+
}
|
|
152
|
+
this.#bytes -= this.#sizes.get(record2.id) ?? 0;
|
|
153
|
+
const size = Buffer.byteLength(JSON.stringify(record2));
|
|
154
|
+
this.#sizes.set(record2.id, size);
|
|
155
|
+
this.#bytes += size;
|
|
156
|
+
if (continuation) this.#records[previousIndex] = record2;
|
|
157
|
+
else this.#records.push(record2);
|
|
158
|
+
while (this.#records.length > 200 || this.#bytes > MAX_BYTES) {
|
|
159
|
+
const removed = this.#records.shift();
|
|
160
|
+
this.#bytes -= this.#sizes.get(removed.id) ?? 0;
|
|
161
|
+
this.#sizes.delete(removed.id);
|
|
162
|
+
this.#dropped++;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
boundary() {
|
|
167
|
+
this.#active.clear();
|
|
168
|
+
}
|
|
169
|
+
clear() {
|
|
170
|
+
this.#records = [];
|
|
171
|
+
this.#active.clear();
|
|
172
|
+
this.#sizes.clear();
|
|
173
|
+
this.#bytes = 0;
|
|
174
|
+
this.#dropped = 0;
|
|
175
|
+
}
|
|
176
|
+
snapshot() {
|
|
177
|
+
return { records: [...this.#records].reverse(), dropped: this.#dropped };
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
12
181
|
// apps/preview/src/controller.ts
|
|
13
182
|
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
14
183
|
function publicFailure(cause) {
|
|
@@ -31,8 +200,10 @@ var PreviewController = class {
|
|
|
31
200
|
#failedOutputRuns = /* @__PURE__ */ new Set();
|
|
32
201
|
#observedActiveRuns = /* @__PURE__ */ new Set();
|
|
33
202
|
#events = [];
|
|
203
|
+
#diagnostics = new RuntimeDiagnostics();
|
|
34
204
|
#runOrdinal = 0;
|
|
35
205
|
#conversationRevision = 0;
|
|
206
|
+
#simulatorRevision = 0;
|
|
36
207
|
#resetting = false;
|
|
37
208
|
#sendingMessage = false;
|
|
38
209
|
get resetting() {
|
|
@@ -78,7 +249,7 @@ var PreviewController = class {
|
|
|
78
249
|
return () => this.#simulatorListeners.delete(listener);
|
|
79
250
|
}
|
|
80
251
|
simulatorSnapshot() {
|
|
81
|
-
return Object.freeze({ ...this.#simulatorState, conversationRevision: this.#conversationRevision });
|
|
252
|
+
return Object.freeze({ ...this.#simulatorState, conversationRevision: this.#conversationRevision, stateRevision: this.#simulatorRevision });
|
|
82
253
|
}
|
|
83
254
|
start() {
|
|
84
255
|
if (this.#startPromise) return this.#startPromise;
|
|
@@ -87,6 +258,7 @@ var PreviewController = class {
|
|
|
87
258
|
return Promise.reject(Object.assign(new Error("DEV \u4F1A\u8BDD\u6B63\u5728\u505C\u6B62"), { code: "DEV_STOPPING" }));
|
|
88
259
|
}
|
|
89
260
|
const controller = new AbortController();
|
|
261
|
+
this.#diagnostics.boundary();
|
|
90
262
|
this.#startController = controller;
|
|
91
263
|
this.#record({ channel: "dev", type: "dev.start_requested", summary: "\u8BF7\u6C42\u542F\u52A8 DEV \u4F1A\u8BDD" });
|
|
92
264
|
this.#setState({ phase: "starting" });
|
|
@@ -95,7 +267,8 @@ var PreviewController = class {
|
|
|
95
267
|
signal: controller.signal,
|
|
96
268
|
onLog: ({ source, text: text2 }) => {
|
|
97
269
|
if (controller.signal.aborted) return;
|
|
98
|
-
this.#
|
|
270
|
+
this.#diagnostics.write(source, text2, this.#now().toISOString());
|
|
271
|
+
this.#record({ channel: "dev", type: source, summary: text2.slice(0, 512) });
|
|
99
272
|
this.#notifySimulator();
|
|
100
273
|
}
|
|
101
274
|
}).then(({ handle, ready, simulator }) => {
|
|
@@ -218,6 +391,7 @@ var PreviewController = class {
|
|
|
218
391
|
this.#runs.clear();
|
|
219
392
|
this.#observedActiveRuns.clear();
|
|
220
393
|
this.#events.length = 0;
|
|
394
|
+
this.#diagnostics.clear();
|
|
221
395
|
this.#runOrdinal = 0;
|
|
222
396
|
this.#lastActiveRunId = void 0;
|
|
223
397
|
this.#messages = Object.freeze([]);
|
|
@@ -235,7 +409,29 @@ var PreviewController = class {
|
|
|
235
409
|
const started = this.#now();
|
|
236
410
|
this.#record({ channel: "client", type: "message.send", summary: "\u53D1\u9001\u4E00\u6761\u79C1\u804A\u6D88\u606F" });
|
|
237
411
|
const accepted = await this.#requiredSimulator().send(input, { signal });
|
|
238
|
-
if (this.#runs.has(accepted.runId))
|
|
412
|
+
if (this.#runs.has(accepted.runId)) {
|
|
413
|
+
if (this.#simulatorState.activeRun?.runId === accepted.runId && !this.#simulatorState.messages.some((message2) => message2.messageId === accepted.messageId)) {
|
|
414
|
+
this.#record({
|
|
415
|
+
channel: "app_server",
|
|
416
|
+
type: "message.accepted",
|
|
417
|
+
summary: "\u8FFD\u52A0\u6D88\u606F\u5DF2\u63A5\u6536",
|
|
418
|
+
runId: accepted.runId,
|
|
419
|
+
durationMs: Math.max(0, this.#now().getTime() - started.getTime())
|
|
420
|
+
});
|
|
421
|
+
this.#simulatorState = Object.freeze({
|
|
422
|
+
...this.#simulatorState,
|
|
423
|
+
messages: Object.freeze([...this.#simulatorState.messages, Object.freeze({
|
|
424
|
+
messageId: accepted.messageId,
|
|
425
|
+
role: "user",
|
|
426
|
+
text: input.text,
|
|
427
|
+
createdAt: this.#now().toISOString()
|
|
428
|
+
})])
|
|
429
|
+
});
|
|
430
|
+
this.#messages = this.#simulatorState.messages;
|
|
431
|
+
this.#notifySimulator();
|
|
432
|
+
}
|
|
433
|
+
return accepted;
|
|
434
|
+
}
|
|
239
435
|
const acceptedAt = this.#now();
|
|
240
436
|
const latency = Math.max(0, acceptedAt.getTime() - started.getTime());
|
|
241
437
|
this.#upsertRun({
|
|
@@ -314,7 +510,8 @@ var PreviewController = class {
|
|
|
314
510
|
generatedAt: this.#now().toISOString(),
|
|
315
511
|
runs: Object.freeze([...this.#runs.values()].reverse()),
|
|
316
512
|
events: Object.freeze([...this.#events].reverse()),
|
|
317
|
-
messages: Object.freeze([...this.#messages])
|
|
513
|
+
messages: Object.freeze([...this.#messages]),
|
|
514
|
+
diagnostics: this.#diagnostics.snapshot()
|
|
318
515
|
});
|
|
319
516
|
}
|
|
320
517
|
devicePairing() {
|
|
@@ -460,7 +657,7 @@ var PreviewController = class {
|
|
|
460
657
|
void simulator.watchOutput(runId, {
|
|
461
658
|
signal: controller.signal,
|
|
462
659
|
onEvent: (event) => {
|
|
463
|
-
if (this.#resetting || simulator !== this.#simulator) return;
|
|
660
|
+
if (controller.signal.aborted || this.#resetting || simulator !== this.#simulator) return;
|
|
464
661
|
return this.#observeOutputEvent(event);
|
|
465
662
|
}
|
|
466
663
|
}).catch((cause) => {
|
|
@@ -479,6 +676,14 @@ var PreviewController = class {
|
|
|
479
676
|
});
|
|
480
677
|
}
|
|
481
678
|
async #observeOutputEvent(event) {
|
|
679
|
+
if (event.type === "state") {
|
|
680
|
+
if (event.resetStreaming) {
|
|
681
|
+
const { streaming: _streaming, ...state } = this.#simulatorState;
|
|
682
|
+
this.#simulatorState = Object.freeze(state);
|
|
683
|
+
}
|
|
684
|
+
this.#observeSimulatorState(event.state);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
482
687
|
if (event.type === "reset") {
|
|
483
688
|
if (this.#simulatorState.streaming?.runId === event.runId) {
|
|
484
689
|
const { streaming: _streaming, ...state } = this.#simulatorState;
|
|
@@ -560,6 +765,7 @@ var PreviewController = class {
|
|
|
560
765
|
controller.abort(new Error("Agent local turn reached a terminal state"));
|
|
561
766
|
}
|
|
562
767
|
#notifySimulator() {
|
|
768
|
+
this.#simulatorRevision++;
|
|
563
769
|
if (this.#simulatorListeners.size === 0) return;
|
|
564
770
|
const value = Object.freeze({ state: this.simulatorSnapshot(), debug: this.debugSnapshot() });
|
|
565
771
|
for (const listener of this.#simulatorListeners) {
|
|
@@ -640,6 +846,7 @@ var WEB_ASSETS = Object.freeze(/* @__PURE__ */ new Map([
|
|
|
640
846
|
contentType: "text/javascript; charset=utf-8",
|
|
641
847
|
body: readFileSync(new URL("../web/app.js", import.meta.url))
|
|
642
848
|
}],
|
|
849
|
+
["/assets/diagnostics.js", { contentType: "text/javascript; charset=utf-8", body: readFileSync(new URL("../web/diagnostics.js", import.meta.url)) }],
|
|
643
850
|
["/assets/developer-platform-icon.png", {
|
|
644
851
|
contentType: "image/png",
|
|
645
852
|
body: BRAND_ICON
|
|
@@ -1463,7 +1670,7 @@ var MlbSimulatorClient = class {
|
|
|
1463
1670
|
this.#active = void 0;
|
|
1464
1671
|
this.#streaming = void 0;
|
|
1465
1672
|
} else if (this.#active.observedReplying) {
|
|
1466
|
-
this.#latest = Object.freeze({ runId: this.#active.runId, status: "
|
|
1673
|
+
this.#latest = Object.freeze({ runId: this.#active.runId, status: "completed" });
|
|
1467
1674
|
this.#active = void 0;
|
|
1468
1675
|
this.#streaming = void 0;
|
|
1469
1676
|
}
|
|
@@ -1490,11 +1697,11 @@ var MlbSimulatorClient = class {
|
|
|
1490
1697
|
if (this.#lastSend.text !== input.text.trim()) throw httpError(409, { error: "\u540C\u4E00\u6761\u6D88\u606F\u4E0D\u80FD\u6539\u5199\u5185\u5BB9" });
|
|
1491
1698
|
return this.#lastSend.receipt;
|
|
1492
1699
|
}
|
|
1493
|
-
if (this.#
|
|
1494
|
-
throw Object.assign(new Error("\
|
|
1495
|
-
code: "
|
|
1700
|
+
if (this.#sending) {
|
|
1701
|
+
throw Object.assign(new Error("\u6D88\u606F\u6B63\u5728\u53D1\u9001"), {
|
|
1702
|
+
code: "SEND_IN_PROGRESS",
|
|
1496
1703
|
status: 409,
|
|
1497
|
-
publicMessage: "\
|
|
1704
|
+
publicMessage: "\u6D88\u606F\u6B63\u5728\u53D1\u9001\uFF0C\u8BF7\u7A0D\u5019"
|
|
1498
1705
|
});
|
|
1499
1706
|
}
|
|
1500
1707
|
this.#sending = true;
|
|
@@ -1528,15 +1735,17 @@ var MlbSimulatorClient = class {
|
|
|
1528
1735
|
}
|
|
1529
1736
|
const receipt = object(value);
|
|
1530
1737
|
const item = object(receipt.items[0]);
|
|
1531
|
-
const runId = localReceipt("turn", idempotencyKey);
|
|
1738
|
+
const runId = this.#active?.runId ?? localReceipt("turn", idempotencyKey);
|
|
1532
1739
|
const messageId = safeText(item.messageId, "receipt.items[0].messageId", 256);
|
|
1533
|
-
this.#active
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1740
|
+
if (!this.#active) {
|
|
1741
|
+
this.#active = receipt.shouldReply ? Object.freeze({
|
|
1742
|
+
runId,
|
|
1743
|
+
messageTimestamp: item.timestamp,
|
|
1744
|
+
observedReplying: false
|
|
1745
|
+
}) : void 0;
|
|
1746
|
+
this.#latest = Object.freeze({ runId, status: receipt.shouldReply ? "pending" : "completed" });
|
|
1747
|
+
this.#streaming = void 0;
|
|
1748
|
+
}
|
|
1540
1749
|
const accepted = Object.freeze({ messageId, runId });
|
|
1541
1750
|
this.#lastSend = { key: idempotencyKey, text: input.text.trim(), receipt: accepted };
|
|
1542
1751
|
return accepted;
|
|
@@ -1604,6 +1813,7 @@ var MlbSimulatorClient = class {
|
|
|
1604
1813
|
const url = endpoint(this.#baseUrl, "/app/ws");
|
|
1605
1814
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
1606
1815
|
let sequence = 0;
|
|
1816
|
+
let assistantIds = new Set(this.#messages.filter((message2) => message2.role === "assistant").map((message2) => message2.messageId));
|
|
1607
1817
|
const reset = async () => {
|
|
1608
1818
|
sequence = 0;
|
|
1609
1819
|
this.#streaming = void 0;
|
|
@@ -1629,11 +1839,21 @@ var MlbSimulatorClient = class {
|
|
|
1629
1839
|
continue;
|
|
1630
1840
|
}
|
|
1631
1841
|
if (frame.event !== "chat" && frame.event !== "ready") continue;
|
|
1632
|
-
|
|
1842
|
+
let state = await this.state({ signal: options.signal });
|
|
1843
|
+
const nextAssistantIds = new Set(state.messages.filter((message2) => message2.role === "assistant").map((message2) => message2.messageId));
|
|
1844
|
+
const committedReply = [...nextAssistantIds].some((id) => !assistantIds.has(id));
|
|
1845
|
+
assistantIds = nextAssistantIds;
|
|
1846
|
+
if (committedReply && state.activeRun) {
|
|
1847
|
+
sequence = 0;
|
|
1848
|
+
this.#streaming = void 0;
|
|
1849
|
+
const { streaming: _streaming, ...snapshot } = state;
|
|
1850
|
+
state = snapshot;
|
|
1851
|
+
}
|
|
1633
1852
|
if (state.latestRun?.runId === safeRunId && (state.latestRun.status === "completed" || state.latestRun.status === "failed")) {
|
|
1634
1853
|
await options.onEvent({ type: state.latestRun.status, runId: safeRunId, status: state.latestRun.status });
|
|
1635
1854
|
return;
|
|
1636
1855
|
}
|
|
1856
|
+
await options.onEvent({ type: "state", runId: safeRunId, state, resetStreaming: committedReply });
|
|
1637
1857
|
}
|
|
1638
1858
|
if (options.signal?.aborted) return;
|
|
1639
1859
|
} catch (cause) {
|
|
@@ -1728,6 +1948,42 @@ async function connectPreviewBridge(options) {
|
|
|
1728
1948
|
const initial = await attach();
|
|
1729
1949
|
if (initial.reused) return { reused: true, completion: Promise.resolve(), async close() {
|
|
1730
1950
|
} };
|
|
1951
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
1952
|
+
let eventSequence = 0;
|
|
1953
|
+
let publishing;
|
|
1954
|
+
let retryEvents;
|
|
1955
|
+
const flushEvents = () => {
|
|
1956
|
+
if (!initial.liveEvents || signal.aborted || publishing || retryEvents || !pendingEvents.size) return;
|
|
1957
|
+
publishing = (async () => {
|
|
1958
|
+
while (!signal.aborted && pendingEvents.size) {
|
|
1959
|
+
const events = [...pendingEvents.values()].sort((a, b) => a.sequence - b.sequence);
|
|
1960
|
+
try {
|
|
1961
|
+
await call("api/preview/events", connectionId, { events });
|
|
1962
|
+
for (const event of events) if (pendingEvents.get(event.type) === event) pendingEvents.delete(event.type);
|
|
1963
|
+
} catch {
|
|
1964
|
+
if (!signal.aborted) retryEvents = setTimeout(() => {
|
|
1965
|
+
retryEvents = void 0;
|
|
1966
|
+
flushEvents();
|
|
1967
|
+
}, 500);
|
|
1968
|
+
break;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
})().finally(() => {
|
|
1972
|
+
publishing = void 0;
|
|
1973
|
+
});
|
|
1974
|
+
};
|
|
1975
|
+
const queueEvent = (type, value) => {
|
|
1976
|
+
if (!initial.liveEvents || signal.aborted) return;
|
|
1977
|
+
pendingEvents.set(type, { sequence: eventSequence++, type, value });
|
|
1978
|
+
flushEvents();
|
|
1979
|
+
};
|
|
1980
|
+
const snapshotEvents = () => {
|
|
1981
|
+
queueEvent("dev.state", options.controller.state());
|
|
1982
|
+
queueEvent("simulator.state", { state: options.controller.simulatorSnapshot() });
|
|
1983
|
+
};
|
|
1984
|
+
const unsubscribeDev = options.controller.subscribe((state) => queueEvent("dev.state", state));
|
|
1985
|
+
const unsubscribeSimulator = options.controller.subscribeSimulator(({ state }) => queueEvent("simulator.state", { state }));
|
|
1986
|
+
snapshotEvents();
|
|
1731
1987
|
const watchdog = setInterval(() => {
|
|
1732
1988
|
if (Date.now() - lastContact < 1e4 || stopping || interrupted) return;
|
|
1733
1989
|
interrupted = true;
|
|
@@ -1801,12 +2057,18 @@ async function connectPreviewBridge(options) {
|
|
|
1801
2057
|
if (status === 410) {
|
|
1802
2058
|
await options.controller.stop();
|
|
1803
2059
|
if ((await attach()).reused) throw new Error("\u5F53\u524D\u5DE5\u7A0B\u5DF2\u6709\u5176\u4ED6 CLI \u8FDE\u63A5\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C preview \u6253\u5F00\u540E\u53F0");
|
|
2060
|
+
snapshotEvents();
|
|
1804
2061
|
} else if (status && status >= 400 && status < 500 && status !== 429) throw cause;
|
|
1805
2062
|
}
|
|
1806
2063
|
await delay(replies.size ? 20 : 500, void 0, { signal }).catch(() => void 0);
|
|
1807
2064
|
}
|
|
1808
2065
|
} finally {
|
|
1809
2066
|
clearInterval(watchdog);
|
|
2067
|
+
unsubscribeDev();
|
|
2068
|
+
unsubscribeSimulator();
|
|
2069
|
+
if (retryEvents) clearTimeout(retryEvents);
|
|
2070
|
+
shutdown.abort();
|
|
2071
|
+
await publishing;
|
|
1810
2072
|
}
|
|
1811
2073
|
})();
|
|
1812
2074
|
void completion.catch(() => void 0);
|