@burdenoff/fe-libs 2026.825.7 → 2026.903.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.
@@ -12,4 +12,6 @@ export { isQuotaExhaustedError } from './gatewayClient';
12
12
  export { buildProgress, getAssistantMedia, sanitize, isRecoverable, mergeMessagesById, mapSessionToHistory, } from './progress';
13
13
  export { buildPromptRequestBody, findExistingSandbox, createAssistantSandbox, waitForSandboxRunning, waitForAssistantServiceReady, createAssistantSession, getAssistantSession, sendAssistantPromptAsync, getAssistantMessages, getAssistantStreamAccess, extendAssistantSandboxTTL, sendHeartbeat, getAssistantArtifacts, downloadAssistantArtifact, checkAssistantAiCredits, reportAssistantAiTokenUsage, } from './api';
14
14
  export type { AssistantMode, AssistantPromptPart, PageContext, SandboxAssistantConfig, SandboxAssistantTransport, AssistantSendArgs, PrewarmLevel, AssistantRawMessage, AssistantRawMessagePart, AssistantWidgetMessage, AssistantArtifact, AssistantSandboxAuthContext, AssistantSessionInfo, DownloadedAssistantArtifact, StreamAccess, } from './types';
15
+ export { isVoiceTurn, voiceSessionIdOf, voiceMarker, chatTurns, voiceTurnsOnly, groupBySession, groupConsecutiveBySession, } from './voice/turnVisibility';
16
+ export type { VoiceSessionGroup, VoiceTranscriptTurn } from './voice/turnVisibility';
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAKpB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,OAAO,CAAC;AAEf,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,EAC3B,oBAAoB,EACpB,2BAA2B,EAC3B,YAAY,GACb,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAKpB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,OAAO,CAAC;AAEf,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,EAC3B,oBAAoB,EACpB,2BAA2B,EAC3B,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,cAAc,EACd,cAAc,EACd,yBAAyB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Which surface does an assistant turn belong to — typed chat, or live voice?
3
+ *
4
+ * Shared by every product, for the same reason the transport is: voice was built
5
+ * as an overlay over the chat thread in the first product that needed it, so
6
+ * both surfaces write into ONE conversation. Without a marker there is no way to
7
+ * tell a spoken turn from a typed one once it is stored, and every voice
8
+ * exchange shows up in the typed thread.
9
+ *
10
+ * One marker fixes both directions:
11
+ * - the chat panel hides turns that happened in voice
12
+ * - the voice panel shows ONLY turns that happened in voice
13
+ *
14
+ * ★ Deliberately a DISPLAY split, not separate persistence. It survives a reload
15
+ * because the chat transcript is never rehydrated from the backend — the store is
16
+ * the only source of truth for what either panel shows. The backend session stays
17
+ * shared, so the model keeps full context across both surfaces, which is what
18
+ * makes "carry on where we left off" work. Genuinely separate backend sessions
19
+ * are a larger change and would cost that.
20
+ *
21
+ * ★ Product-agnostic by construction: it knows about turns and surfaces, never
22
+ * about crops, workflows or nodes. Nothing here needs to change for a product
23
+ * whose modes are `fluidgrids-node` rather than `api-calls`.
24
+ */
25
+ interface TurnLike {
26
+ metadata?: ({
27
+ voice?: boolean;
28
+ voiceSessionId?: string;
29
+ } & Record<string, unknown>) | undefined;
30
+ }
31
+ /** True when this turn happened inside a live voice session. */
32
+ export declare function isVoiceTurn(message: TurnLike): boolean;
33
+ /**
34
+ * WHICH voice session produced this turn, if we know.
35
+ *
36
+ * ★ `undefined` is a real answer, not a failure: every spoken turn recorded
37
+ * before session ids existed carries `voice: true` and nothing else, and those
38
+ * conversations are in users' localStorage today. It means "some earlier
39
+ * session" and must never be read as "not spoken" — `isVoiceTurn` remains the
40
+ * only thing that decides which surface a turn belongs to.
41
+ */
42
+ export declare function voiceSessionIdOf(message: TurnLike): string | undefined;
43
+ /**
44
+ * The marker as a fragment to spread into a message's `metadata`.
45
+ *
46
+ * ★ `patchMessage` REPLACES `metadata` instead of merging it, so every write
47
+ * that rebuilds the object has to re-assert this or the turn silently demotes
48
+ * itself to a chat turn. That is exactly what shipped: the marker was stamped
49
+ * at creation and destroyed the instant the reply completed, so a spoken answer
50
+ * appeared in the typed thread AND disappeared from the voice transcript at the
51
+ * same moment — the transcript could only ever show the user's questions.
52
+ *
53
+ * Spreading one helper at all four sites is what keeps them from drifting;
54
+ * `voiceMarkerCallSitesAreComplete` in the test file fails the build if a new
55
+ * metadata write forgets it.
56
+ *
57
+ * ★ Takes the SESSION ID rather than a boolean, and stamps both fields from it.
58
+ * There used to be a separate `dictated` flag that call sites passed straight
59
+ * through, which made it possible to mark a turn as spoken while recording no
60
+ * session — a state with no meaning that only the transcript would ever notice.
61
+ * One argument means the two can never disagree.
62
+ *
63
+ * An absent id yields an EMPTY object, so a typed turn gets no marker at all:
64
+ * `chatTurns` is allow-by-default, and a stray `voice: false` would be a second
65
+ * way of saying the same thing.
66
+ */
67
+ export declare function voiceMarker(sessionId: string | undefined): {
68
+ voice?: true;
69
+ voiceSessionId?: string;
70
+ };
71
+ /**
72
+ * The turns the CHAT panel should show: everything that was not spoken.
73
+ *
74
+ * Untagged turns are treated as chat, so every conversation recorded before the
75
+ * marker existed keeps rendering exactly as it did.
76
+ */
77
+ export declare function chatTurns<T extends TurnLike>(messages: readonly T[]): T[];
78
+ /**
79
+ * The turns the VOICE panel should show: only what was spoken.
80
+ *
81
+ * This is also what scopes the voice transcript to the session. Previously it
82
+ * took the last six messages of the whole conversation, so opening voice on an
83
+ * existing thread immediately painted typed replies — the wall of raw markdown
84
+ * in the operator's screenshot.
85
+ */
86
+ export declare function voiceTurnsOnly<T extends TurnLike>(messages: readonly T[]): T[];
87
+ /** One microphone-on-to-End, and the turns it produced, oldest first. */
88
+ export interface VoiceSessionGroup<T> {
89
+ /**
90
+ * `undefined` for turns recorded before session ids existed. Rendered as one
91
+ * leading group rather than dropped — it is real history the user spoke.
92
+ */
93
+ sessionId: string | undefined;
94
+ turns: T[];
95
+ }
96
+ /**
97
+ * Split spoken turns into the sessions that produced them.
98
+ *
99
+ * The transcript shows a conversation's whole spoken history, which is the more
100
+ * useful of the two options — you can go back to what you asked last time — but
101
+ * run together it reads as one long exchange, and a question from a session
102
+ * three days ago looks like it was just asked. Grouping is what makes showing
103
+ * everything honest.
104
+ *
105
+ * ★ Groups by RUNS, not by collecting every turn with the same id. Turns are
106
+ * stored in the order they happened, so a run IS a session; gathering by id
107
+ * instead would silently reorder history if two sessions ever interleaved, and
108
+ * would hide it rather than show it if they did.
109
+ *
110
+ * ★ Consecutive un-stamped turns collapse into a single leading group. Treating
111
+ * each as its own session would print a divider between every pre-existing turn
112
+ * — one long exchange shattered into fragments, which is worse than the
113
+ * undivided version this is meant to fix.
114
+ */
115
+ export declare function groupBySession<T extends TurnLike>(turns: readonly T[]): VoiceSessionGroup<T>[];
116
+ /**
117
+ * The same grouping for turns that have already been flattened for display.
118
+ *
119
+ * The transcript renders `{id, role, text}`, not `Message`, so it cannot reach
120
+ * `metadata`. Passing the extractor keeps ONE implementation of the run logic
121
+ * instead of a second copy that drifts — the subtle part is the `undefined`
122
+ * merge, and having it in two places is how one of them stops doing it.
123
+ */
124
+ export declare function groupConsecutiveBySession<T>(turns: readonly T[], sessionIdOf: (turn: T) => string | undefined): VoiceSessionGroup<T>[];
125
+ /**
126
+ * A spoken turn flattened for display.
127
+ *
128
+ * ★ Shared by every component between the chat area and the transcript, so the
129
+ * session id is carried in the TYPE and not merely at runtime. The intermediate
130
+ * props used to declare `{id, role, text}` while the objects flowing through
131
+ * happened to have more: it compiled, and the dividers worked, purely because
132
+ * JavaScript keeps the extra property. Any future refactor that rebuilt one of
133
+ * those objects would have dropped the id and silently run every session back
134
+ * together — the exact defect this field was added to fix, reintroduced with no
135
+ * type error.
136
+ */
137
+ export interface VoiceTranscriptTurn {
138
+ id: string;
139
+ role: 'user' | 'assistant';
140
+ text: string;
141
+ /**
142
+ * When the turn happened, ISO.
143
+ *
144
+ * Carried for the SAVED transcript rather than for the on-screen one: a file
145
+ * that outlives the session is read later, when "when did I ask that?" is the
146
+ * actual question, and a record without times answers it badly.
147
+ */
148
+ timestamp: string;
149
+ /**
150
+ * `undefined` on turns recorded before session ids existed.
151
+ *
152
+ * ★ REQUIRED-but-nullable rather than optional, and the difference is the
153
+ * whole point. With `sessionId?: string`, an object rebuilt without the key is
154
+ * still structurally assignable — so a refactor that mapped turns through and
155
+ * dropped it would compile, and every session would silently run back
156
+ * together. Written this way, omitting the key is a type error and passing
157
+ * `undefined` is a decision someone made on purpose.
158
+ */
159
+ sessionId: string | undefined;
160
+ }
161
+ export {};
162
+ //# sourceMappingURL=turnVisibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnVisibility.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/voice/turnVisibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,UAAU,QAAQ;IAChB,QAAQ,CAAC,EAAE,CAAC;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CACjG;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAEtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,KAAK,CAAC,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAEpG;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAE9E;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,CAAC,EAAE,CAAC;CACZ;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EACzC,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,GAC3C,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAWxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;;OASG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B"}
@@ -0,0 +1,36 @@
1
+ //#region src/shared/assistant/voice/turnVisibility.ts
2
+ function e(e) {
3
+ return e.metadata?.voice === !0;
4
+ }
5
+ function t(e) {
6
+ let t = e.metadata?.voiceSessionId;
7
+ return typeof t == "string" && t.length > 0 ? t : void 0;
8
+ }
9
+ function n(e) {
10
+ return e ? {
11
+ voice: !0,
12
+ voiceSessionId: e
13
+ } : {};
14
+ }
15
+ function r(t) {
16
+ return t.filter((t) => !e(t));
17
+ }
18
+ function i(t) {
19
+ return t.filter(e);
20
+ }
21
+ function a(e) {
22
+ return o(e, t);
23
+ }
24
+ function o(e, t) {
25
+ let n = [];
26
+ for (let r of e) {
27
+ let e = t(r), i = n[n.length - 1];
28
+ i && i.sessionId === e ? i.turns.push(r) : n.push({
29
+ sessionId: e,
30
+ turns: [r]
31
+ });
32
+ }
33
+ return n;
34
+ }
35
+ //#endregion
36
+ export { r as chatTurns, a as groupBySession, o as groupConsecutiveBySession, e as isVoiceTurn, n as voiceMarker, t as voiceSessionIdOf, i as voiceTurnsOnly };
@@ -4,4 +4,5 @@ import { AssistantRuntimeManager as _ } from "./shared/assistant/runtime.js";
4
4
  import { buildProgress as v, getAssistantMedia as y, isRecoverable as b, mapSessionToHistory as x, mergeMessagesById as S, sanitize as C } from "./shared/assistant/progress.js";
