@burdenoff/fe-libs 2026.925.3 → 2026.925.4

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.
@@ -0,0 +1,283 @@
1
+ /**
2
+ * ★★ Where a turn's time goes — measured before anything is optimised
3
+ * (BOFF-7801).
4
+ *
5
+ * First answers took 55–150s+ in production, and nobody could say which part
6
+ * of that was the sandbox starting, a dead sandbox being probed, the model
7
+ * thinking, the poll noticing, our own bookkeeping, or the voice fetching
8
+ * audio. This records each milestone of a turn as it happens, so the slow part
9
+ * is measured instead of guessed.
10
+ *
11
+ * The chain, in order:
12
+ *
13
+ * speechEnded the last time the heard words CHANGED (voice only)
14
+ * graceStarted the correction grace began — the last result that re-armed
15
+ * it; after speechEnded when the recogniser re-commits late
16
+ * graceEnded the correction grace ran out; the turn was submitted
17
+ * handedOver its attachments settled and it went to the panel (voice)
18
+ * accepted the panel took the turn: the question is on screen
19
+ * cleared the send-time checks passed (mode access, write consent)
20
+ * attemptStarted this attempt at a runtime began (again, on a retry)
21
+ * provisionStarted no reusable sandbox — making room for a new one starts here
22
+ * createRequested the new sandbox was requested (after releasing old ones)
23
+ * sandboxRunning the created sandbox is running (cold)
24
+ * serviceReady the assistant service inside it answers (cold)
25
+ * runtimeReady a sandbox and a session are ready to take the prompt
26
+ * promptSent the sandbox accepted the prompt
27
+ * lastQuietPoll the last poll that saw nothing — with firstOutput, the
28
+ * UPPER bound of how late polling noticed the output
29
+ * firstOutput the first poll that saw anything to show (a tool line too)
30
+ * firstText the first poll that saw answer TEXT — prose not followed
31
+ * by a tool call, confirmed when the turn completes, so
32
+ * pre-tool narration does not count. In modes whose
33
+ * narration IS the answer (BigConsole's build log), the
34
+ * first narration: group reports by `mode`
35
+ * answerDone the poll that saw the answer complete
36
+ * complete the turn ended (after artifacts and the credit report)
37
+ * ttsStarted auto-speak handed the reply to the speech engine
38
+ * firstAudio the engine reported sound actually started
39
+ *
40
+ * ★ `graceStarted → graceEnded` is the 2s correction grace, and it is KEPT: it
41
+ * stops a pause mid-sentence from submitting half a question. It is a segment
42
+ * of its own so it can never be "optimised" away by accident — and the
43
+ * recogniser's own endpointing (`speechEnded → graceStarted`, 0.5–1.5s and
44
+ * longer on Android) is kept OUT of it, because that one can be optimised.
45
+ *
46
+ * ★ Filler is never output: the progress `content` is model output only
47
+ * (BOFF-7277) and the waiting copy is drawn by the bubble from the stage.
48
+ *
49
+ * ★ A turn whose first attempt fails recoverably is retried under the SAME id.
50
+ * `restartAttempt` files the first attempt's marks under `attempts` so the
51
+ * second records its own — mixed attempts are not a timeline.
52
+ *
53
+ * Times are `performance.now()` — monotonic, comparable within the page. The
54
+ * event is visible to EVERY script on the page, so it carries timings and
55
+ * coarse dimensions only: never message text, never tenant, user, workspace or
56
+ * conversation ids. Delivery is asynchronous, so a listener never runs inside
57
+ * the step it measures.
58
+ */
59
+ export declare const TURN_MILESTONES: readonly ["speechEnded", "graceStarted", "graceEnded", "handedOver", "accepted", "cleared", "attemptStarted", "provisionStarted", "createRequested", "sandboxRunning", "serviceReady", "runtimeReady", "promptSent", "lastQuietPoll", "firstOutput", "firstText", "answerDone", "complete", "ttsStarted", "firstAudio"];
60
+ export type TurnMilestone = (typeof TURN_MILESTONES)[number];
61
+ /**
62
+ * How the runtime was found:
63
+ * - `warm`: a remembered sandbox with a fresh readiness proof, session reused
64
+ * - `new-session`: that sandbox, but a new session
65
+ * - `probed`: a RUNNING sandbox was reused only after a reachability probe
66
+ * - `adopted`: a boot was joined and waited out — the sandbox was listed
67
+ * PROVISIONING/PENDING, or its service answered "not up yet" during the
68
+ * probe (a reload or second tab mid-cold-start; a remembered sandbox that
69
+ * restarted or woke). ★ Limits: a single transient proxy 502 also reads as
70
+ * adopted, and a remembered sandbox reported RUNNING only once serving
71
+ * gives no signal at all — so `probed` can still hold a joined boot: read
72
+ * its `runtime` segment, not just the label
73
+ * - `cold`: a sandbox was created
74
+ */
75
+ export type TurnRuntime = "warm" | "new-session" | "probed" | "adopted" | "cold";
76
+ /**
77
+ * Counted as the person was SHOWN it (`classifyTurnFailure`):
78
+ * - `refused`: a usage wall, a permission refusal (server 403/429 included),
79
+ * or a confirmation to give — a decision, not a breakdown;
80
+ * - `failed`: a temporary failure, including a mode-access check that had not
81
+ * answered yet;
82
+ * - `timedOut`: the client's stream budget ran out before the answer finished.
83
+ * Only `answered` turns carry `firstText`; a timed-out turn's unconfirmed text
84
+ * is not recorded.
85
+ */
86
+ export type TurnTimelineOutcome = "answered" | "timedOut" | "refused" | "failed" | "stopped";
87
+ export interface TurnDimensions {
88
+ channel: "chat" | "voice";
89
+ mode: string;
90
+ product: string;
91
+ /** The reply language the turn asked for. */
92
+ language: string;
93
+ device: "touch" | "pointer" | "unknown";
94
+ /** Auto chose the mode (an Auto escalation can cost a cold start). */
95
+ routedByAuto: boolean;
96
+ }
97
+ /** What one attempt found out. */
98
+ export interface TurnAttemptFacts {
99
+ runtime?: TurnRuntime;
100
+ /** The model called at least one tool on the way to its answer. */
101
+ usedTools?: boolean;
102
+ /** A reuse probe failed — a dead sandbox cost time before a new one. */
103
+ deadSandboxProbe?: boolean;
104
+ }
105
+ export interface TurnAttempt extends TurnAttemptFacts {
106
+ at: Partial<Record<TurnMilestone, number>>;
107
+ /** When the attempt was abandoned for the next one: its time to failure. */
108
+ endedAt?: number;
109
+ }
110
+ export interface TurnTimeline extends TurnDimensions, TurnAttemptFacts {
111
+ turnId: string;
112
+ at: Partial<Record<TurnMilestone, number>>;
113
+ /** Earlier attempts of a retried turn, oldest first. */
114
+ attempts?: TurnAttempt[];
115
+ retried?: boolean;
116
+ outcome?: TurnTimelineOutcome;
117
+ }
118
+ /** Marks a voice turn brings with it, taken before the panel has an id for it. */
119
+ export type VoiceTurnTiming = Partial<Record<"speechEnded" | "graceStarted" | "graceEnded" | "handedOver", number>>;
120
+ /** Announced on `window` with a copy of the timeline each time it changes. */
121
+ export declare const TURN_TIMELINE_EVENT = "burdenoff:assistant-turn-timeline";
122
+ export declare function turnClockMs(): number;
123
+ /** Coarse pointer = a finger. The split the latency report needs, not a device model. */
124
+ export declare function detectTurnDevice(): TurnDimensions["device"];
125
+ /**
126
+ * Start recording a turn. `carried` holds marks taken before the turn had an
127
+ * id — a voice turn's end of speech, end of grace and hand-over.
128
+ *
129
+ * Idempotent: beginning a recorded turn again does nothing, so nothing can
130
+ * reset a clock a turn is being measured against.
131
+ */
132
+ export declare function beginTurn(turnId: string, dimensions: TurnDimensions, carried?: VoiceTurnTiming): void;
133
+ /**
134
+ * Record a milestone. The FIRST time wins — the poll that first saw output is
135
+ * the one that counts, however many polls follow — and a turn that was never
136
+ * begun is ignored, so "Listen" on yesterday's answer is not a turn.
137
+ */
138
+ export declare function markTurn(turnId: string, milestone: TurnMilestone, at?: number): void;
139
+ /** Facts about the turn learnt along the way. The first answer to each stands. */
140
+ export declare function describeTurn(turnId: string, facts: TurnAttemptFacts & Pick<TurnTimeline, "outcome">): void;
141
+ /**
142
+ * The turn is starting over on a new runtime. File this attempt's marks and
143
+ * facts under `attempts`, stamped with when it ended, and clear them, so the
144
+ * next attempt records its own. What the person did — speech, hand-over,
145
+ * acceptance, the checks — stays.
146
+ */
147
+ export declare function restartAttempt(turnId: string, at?: number): void;
148
+ export declare function turnTimeline(turnId: string): TurnTimeline | undefined;
149
+ /** Newest last. For a console or a measurement run. */
150
+ export declare function recentTurnTimelines(): TurnTimeline[];
151
+ /** Tests only. */
152
+ export declare function resetTurnTimelines(): void;
153
+ /**
154
+ * The named spans of a turn, each present only when both ends were recorded
155
+ * in order. The first group is contiguous steps; the `to*` and speech totals
156
+ * are what a person actually feels.
157
+ */
158
+ export declare const TURN_SEGMENTS: readonly [{
159
+ readonly name: "endpointing";
160
+ readonly from: "speechEnded";
161
+ readonly to: "graceStarted";
162
+ }, {
163
+ readonly name: "grace";
164
+ readonly from: "graceStarted";
165
+ readonly to: "graceEnded";
166
+ }, {
167
+ readonly name: "uploadWait";
168
+ readonly from: "graceEnded";
169
+ readonly to: "handedOver";
170
+ }, {
171
+ readonly name: "queue";
172
+ readonly from: "handedOver";
173
+ readonly to: "accepted";
174
+ }, {
175
+ readonly name: "checks";
176
+ readonly from: "accepted";
177
+ readonly to: "cleared";
178
+ }, {
179
+ readonly name: "runtime";
180
+ readonly from: "attemptStarted";
181
+ readonly to: "runtimeReady";
182
+ }, {
183
+ readonly name: "reuseCheck";
184
+ readonly from: "attemptStarted";
185
+ readonly to: "provisionStarted";
186
+ }, {
187
+ readonly name: "slotRelease";
188
+ readonly from: "provisionStarted";
189
+ readonly to: "createRequested";
190
+ }, {
191
+ readonly name: "sandboxStart";
192
+ readonly from: "createRequested";
193
+ readonly to: "sandboxRunning";
194
+ }, {
195
+ readonly name: "serviceStart";
196
+ readonly from: "sandboxRunning";
197
+ readonly to: "serviceReady";
198
+ }, {
199
+ readonly name: "prompt";
200
+ readonly from: "runtimeReady";
201
+ readonly to: "promptSent";
202
+ }, {
203
+ readonly name: "model";
204
+ readonly from: "promptSent";
205
+ readonly to: "firstOutput";
206
+ }, {
207
+ readonly name: "pollLag";
208
+ readonly from: "lastQuietPoll";
209
+ readonly to: "firstOutput";
210
+ }, {
211
+ readonly name: "finish";
212
+ readonly from: "firstOutput";
213
+ readonly to: "answerDone";
214
+ }, {
215
+ readonly name: "post";
216
+ readonly from: "answerDone";
217
+ readonly to: "complete";
218
+ }, {
219
+ readonly name: "speechQueue";
220
+ readonly from: "complete";
221
+ readonly to: "ttsStarted";
222
+ }, {
223
+ readonly name: "speechFetch";
224
+ readonly from: "ttsStarted";
225
+ readonly to: "firstAudio";
226
+ }, {
227
+ readonly name: "toFirstOutput";
228
+ readonly from: "accepted";
229
+ readonly to: "firstOutput";
230
+ }, {
231
+ readonly name: "toFirstText";
232
+ readonly from: "accepted";
233
+ readonly to: "firstText";
234
+ }, {
235
+ readonly name: "toAnswer";
236
+ readonly from: "accepted";
237
+ readonly to: "answerDone";
238
+ }, {
239
+ readonly name: "toComplete";
240
+ readonly from: "accepted";
241
+ readonly to: "complete";
242
+ }, {
243
+ readonly name: "toFirstAudio";
244
+ readonly from: "accepted";
245
+ readonly to: "firstAudio";
246
+ }, {
247
+ readonly name: "processingToFirstAudio";
248
+ readonly from: "graceEnded";
249
+ readonly to: "firstAudio";
250
+ }, {
251
+ readonly name: "heardAfterSpeaking";
252
+ readonly from: "speechEnded";
253
+ readonly to: "firstAudio";
254
+ }];
255
+ export type TurnSegmentName = (typeof TURN_SEGMENTS)[number]["name"];
256
+ /**
257
+ * A segment whose end came BEFORE its start is left out: it can only be a
258
+ * wiring fault, and a negative duration averaged in would hide the slow case.
259
+ */
260
+ export declare function turnSegments(timeline: Pick<TurnTimeline, "at">): Partial<Record<TurnSegmentName, number>>;
261
+ export interface SegmentStats {
262
+ n: number;
263
+ median: number;
264
+ /** Nearest-rank 90th percentile — "the slow case", reported apart from the median. */
265
+ p90: number;
266
+ max: number;
267
+ }
268
+ export declare function segmentStats(values: number[]): SegmentStats | undefined;
269
+ /**
270
+ * The turns whose segments compare: answered, on one attempt. A retried,
271
+ * failed, refused, stopped, timed-out or unfinished turn is not averaged into
272
+ * steps it did not take. ★ A retried turn is often the slowest a person felt:
273
+ * report it too — its own group, its felt totals (`toAnswer`, `toFirstAudio`)
274
+ * — by passing `(t) => t.retried === true && t.outcome === "answered"` as
275
+ * `include` (and the retried turns that did not answer, by count).
276
+ */
277
+ export declare function isComparableTurn(timeline: TurnTimeline): boolean;
278
+ /**
279
+ * Median and slow case per segment, split by whatever `groupOf` says — warm /
280
+ * cold, tool / no tool, language, device, channel, or any combination.
281
+ */
282
+ export declare function summariseTurnTimelines(timelines: readonly TurnTimeline[], groupOf: (timeline: TurnTimeline) => string, include?: (timeline: TurnTimeline) => boolean): Record<string, Partial<Record<TurnSegmentName, SegmentStats>>>;
283
+ //# sourceMappingURL=turnTimeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnTimeline.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/turnTimeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,291 @@
1
+ //#region src/shared/assistant/turnTimeline.ts
2
+ var e = [
3
+ "speechEnded",
4
+ "graceStarted",
5
+ "graceEnded",
6
+ "handedOver",
7
+ "accepted",
8
+ "cleared",
9
+ "attemptStarted",
10
+ "provisionStarted",
11
+ "createRequested",
12
+ "sandboxRunning",
13
+ "serviceReady",
14
+ "runtimeReady",
15
+ "promptSent",
16
+ "lastQuietPoll",
17
+ "firstOutput",
18
+ "firstText",
19
+ "answerDone",
20
+ "complete",
21
+ "ttsStarted",
22
+ "firstAudio"
23
+ ], t = [
24
+ "attemptStarted",
25
+ "provisionStarted",
26
+ "createRequested",
27
+ "sandboxRunning",
28
+ "serviceReady",
29
+ "runtimeReady",
30
+ "promptSent",
31
+ "lastQuietPoll",
32
+ "firstOutput",
33
+ "firstText",
34
+ "answerDone"
35
+ ], n = "burdenoff:assistant-turn-timeline", r = 50, i = /* @__PURE__ */ new Map();
36
+ function a() {
37
+ try {
38
+ if (typeof performance < "u" && typeof performance?.now == "function") return performance.now();
39
+ } catch {}
40
+ return Date.now();
41
+ }
42
+ function o() {
43
+ try {
44
+ let e = typeof window > "u" ? null : window;
45
+ return !e || typeof e.matchMedia != "function" ? "unknown" : e.matchMedia("(pointer: coarse)").matches ? "touch" : "pointer";
46
+ } catch {
47
+ return "unknown";
48
+ }
49
+ }
50
+ function s(e) {
51
+ return {
52
+ ...e,
53
+ at: { ...e.at },
54
+ ...e.attempts ? { attempts: e.attempts.map((e) => ({
55
+ ...e,
56
+ at: { ...e.at }
57
+ })) } : {}
58
+ };
59
+ }
60
+ function c(e) {
61
+ let t = s(e), r = () => {
62
+ try {
63
+ let e = typeof window > "u" ? null : window;
64
+ if (!e || typeof e.dispatchEvent != "function" || typeof CustomEvent != "function") return;
65
+ e.dispatchEvent(new CustomEvent(n, { detail: t }));
66
+ } catch {}
67
+ };
68
+ typeof queueMicrotask == "function" ? queueMicrotask(r) : r();
69
+ }
70
+ function l(e, t, n) {
71
+ if (i.has(e)) return;
72
+ let a = {};
73
+ n?.speechEnded !== void 0 && (a.speechEnded = n.speechEnded), n?.graceStarted !== void 0 && (a.graceStarted = n.graceStarted), n?.graceEnded !== void 0 && (a.graceEnded = n.graceEnded), n?.handedOver !== void 0 && (a.handedOver = n.handedOver);
74
+ let o = {
75
+ turnId: e,
76
+ ...t,
77
+ at: a
78
+ };
79
+ for (i.set(e, o); i.size > r;) {
80
+ let e = i.keys().next().value;
81
+ if (e === void 0) break;
82
+ i.delete(e);
83
+ }
84
+ c(o);
85
+ }
86
+ function u(e, t, n = a()) {
87
+ let r = i.get(e);
88
+ !r || r.at[t] !== void 0 || (r.at[t] = n, c(r));
89
+ }
90
+ function d(e, t) {
91
+ let n = i.get(e);
92
+ if (!n) return;
93
+ let r = !1;
94
+ t.runtime !== void 0 && n.runtime === void 0 && (n.runtime = t.runtime, r = !0), t.usedTools !== void 0 && n.usedTools === void 0 && (n.usedTools = t.usedTools, r = !0), t.deadSandboxProbe !== void 0 && n.deadSandboxProbe === void 0 && (n.deadSandboxProbe = t.deadSandboxProbe, r = !0), t.outcome !== void 0 && n.outcome === void 0 && (n.outcome = t.outcome, r = !0), r && c(n);
95
+ }
96
+ function f(e, n = a()) {
97
+ let r = i.get(e);
98
+ if (!r) return;
99
+ let o = {
100
+ at: {},
101
+ endedAt: n
102
+ };
103
+ for (let e of t) {
104
+ let t = r.at[e];
105
+ t !== void 0 && (o.at[e] = t, delete r.at[e]);
106
+ }
107
+ r.runtime !== void 0 && (o.runtime = r.runtime), r.usedTools !== void 0 && (o.usedTools = r.usedTools), r.deadSandboxProbe !== void 0 && (o.deadSandboxProbe = r.deadSandboxProbe), delete r.runtime, delete r.usedTools, delete r.deadSandboxProbe, r.attempts = [...r.attempts ?? [], o], r.retried = !0, c(r);
108
+ }
109
+ function p(e) {
110
+ let t = i.get(e);
111
+ return t ? s(t) : void 0;
112
+ }
113
+ function m() {
114
+ return [...i.values()].map(s);
115
+ }
116
+ function h() {
117
+ i.clear();
118
+ }
119
+ var g = [
120
+ {
121
+ name: "endpointing",
122
+ from: "speechEnded",
123
+ to: "graceStarted"
124
+ },
125
+ {
126
+ name: "grace",
127
+ from: "graceStarted",
128
+ to: "graceEnded"
129
+ },
130
+ {
131
+ name: "uploadWait",
132
+ from: "graceEnded",
133
+ to: "handedOver"
134
+ },
135
+ {
136
+ name: "queue",
137
+ from: "handedOver",
138
+ to: "accepted"
139
+ },
140
+ {
141
+ name: "checks",
142
+ from: "accepted",
143
+ to: "cleared"
144
+ },
145
+ {
146
+ name: "runtime",
147
+ from: "attemptStarted",
148
+ to: "runtimeReady"
149
+ },
150
+ {
151
+ name: "reuseCheck",
152
+ from: "attemptStarted",
153
+ to: "provisionStarted"
154
+ },
155
+ {
156
+ name: "slotRelease",
157
+ from: "provisionStarted",
158
+ to: "createRequested"
159
+ },
160
+ {
161
+ name: "sandboxStart",
162
+ from: "createRequested",
163
+ to: "sandboxRunning"
164
+ },
165
+ {
166
+ name: "serviceStart",
167
+ from: "sandboxRunning",
168
+ to: "serviceReady"
169
+ },
170
+ {
171
+ name: "prompt",
172
+ from: "runtimeReady",
173
+ to: "promptSent"
174
+ },
175
+ {
176
+ name: "model",
177
+ from: "promptSent",
178
+ to: "firstOutput"
179
+ },
180
+ {
181
+ name: "pollLag",
182
+ from: "lastQuietPoll",
183
+ to: "firstOutput"
184
+ },
185
+ {
186
+ name: "finish",
187
+ from: "firstOutput",
188
+ to: "answerDone"
189
+ },
190
+ {
191
+ name: "post",
192
+ from: "answerDone",
193
+ to: "complete"
194
+ },
195
+ {
196
+ name: "speechQueue",
197
+ from: "complete",
198
+ to: "ttsStarted"
199
+ },
200
+ {
201
+ name: "speechFetch",
202
+ from: "ttsStarted",
203
+ to: "firstAudio"
204
+ },
205
+ {
206
+ name: "toFirstOutput",
207
+ from: "accepted",
208
+ to: "firstOutput"
209
+ },
210
+ {
211
+ name: "toFirstText",
212
+ from: "accepted",
213
+ to: "firstText"
214
+ },
215
+ {
216
+ name: "toAnswer",
217
+ from: "accepted",
218
+ to: "answerDone"
219
+ },
220
+ {
221
+ name: "toComplete",
222
+ from: "accepted",
223
+ to: "complete"
224
+ },
225
+ {
226
+ name: "toFirstAudio",
227
+ from: "accepted",
228
+ to: "firstAudio"
229
+ },
230
+ {
231
+ name: "processingToFirstAudio",
232
+ from: "graceEnded",
233
+ to: "firstAudio"
234
+ },
235
+ {
236
+ name: "heardAfterSpeaking",
237
+ from: "speechEnded",
238
+ to: "firstAudio"
239
+ }
240
+ ];
241
+ function _(e) {
242
+ let t = {};
243
+ for (let { name: n, from: r, to: i } of g) {
244
+ let a = e.at[r], o = e.at[i];
245
+ a !== void 0 && o !== void 0 && o >= a && (t[n] = o - a);
246
+ }
247
+ return t;
248
+ }
249
+ function v(e, t) {
250
+ return e[Math.max(1, Math.ceil(t * e.length)) - 1];
251
+ }
252
+ function y(e) {
253
+ if (!e.length) return;
254
+ let t = [...e].sort((e, t) => e - t), n = Math.floor(t.length / 2), r = t.length % 2 ? t[n] : (t[n - 1] + t[n]) / 2;
255
+ return {
256
+ n: t.length,
257
+ median: r,
258
+ p90: v(t, .9),
259
+ max: t[t.length - 1]
260
+ };
261
+ }
262
+ function b(e) {
263
+ return e.outcome === "answered" && !e.retried;
264
+ }
265
+ function x(e, t, n = b) {
266
+ let r = /* @__PURE__ */ new Map();
267
+ for (let i of e) {
268
+ if (!n(i)) continue;
269
+ let e = t(i), a = r.get(e) ?? /* @__PURE__ */ new Map();
270
+ r.set(e, a);
271
+ let o = _(i);
272
+ for (let { name: e } of g) {
273
+ let t = o[e];
274
+ if (t === void 0) continue;
275
+ let n = a.get(e) ?? [];
276
+ n.push(t), a.set(e, n);
277
+ }
278
+ }
279
+ let i = {};
280
+ for (let [e, t] of r) {
281
+ let n = {};
282
+ for (let [e, r] of t) {
283
+ let t = y(r);
284
+ t && (n[e] = t);
285
+ }
286
+ i[e] = n;
287
+ }
288
+ return i;
289
+ }
290
+ //#endregion
291
+ export { e as TURN_MILESTONES, g as TURN_SEGMENTS, n as TURN_TIMELINE_EVENT, l as beginTurn, d as describeTurn, o as detectTurnDevice, b as isComparableTurn, u as markTurn, m as recentTurnTimelines, h as resetTurnTimelines, f as restartAttempt, y as segmentStats, x as summariseTurnTimelines, a as turnClockMs, _ as turnSegments, p as turnTimeline };
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantChatArea.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantChatArea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,EAAE,EACR,MAAM,OAAO,CAAC;AAkGf,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAyDvE;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,oBAAoB,CAAC;CACpC;AAED,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAqgFxD,CAAC"}
1
+ {"version":3,"file":"AssistantChatArea.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantChatArea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,EAAE,EACR,MAAM,OAAO,CAAC;AAmHf,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAyDvE;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,oBAAoB,CAAC;CACpC;AAED,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAgqFxD,CAAC"}