5
5
  import { createSandboxAssistantTransport as w } from "./shared/assistant/createSandboxAssistantTransport.js";
6
6
  import { useSandboxAssistantTransport as T } from "./shared/assistant/useSandboxAssistantTransport.js";
7
- export { _ as AssistantRuntimeManager, v as buildProgress, t as buildPromptRequestBody, n as checkAssistantAiCredits, r as createAssistantSandbox, i as createAssistantSession, w as createSandboxAssistantTransport, a as downloadAssistantArtifact, o as extendAssistantSandboxTTL, s as findExistingSandbox, c as getAssistantArtifacts, y as getAssistantMedia, l as getAssistantMessages, u as getAssistantSession, d as getAssistantStreamAccess, e as isQuotaExhaustedError, b as isRecoverable, x as mapSessionToHistory, S as mergeMessagesById, f as reportAssistantAiTokenUsage, C as sanitize, p as sendAssistantPromptAsync, m as sendHeartbeat, T as useSandboxAssistantTransport, h as waitForAssistantServiceReady, g as waitForSandboxRunning };
7
+ import { chatTurns as E, groupBySession as D, groupConsecutiveBySession as O, isVoiceTurn as k, voiceMarker as A, voiceSessionIdOf as j, voiceTurnsOnly as M } from "./shared/assistant/voice/turnVisibility.js";
8
+ export { _ as AssistantRuntimeManager, v as buildProgress, t as buildPromptRequestBody, E as chatTurns, n as checkAssistantAiCredits, r as createAssistantSandbox, i as createAssistantSession, w as createSandboxAssistantTransport, a as downloadAssistantArtifact, o as extendAssistantSandboxTTL, s as findExistingSandbox, c as getAssistantArtifacts, y as getAssistantMedia, l as getAssistantMessages, u as getAssistantSession, d as getAssistantStreamAccess, D as groupBySession, O as groupConsecutiveBySession, e as isQuotaExhaustedError, b as isRecoverable, k as isVoiceTurn, x as mapSessionToHistory, S as mergeMessagesById, f as reportAssistantAiTokenUsage, C as sanitize, p as sendAssistantPromptAsync, m as sendHeartbeat, T as useSandboxAssistantTransport, A as voiceMarker, j as voiceSessionIdOf, M as voiceTurnsOnly, h as waitForAssistantServiceReady, g as waitForSandboxRunning };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.825.7",
3
+ "version": "2026.903.1",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {
@@ -220,8 +220,14 @@
220
220
  "import": "./dist/shared/shims/*.js",
221
221
  "default": "./dist/shared/shims/*.js"
222
222
  },
223
- "./shared/styles/app-shell.css": "./src/shared/styles/app-shell.css",
224
- "./shared/styles/native.css": "./src/shared/styles/native.css",
223
+ "./shared/styles/app-shell.css": {
224
+ "style": "./src/shared/styles/app-shell.css",
225
+ "default": "./src/shared/styles/app-shell.css"
226
+ },
227
+ "./shared/styles/native.css": {
228
+ "style": "./src/shared/styles/native.css",
229
+ "default": "./src/shared/styles/native.css"
230
+ },
225
231
  "./shared/utils/lazyWithRetry": {
226
232
  "types": "./dist/shared/utils/lazyWithRetry.d.ts",
227
233
  "import": "./dist/shared/utils/lazyWithRetry.js"
@@ -296,6 +302,7 @@
296
302
  "lint:sanity": "if [ \"${CI:-}\" = \"true\" ]; then bun run lint; else bun run lint -- --cache --cache-location .eslintcache; fi",
297
303
  "format": "prettier --config shared/config/prettier.config.mjs --write \"src/**/*.{ts,tsx}\" \"!src/config/*.config.ts\"",
298
304
  "type:check": "bash scripts/build-limits.sh tsc --noEmit",
305
+ "test:package-css-exports": "node scripts/validate-package-css-exports.mjs",
299
306
  "test": "bun test scripts/graphql-schema-drift/__tests__ scripts/a11y-audit/__tests__ src/credentials src/storage src/shared/assistant src/shared/graphql/links/sse-link.test.ts src/shared/providers/AppShellProvider.test.ts src/shared/events/sinks/RybbitSink.test.ts src/shell-native src/shared/native src/chrome/__tests__ src/ui/__tests__",
300
307
  "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run lint:sanity && bun run format && bun run type:check && bun run test && bun run build'",
301
308
  "switch:status": "bun scripts/deps-mode.ts status",