@burdenoff/fe-libs 2026.926.4 → 2026.927.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/dist/chrome/ProductPickerSheet.d.ts.map +1 -1
- package/dist/chrome/ProductPickerSheet.js +45 -44
- package/dist/chrome/ProductSwitcher.d.ts.map +1 -1
- package/dist/chrome/ProductSwitcher.js +83 -82
- package/dist/shared/assistant/conversation.d.ts +91 -3
- package/dist/shared/assistant/conversation.d.ts.map +1 -1
- package/dist/shared/assistant/gatewayClient.d.ts +15 -0
- package/dist/shared/assistant/gatewayClient.d.ts.map +1 -1
- package/dist/shared/assistant/gatewayClient.js +1 -1
- package/dist/shared/assistant/guardedStorage.d.ts +4 -2
- package/dist/shared/assistant/guardedStorage.d.ts.map +1 -1
- package/dist/shared/assistant/guardedStorage.js +1 -1
- package/dist/shared/assistant/history/historyApi.d.ts +38 -0
- package/dist/shared/assistant/history/historyApi.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyApi.js +153 -0
- package/dist/shared/assistant/history/historyEngine.d.ts +96 -0
- package/dist/shared/assistant/history/historyEngine.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyEngine.js +774 -0
- package/dist/shared/assistant/history/historyErrors.d.ts +72 -0
- package/dist/shared/assistant/history/historyErrors.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyErrors.js +74 -0
- package/dist/shared/assistant/history/historyList.d.ts +115 -0
- package/dist/shared/assistant/history/historyList.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyList.js +116 -0
- package/dist/shared/assistant/history/historyMapping.d.ts +65 -0
- package/dist/shared/assistant/history/historyMapping.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyMapping.js +383 -0
- package/dist/shared/assistant/history/historyMerge.d.ts +65 -0
- package/dist/shared/assistant/history/historyMerge.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyMerge.js +97 -0
- package/dist/shared/assistant/history/historyOperations.d.ts +24 -0
- package/dist/shared/assistant/history/historyOperations.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyOperations.js +125 -0
- package/dist/shared/assistant/history/historyResume.d.ts +56 -0
- package/dist/shared/assistant/history/historyResume.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyResume.js +55 -0
- package/dist/shared/assistant/history/historyStatus.d.ts +76 -0
- package/dist/shared/assistant/history/historyStatus.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyStatus.js +47 -0
- package/dist/shared/assistant/history/historyStore.d.ts +192 -0
- package/dist/shared/assistant/history/historyStore.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyStore.js +503 -0
- package/dist/shared/assistant/history/historyTimestamps.d.ts +11 -0
- package/dist/shared/assistant/history/historyTimestamps.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyTimestamps.js +9 -0
- package/dist/shared/assistant/history/historyTypes.d.ts +256 -0
- package/dist/shared/assistant/history/historyTypes.d.ts.map +1 -0
- package/dist/shared/assistant/history/historyTypes.js +20 -0
- package/dist/shared/assistant/history/profileBoundStorage.d.ts +36 -0
- package/dist/shared/assistant/history/profileBoundStorage.d.ts.map +1 -0
- package/dist/shared/assistant/history/profileBoundStorage.js +74 -0
- package/dist/shared/assistant/history/stampRegistry.d.ts +84 -0
- package/dist/shared/assistant/history/stampRegistry.d.ts.map +1 -0
- package/dist/shared/assistant/history/stampRegistry.js +58 -0
- package/dist/shared/assistant/history/useAssistantServerHistory.d.ts +18 -0
- package/dist/shared/assistant/history/useAssistantServerHistory.d.ts.map +1 -0
- package/dist/shared/assistant/history/useAssistantServerHistory.js +12 -0
- package/dist/shared/assistant/i18nManifest.d.ts +18 -0
- package/dist/shared/assistant/i18nManifest.d.ts.map +1 -1
- package/dist/shared/assistant/i18nManifest.js +18 -0
- package/dist/shared/assistant/index.d.ts +16 -0
- package/dist/shared/assistant/index.d.ts.map +1 -1
- package/dist/shared/assistant/liveTurns.d.ts +7 -0
- package/dist/shared/assistant/liveTurns.d.ts.map +1 -1
- package/dist/shared/assistant/liveTurns.js +35 -32
- package/dist/shared/assistant/spokenTurns.d.ts +20 -0
- package/dist/shared/assistant/spokenTurns.d.ts.map +1 -1
- package/dist/shared/assistant/spokenTurns.js +13 -8
- package/dist/shared/assistant/store.d.ts +35 -1
- package/dist/shared/assistant/store.d.ts.map +1 -1
- package/dist/shared/assistant/store.js +93 -24
- package/dist/shared/assistant/ui/AIAssistantPanel.d.ts +16 -0
- package/dist/shared/assistant/ui/AIAssistantPanel.d.ts.map +1 -1
- package/dist/shared/assistant/ui/AIAssistantPanel.js +63 -60
- package/dist/shared/assistant/ui/AssistantChatArea.d.ts.map +1 -1
- package/dist/shared/assistant/ui/AssistantChatArea.js +730 -672
- package/dist/shared/assistant/ui/AssistantConversationList.d.ts +17 -0
- package/dist/shared/assistant/ui/AssistantConversationList.d.ts.map +1 -1
- package/dist/shared/assistant/ui/AssistantConversationList.js +252 -145
- package/dist/shared/assistant/ui/AssistantHistoryNotices.d.ts +36 -0
- package/dist/shared/assistant/ui/AssistantHistoryNotices.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantHistoryNotices.js +103 -0
- package/dist/shared/assistant/ui/AssistantOverlay.d.ts +4 -0
- package/dist/shared/assistant/ui/AssistantOverlay.d.ts.map +1 -1
- package/dist/shared/assistant/ui/AssistantOverlay.js +97 -94
- package/dist/shared/assistant/ui/AssistantSpokenTurnsNotice.d.ts +5 -3
- package/dist/shared/assistant/ui/AssistantSpokenTurnsNotice.d.ts.map +1 -1
- package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts +6 -0
- package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts.map +1 -1
- package/dist/shared/assistant/ui/composer/AssistantComposer.js +54 -54
- package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.d.ts +9 -0
- package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.d.ts.map +1 -1
- package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.js +37 -32
- package/dist/shared/assistant/voice/turnVisibility.d.ts +7 -5
- package/dist/shared/assistant/voice/turnVisibility.d.ts.map +1 -1
- package/dist/shared/assistant/voice/voiceTranscript.d.ts +3 -2
- package/dist/shared/assistant/voice/voiceTranscript.d.ts.map +1 -1
- package/dist/shared/config/index.d.ts +3 -1
- package/dist/shared/config/index.d.ts.map +1 -1
- package/dist/shared/config/productSlug.d.ts +36 -0
- package/dist/shared/config/productSlug.d.ts.map +1 -0
- package/dist/shared/config/productSlug.js +23 -0
- package/dist/shared/config/products.d.ts +8 -1
- package/dist/shared/config/products.d.ts.map +1 -1
- package/dist/shared/config/products.js +21 -14
- package/dist/shared/hooks/shell/useProducts.d.ts +8 -0
- package/dist/shared/hooks/shell/useProducts.d.ts.map +1 -1
- package/dist/shared/hooks/shell/useProducts.js +46 -42
- package/dist/shared/providers/AppShellProvider.d.ts.map +1 -1
- package/dist/shared/providers/AppShellProvider.js +228 -227
- package/dist/shared/providers/shell/ThemeProvider.d.ts +1 -1
- package/dist/shared/providers/shell/ThemeProvider.d.ts.map +1 -1
- package/dist/shared/providers/shell/ThemeProvider.js +58 -57
- package/dist/shared/providers/shell/brandAttribute.d.ts +11 -0
- package/dist/shared/providers/shell/brandAttribute.d.ts.map +1 -0
- package/dist/shared/providers/shell/brandAttribute.js +7 -0
- package/dist/shared-assistant.js +153 -137
- package/dist/shared-config.js +11 -10
- package/dist/shared.js +121 -120
- package/package.json +2 -2
- package/src/vite/types.ts +4 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-backed assistant history (BOFF-7335): the shapes shared by the
|
|
3
|
+
* transport, the mapper, the persisted history store and the engine.
|
|
4
|
+
*
|
|
5
|
+
* ★★ The WIRE types mirror the wspace-conversations `my*` assistant-history
|
|
6
|
+
* fields as BUILT — `history/__fixtures__/assistantHistory.sdl.graphql` is a
|
|
7
|
+
* vendored copy of that SDL, and `historySdlContract.test.ts` validates every
|
|
8
|
+
* operation the client sends against it. When the two disagree, the server is
|
|
9
|
+
* right and this file changes.
|
|
10
|
+
*
|
|
11
|
+
* Product-neutral by construction: a product is only ever the string the
|
|
12
|
+
* product itself declared (`AssistantProductConfig.product`), passed through.
|
|
13
|
+
*/
|
|
14
|
+
/** Whose history a request reads or writes: one account, in one workspace, in one product. */
|
|
15
|
+
export interface AssistantHistoryScope {
|
|
16
|
+
actorId: string;
|
|
17
|
+
workspaceId: string;
|
|
18
|
+
/** Sent to the server untouched as `productId`. */
|
|
19
|
+
product: string;
|
|
20
|
+
}
|
|
21
|
+
/** `${actorId}|${workspaceId}|${product}` — see {@link assistantHistoryScopeKey}. */
|
|
22
|
+
export type AssistantHistoryScopeKey = string;
|
|
23
|
+
/**
|
|
24
|
+
* A conversation's key in the history store: its `clientConversationId` (the
|
|
25
|
+
* local conversation id), or `legacy:<serverId>` for a conversation saved
|
|
26
|
+
* before v2, which has no client id.
|
|
27
|
+
*/
|
|
28
|
+
export type AssistantHistoryConvKey = string;
|
|
29
|
+
export declare const LEGACY_HISTORY_KEY_PREFIX = "legacy:";
|
|
30
|
+
export declare function assistantHistoryScopeKey(scope: AssistantHistoryScope): AssistantHistoryScopeKey;
|
|
31
|
+
/** A complete scope, or null when any part is missing (history is then off, never "partly on"). */
|
|
32
|
+
export declare function completeAssistantHistoryScope(actorId: string | null | undefined, workspaceId: string | null | undefined, product: string | null | undefined): AssistantHistoryScope | null;
|
|
33
|
+
export declare function legacyHistoryConvKey(serverId: string): AssistantHistoryConvKey;
|
|
34
|
+
export declare function isLegacyHistoryConvKey(key: AssistantHistoryConvKey): boolean;
|
|
35
|
+
/** Exactly one of the two: a client-minted id, or a legacy row's server id. */
|
|
36
|
+
export type AssistantHistoryConvRef = {
|
|
37
|
+
clientConversationId: string;
|
|
38
|
+
} | {
|
|
39
|
+
conversationId: string;
|
|
40
|
+
};
|
|
41
|
+
export type AssistantHistoryEntryKind = "TURN" | "LOCAL_COMMAND" | "NOTE";
|
|
42
|
+
export type AssistantHistoryRole = "USER" | "ASSISTANT" | "NOTE";
|
|
43
|
+
export type AssistantHistorySaveOutcome = "APPLIED" | "ALREADY_APPLIED" | "CONVERSATION_DELETED";
|
|
44
|
+
export type AssistantHistoryDeleteOutcome = "DELETED" | "ALREADY_DELETED" | "TOMBSTONED_UNSEEN" | "NOT_PRESENT";
|
|
45
|
+
/** `AssistantHistoryConversation`, with its DateTimes normalised to ISO strings. */
|
|
46
|
+
export interface AssistantHistoryServerConversation {
|
|
47
|
+
id: string;
|
|
48
|
+
/** Null for a conversation saved before v2 (read-only). */
|
|
49
|
+
clientConversationId: string | null;
|
|
50
|
+
/** The product as the request named it (a renamed product's alias is echoed back as sent). */
|
|
51
|
+
productId: string;
|
|
52
|
+
/** Null once the conversation is deleted: a deleted chat's title is never served. */
|
|
53
|
+
title: string | null;
|
|
54
|
+
legacy: boolean;
|
|
55
|
+
revision: number;
|
|
56
|
+
lastMessageSeq: number;
|
|
57
|
+
deleted: boolean;
|
|
58
|
+
deletedAt: string | null;
|
|
59
|
+
createdAt: string;
|
|
60
|
+
updatedAt: string;
|
|
61
|
+
}
|
|
62
|
+
/** `AssistantHistoryEntry`, with `createdAt` normalised to an ISO string. */
|
|
63
|
+
export interface AssistantHistoryServerEntry {
|
|
64
|
+
id: string;
|
|
65
|
+
/** Null for a message saved before v2. */
|
|
66
|
+
clientMessageId: string | null;
|
|
67
|
+
kind: AssistantHistoryEntryKind | null;
|
|
68
|
+
role: AssistantHistoryRole;
|
|
69
|
+
content: string;
|
|
70
|
+
seq: number | null;
|
|
71
|
+
createdAt: string;
|
|
72
|
+
/** The client's own timestamp, exactly as it was sent (or null when the server clamped it away). */
|
|
73
|
+
clientCreatedAt: string | null;
|
|
74
|
+
/** The allow-listed metadata, verbatim. Untrusted input to `fromHistoryEntry`, never executed. */
|
|
75
|
+
metadata: Record<string, unknown> | null;
|
|
76
|
+
}
|
|
77
|
+
export interface AssistantHistoryPageInfo {
|
|
78
|
+
hasNextPage: boolean;
|
|
79
|
+
hasPreviousPage: boolean;
|
|
80
|
+
startCursor: string | null;
|
|
81
|
+
endCursor: string | null;
|
|
82
|
+
}
|
|
83
|
+
/** `myAssistantHistoryChanges`: every change in commit order, tombstones included. */
|
|
84
|
+
export interface AssistantHistoryChangePage {
|
|
85
|
+
nodes: AssistantHistoryServerConversation[];
|
|
86
|
+
pageInfo: AssistantHistoryPageInfo;
|
|
87
|
+
/** The cursor predates retained tombstones: drop synced state and pull from the start. */
|
|
88
|
+
resetRequired: boolean;
|
|
89
|
+
}
|
|
90
|
+
/** `myAssistantConversations`: live conversations, most recently changed first. */
|
|
91
|
+
export interface AssistantHistoryHeaderPage {
|
|
92
|
+
nodes: AssistantHistoryServerConversation[];
|
|
93
|
+
pageInfo: AssistantHistoryPageInfo;
|
|
94
|
+
}
|
|
95
|
+
/** `myAssistantConversationEntries`: nodes are always ascending by `seq`. */
|
|
96
|
+
export interface AssistantHistoryEntryPage {
|
|
97
|
+
conversation: AssistantHistoryServerConversation;
|
|
98
|
+
nodes: AssistantHistoryServerEntry[];
|
|
99
|
+
pageInfo: AssistantHistoryPageInfo;
|
|
100
|
+
}
|
|
101
|
+
/** `AssistantHistoryEntryInput`. `metadata` is already allow-listed by `toHistoryEntries`. */
|
|
102
|
+
export interface AssistantHistoryEntryInput {
|
|
103
|
+
clientMessageId: string;
|
|
104
|
+
role: AssistantHistoryRole;
|
|
105
|
+
content: string;
|
|
106
|
+
clientCreatedAt?: string;
|
|
107
|
+
metadata?: Record<string, unknown>;
|
|
108
|
+
}
|
|
109
|
+
/** A save, minus `productId` — the transport takes that from the scope, never from the caller. */
|
|
110
|
+
export interface AssistantHistorySaveInput {
|
|
111
|
+
clientConversationId: string;
|
|
112
|
+
kind: AssistantHistoryEntryKind;
|
|
113
|
+
entries: AssistantHistoryEntryInput[];
|
|
114
|
+
}
|
|
115
|
+
export interface AssistantHistorySavePayload {
|
|
116
|
+
outcome: AssistantHistorySaveOutcome;
|
|
117
|
+
conversation: AssistantHistoryServerConversation;
|
|
118
|
+
entries: AssistantHistoryServerEntry[];
|
|
119
|
+
/** `<entryIndex>.<key>` (or `<entryIndex>.<key>.<path>.pendingWrite`) for what the server did not store. */
|
|
120
|
+
droppedMetadataKeys: string[];
|
|
121
|
+
}
|
|
122
|
+
export interface AssistantHistoryDeletePayload {
|
|
123
|
+
outcome: AssistantHistoryDeleteOutcome;
|
|
124
|
+
conversation: AssistantHistoryServerConversation | null;
|
|
125
|
+
}
|
|
126
|
+
/** Paging for `myAssistantConversationEntries`: backward (`last`/`before`) OR forward (`first`/`after`/`sinceSeq`), never both. */
|
|
127
|
+
export type AssistantHistoryEntryPaging = {
|
|
128
|
+
last?: number;
|
|
129
|
+
before?: string;
|
|
130
|
+
} | {
|
|
131
|
+
first?: number;
|
|
132
|
+
after?: string;
|
|
133
|
+
sinceSeq?: number;
|
|
134
|
+
};
|
|
135
|
+
/** What the list shows for a server conversation that is not (yet) materialised locally. */
|
|
136
|
+
export interface AssistantHistoryHeader {
|
|
137
|
+
serverId: string;
|
|
138
|
+
clientConversationId: string | null;
|
|
139
|
+
title: string | null;
|
|
140
|
+
legacy: boolean;
|
|
141
|
+
revision: number;
|
|
142
|
+
lastMessageSeq: number;
|
|
143
|
+
createdAt: string;
|
|
144
|
+
updatedAt: string;
|
|
145
|
+
/**
|
|
146
|
+
* The server answered NOT_FOUND for this conversation (it never had it in
|
|
147
|
+
* this scope), so the list hides it. NOT a delete: nothing is tombstoned,
|
|
148
|
+
* and a feed node with a HIGHER revision makes it visible again. At the same
|
|
149
|
+
* revision `gone` wins a merge, so another tab cannot quietly bring it back.
|
|
150
|
+
*/
|
|
151
|
+
gone?: true;
|
|
152
|
+
}
|
|
153
|
+
export interface AssistantHistoryScopeState {
|
|
154
|
+
/**
|
|
155
|
+
* How many times this scope was reset (`resetRequired`). A merge keeps the
|
|
156
|
+
* cursor and headers of the HIGHER generation wholesale — a union would put
|
|
157
|
+
* back exactly what the reset threw away. Absent means 0.
|
|
158
|
+
*/
|
|
159
|
+
gen?: number;
|
|
160
|
+
/** The feed's opaque `endCursor`, and the revision it reached (merges keep the higher). */
|
|
161
|
+
cursor: string | null;
|
|
162
|
+
cursorRev: number;
|
|
163
|
+
headers: Record<AssistantHistoryConvKey, AssistantHistoryHeader>;
|
|
164
|
+
/** Headers were dropped to the cache bound; "Show older conversations" pages the live list. */
|
|
165
|
+
hasOlderHeaders?: true;
|
|
166
|
+
pulledAt?: number;
|
|
167
|
+
/**
|
|
168
|
+
* Epoch ms of this generation's reset (`resetRequired`), kept after its
|
|
169
|
+
* re-pull finishes: everything held since was read after it, so for a
|
|
170
|
+
* while a further `resetRequired` cannot mean a missed delete (the engine's
|
|
171
|
+
* feed says how long). Absent for a scope never reset.
|
|
172
|
+
*/
|
|
173
|
+
resetAt?: number;
|
|
174
|
+
/** That re-pull has pages still to read: after a reload it resumes, and still removes what it did not return. */
|
|
175
|
+
rescanning?: true;
|
|
176
|
+
}
|
|
177
|
+
export interface AssistantHistoryTombstone {
|
|
178
|
+
scopeKey: AssistantHistoryScopeKey;
|
|
179
|
+
/** Epoch ms it was recorded here. */
|
|
180
|
+
at: number;
|
|
181
|
+
/** The server has confirmed the delete (any outcome), or reported it deleted. */
|
|
182
|
+
confirmed: boolean;
|
|
183
|
+
serverId?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* A save carries a SNAPSHOT of the entries, built from the store at commit
|
|
187
|
+
* time — never message ids to look up later — so it survives the main store
|
|
188
|
+
* losing the conversation (another tab's last write wins there).
|
|
189
|
+
*/
|
|
190
|
+
export interface AssistantHistorySaveItem {
|
|
191
|
+
id: string;
|
|
192
|
+
type: "save";
|
|
193
|
+
scopeKey: AssistantHistoryScopeKey;
|
|
194
|
+
convKey: AssistantHistoryConvKey;
|
|
195
|
+
kind: AssistantHistoryEntryKind;
|
|
196
|
+
entries: AssistantHistoryEntryInput[];
|
|
197
|
+
attempts: number;
|
|
198
|
+
nextAttemptAt?: number;
|
|
199
|
+
parked?: "quota";
|
|
200
|
+
}
|
|
201
|
+
export interface AssistantHistoryDeleteItem {
|
|
202
|
+
id: string;
|
|
203
|
+
type: "delete";
|
|
204
|
+
scopeKey: AssistantHistoryScopeKey;
|
|
205
|
+
convKey: AssistantHistoryConvKey;
|
|
206
|
+
serverId?: string;
|
|
207
|
+
legacy: boolean;
|
|
208
|
+
attempts: number;
|
|
209
|
+
nextAttemptAt?: number;
|
|
210
|
+
}
|
|
211
|
+
export type AssistantHistoryOutboxItem = AssistantHistorySaveItem | AssistantHistoryDeleteItem;
|
|
212
|
+
/**
|
|
213
|
+
* ★★ `v` is this build's FORMAT. A later build keeps it at 1 only for ADDITIVE
|
|
214
|
+
* changes; anything else takes a higher `v`, and this build then never writes
|
|
215
|
+
* the key (nor reads it). At `v: 1`, what this build does with what it does
|
|
216
|
+
* not read (see historyStore):
|
|
217
|
+
*
|
|
218
|
+
* PRESERVED, exactly as the key holds it at each write:
|
|
219
|
+
* - a new top-level key;
|
|
220
|
+
* - an outbox item of a new type or save kind, in its place in the queue,
|
|
221
|
+
* until that build finishes it (`doneItemIds`);
|
|
222
|
+
* - a new field on a scope, a tombstone, a header or a known outbox item.
|
|
223
|
+
* Wherever the key holds that record, the field is the key's — at every
|
|
224
|
+
* write and every read of another tab's write — so a value the later
|
|
225
|
+
* build changes stays changed and a field it deletes stays deleted, even
|
|
226
|
+
* in a tab of this build that read the old ones. This build never writes
|
|
227
|
+
* such a field the key does not hold, except on a record the key no
|
|
228
|
+
* longer holds at all, which it writes back as it last read it.
|
|
229
|
+
*
|
|
230
|
+
* NOT preserved:
|
|
231
|
+
* - a new field on a header this build replaces with a higher revision (a
|
|
232
|
+
* newer copy of the server's record replaces the old one whole, and this
|
|
233
|
+
* build's own pulls carry none) — so a header's new fields must describe
|
|
234
|
+
* that revision only;
|
|
235
|
+
* - a new field on a scope this build resets (`resetRequired` starts it
|
|
236
|
+
* again from nothing), or on a scope a copy of a higher `gen` replaces;
|
|
237
|
+
* - a record merely left out of the key: this build keeps what it holds
|
|
238
|
+
* (a union), so a later build removes one only by the facts this build
|
|
239
|
+
* reads — `doneItemIds`, a tombstone, a higher `gen`, `gone`;
|
|
240
|
+
* - a new field NESTED inside a field this build reads (inside a save's
|
|
241
|
+
* `entries`, say): it goes with that field's value, which this build's
|
|
242
|
+
* rules choose (the more-tried copy; at equal attempts, this tab's);
|
|
243
|
+
* - a field this build reads, given another type or meaning: it is read
|
|
244
|
+
* as it always was — a scope's or tombstone's field of another type is
|
|
245
|
+
* dropped, a tombstone or header lacking a required one is dropped whole,
|
|
246
|
+
* and an outbox item lacking one is carried as an item of unknown type.
|
|
247
|
+
*/
|
|
248
|
+
export interface AssistantHistoryPersisted {
|
|
249
|
+
v: 1;
|
|
250
|
+
scopes: Record<AssistantHistoryScopeKey, AssistantHistoryScopeState>;
|
|
251
|
+
outbox: AssistantHistoryOutboxItem[];
|
|
252
|
+
/** Outbox item id -> epoch ms it was acknowledged. Kept so a merge from a stale tab cannot revive it. */
|
|
253
|
+
doneItemIds: Record<string, number>;
|
|
254
|
+
tombstones: Record<AssistantHistoryConvKey, AssistantHistoryTombstone>;
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=historyTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historyTypes.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/history/historyTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/shared/assistant/history/historyTypes.ts
|
|
2
|
+
var e = "legacy:";
|
|
3
|
+
function t(e) {
|
|
4
|
+
return `${e.actorId}|${e.workspaceId}|${e.product}`;
|
|
5
|
+
}
|
|
6
|
+
function n(e, t, n) {
|
|
7
|
+
return !e || !t || !n ? null : {
|
|
8
|
+
actorId: e,
|
|
9
|
+
workspaceId: t,
|
|
10
|
+
product: n
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function r(t) {
|
|
14
|
+
return `${e}${t}`;
|
|
15
|
+
}
|
|
16
|
+
function i(t) {
|
|
17
|
+
return t.startsWith(e);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { e as LEGACY_HISTORY_KEY_PREFIX, t as assistantHistoryScopeKey, n as completeAssistantHistoryScope, i as isLegacyHistoryConvKey, r as legacyHistoryConvKey };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { StateStorage } from 'zustand/middleware';
|
|
2
|
+
export interface ProfileBoundStorage extends StateStorage {
|
|
3
|
+
/** Synchronous: binds to the active profile's key (see the module docblock). */
|
|
4
|
+
getItem(name: string): string | null;
|
|
5
|
+
/** Forget the bound key: writes are refused until the next read binds one (the only way to change profile). */
|
|
6
|
+
unbind(): void;
|
|
7
|
+
/** The storage key the state was read from, or null while unbound. */
|
|
8
|
+
boundKey(): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* What the bound key holds right now, WITHOUT binding: null while unbound or
|
|
11
|
+
* once the profile has changed. A write merges with this first, so another
|
|
12
|
+
* tab's write that this one has not folded in yet is never overwritten.
|
|
13
|
+
*/
|
|
14
|
+
peekItem(name: string): string | null;
|
|
15
|
+
/** Bound, and `name` still resolves to the bound key (the active profile has not changed). */
|
|
16
|
+
isCurrent(name: string): boolean;
|
|
17
|
+
/** The key `name` would bind to right now, or null with no active profile. Reads nothing. */
|
|
18
|
+
activeKey(name: string): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* The browser refused the latest write (a full or blocked storage): what is
|
|
21
|
+
* in memory is not saved on this device. False again once a write succeeds.
|
|
22
|
+
* A write refused for the profile (unbound, or changed) is not counted — it
|
|
23
|
+
* was never meant to land.
|
|
24
|
+
*/
|
|
25
|
+
lastWriteFailed(): boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface ProfileBoundStorageOptions {
|
|
28
|
+
/** Resolve the profile-scoped key for a base name (default: the app's active profile). */
|
|
29
|
+
resolveKey?: (name: string) => string;
|
|
30
|
+
/** The storage to use (default: `window.localStorage`, when there is one). */
|
|
31
|
+
storage?: () => Pick<Storage, "getItem" | "setItem" | "removeItem"> | null;
|
|
32
|
+
/** Told once per refusal streak, and about every failed write. */
|
|
33
|
+
onRefused?: (reason: "unbound" | "profile-changed" | "write-failed", error?: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
export declare function createProfileBoundStorage(options?: ProfileBoundStorageOptions): ProfileBoundStorage;
|
|
36
|
+
//# sourceMappingURL=profileBoundStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profileBoundStorage.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/history/profileBoundStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,gFAAgF;IAChF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACrC,+GAA+G;IAC/G,MAAM,IAAI,IAAI,CAAC;IACf,sEAAsE;IACtE,QAAQ,IAAI,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,8FAA8F;IAC9F,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,6FAA6F;IAC7F,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC;;;;;OAKG;IACH,eAAe,IAAI,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,0FAA0F;IAC1F,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC;IAC3E,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,iBAAiB,GAAG,cAAc,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/F;AAUD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,mBAAmB,CAyGrB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { getProfileScopedKey as e } from "../../utils/profileScopedStorage.js";
|
|
2
|
+
//#region src/shared/assistant/history/profileBoundStorage.ts
|
|
3
|
+
function t() {
|
|
4
|
+
try {
|
|
5
|
+
return typeof localStorage > "u" ? null : localStorage;
|
|
6
|
+
} catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function n(n = {}) {
|
|
11
|
+
let r = n.resolveKey ?? e, i = n.storage ?? t, a = null, o = !1, s = !1, c = (e, t) => {
|
|
12
|
+
(e === "write-failed" || !o) && (o = !0, n.onRefused?.(e, t));
|
|
13
|
+
}, l = (e) => {
|
|
14
|
+
try {
|
|
15
|
+
let t = r(e);
|
|
16
|
+
return t === e ? null : t;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}, u = (e) => a === null ? (c("unbound"), null) : l(e) === a ? a : (c("profile-changed"), null);
|
|
21
|
+
return {
|
|
22
|
+
getItem(e) {
|
|
23
|
+
let t = l(e);
|
|
24
|
+
if (a !== null && t !== a) return c("profile-changed"), null;
|
|
25
|
+
if (a = t, o = !1, t === null) return null;
|
|
26
|
+
try {
|
|
27
|
+
return i()?.getItem(t) ?? null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
setItem(e, t) {
|
|
33
|
+
let n = u(e);
|
|
34
|
+
if (n !== null) try {
|
|
35
|
+
i()?.setItem(n, t), s = !1;
|
|
36
|
+
} catch (e) {
|
|
37
|
+
s = !0, c("write-failed", e);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
removeItem(e) {
|
|
41
|
+
let t = u(e);
|
|
42
|
+
if (t !== null) try {
|
|
43
|
+
i()?.removeItem(t), s = !1;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
s = !0, c("write-failed", e);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
peekItem(e) {
|
|
49
|
+
if (a === null || l(e) !== a) return null;
|
|
50
|
+
try {
|
|
51
|
+
return i()?.getItem(a) ?? null;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
isCurrent(e) {
|
|
57
|
+
return a !== null && l(e) === a;
|
|
58
|
+
},
|
|
59
|
+
activeKey(e) {
|
|
60
|
+
return l(e);
|
|
61
|
+
},
|
|
62
|
+
unbind() {
|
|
63
|
+
a = null, s = !1;
|
|
64
|
+
},
|
|
65
|
+
lastWriteFailed() {
|
|
66
|
+
return s;
|
|
67
|
+
},
|
|
68
|
+
boundKey() {
|
|
69
|
+
return a;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { n as createProfileBoundStorage };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seams between the main assistant store and server history (BOFF-7335).
|
|
3
|
+
*
|
|
4
|
+
* ★★ A LEAF: this module imports nothing. The main store (`../store`) reads
|
|
5
|
+
* it and the history engine writes it, so neither has to import the other —
|
|
6
|
+
* the store stays loadable in every product, and in every test, without the
|
|
7
|
+
* engine, its transport or its persisted store.
|
|
8
|
+
*
|
|
9
|
+
* Four seams, each empty until the engine is enabled — so with server history
|
|
10
|
+
* off (every product that has not opted in) the store behaves exactly as it
|
|
11
|
+
* did before this module existed:
|
|
12
|
+
*
|
|
13
|
+
* - the STAMP: who a conversation created right now belongs to. The store
|
|
14
|
+
* stamps a new conversation with it (`Conversation.sync`), and only then is
|
|
15
|
+
* that conversation ever saved. No source, or a source that cannot name the
|
|
16
|
+
* product, the account AND the workspace, means device-only.
|
|
17
|
+
* - STORAGE FULL: the main store's persisted write was refused for space. The
|
|
18
|
+
* engine frees space by evicting server-backed conversations it can load
|
|
19
|
+
* again; the eviction's own write is the retry.
|
|
20
|
+
* - BUSY: a conversation that must not be rewritten under the person right now
|
|
21
|
+
* (a live voice session in it). The panel registers this; the engine checks
|
|
22
|
+
* it, together with the turn-in-flight state it reads from the store, before
|
|
23
|
+
* it merges loaded messages or evicts.
|
|
24
|
+
* - OPEN IN PLACE: which conversation may open when the open one leaves the
|
|
25
|
+
* store (deleted here or elsewhere). The store used to open the first one
|
|
26
|
+
* left, whoever it belonged to — another workspace's, which the list hides
|
|
27
|
+
* and whose turns would then be sent from this workspace.
|
|
28
|
+
*/
|
|
29
|
+
/** The owner a conversation created now is stamped with. */
|
|
30
|
+
export interface AssistantHistoryStamp {
|
|
31
|
+
product: string;
|
|
32
|
+
workspaceId: string;
|
|
33
|
+
actorId: string;
|
|
34
|
+
}
|
|
35
|
+
/** Install (or, with null, remove) where new conversations' stamps come from. */
|
|
36
|
+
export declare function setHistoryStampSource(source: (() => AssistantHistoryStamp | null) | null): void;
|
|
37
|
+
/**
|
|
38
|
+
* The stamp for a conversation created now, or null: history off, or any part
|
|
39
|
+
* of the owner unknown. Never throws — a source that fails means device-only.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getHistoryStamp(): AssistantHistoryStamp | null;
|
|
42
|
+
/**
|
|
43
|
+
* Told when the main store's write was refused. `stillFailing` reports whether
|
|
44
|
+
* the most recent write attempt (including one the handler itself caused) was
|
|
45
|
+
* refused too.
|
|
46
|
+
*/
|
|
47
|
+
export type AssistantStorageFullHandler = (stillFailing: () => boolean) => void;
|
|
48
|
+
export declare function setAssistantStorageFullHandler(handler: AssistantStorageFullHandler | null): void;
|
|
49
|
+
/** Called by the main store's guarded storage; a no-op while history is off. */
|
|
50
|
+
export declare function notifyAssistantStorageFull(stillFailing: () => boolean): void;
|
|
51
|
+
/**
|
|
52
|
+
* The panel's answer to "is something live in this conversation right now" (a
|
|
53
|
+
* voice session). Returns its release, which clears the probe only while it is
|
|
54
|
+
* still this one — a panel unmounting after another mounted leaves the newer
|
|
55
|
+
* panel's probe in place.
|
|
56
|
+
*/
|
|
57
|
+
export declare function setHistoryBusyProbe(probe: ((conversationId: string) => boolean) | null): () => void;
|
|
58
|
+
/** True when the registered probe says so. A probe that throws answers "busy" — the safe side. */
|
|
59
|
+
export declare function isHistoryConversationBusy(conversationId: string): boolean;
|
|
60
|
+
/** What the store knows of a conversation when it asks whether that one may open. */
|
|
61
|
+
export interface AssistantHistoryOpenCandidate {
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly sync?: {
|
|
64
|
+
readonly product: string;
|
|
65
|
+
readonly workspaceId: string;
|
|
66
|
+
readonly actorId: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Decides, for one removal, which conversations may open in place of the one that went. */
|
|
70
|
+
export type AssistantHistoryOpenFilter = (candidate: AssistantHistoryOpenCandidate) => boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Install (or, with null, remove) the rule for what may open in place of a
|
|
73
|
+
* removed conversation. A source, not a filter: it is asked once per removal,
|
|
74
|
+
* so it can read the active scope once rather than once per conversation.
|
|
75
|
+
*/
|
|
76
|
+
export declare function setHistoryOpenFilterSource(source: (() => AssistantHistoryOpenFilter) | null): void;
|
|
77
|
+
/**
|
|
78
|
+
* The rule for this removal: any conversation with history off (as before),
|
|
79
|
+
* otherwise the source's. A source or filter that throws leaves only the
|
|
80
|
+
* device's own conversations — never another workspace's, at worst an empty
|
|
81
|
+
* panel.
|
|
82
|
+
*/
|
|
83
|
+
export declare function historyOpenFilter(): AssistantHistoryOpenFilter;
|
|
84
|
+
//# sourceMappingURL=stampRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stampRegistry.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/history/stampRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAE/F;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,GAAG,IAAI,CAQ9D;AAED;;;;GAIG;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,YAAY,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAIhF,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,GAAG,IAAI,CAEhG;AAED,gFAAgF;AAChF,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,OAAO,GAAG,IAAI,CAE5E;AAID;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,CAAC,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,IAAI,GAClD,MAAM,IAAI,CAKZ;AAED,kGAAkG;AAClG,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAOzE;AAED,qFAAqF;AACrF,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,4FAA4F;AAC5F,MAAM,MAAM,0BAA0B,GAAG,CAAC,SAAS,EAAE,6BAA6B,KAAK,OAAO,CAAC;AAI/F;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,CAAC,MAAM,0BAA0B,CAAC,GAAG,IAAI,GAAG,IAAI,CAElG;AAKD;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,0BAA0B,CAe9D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//#region src/shared/assistant/history/stampRegistry.ts
|
|
2
|
+
var e = null;
|
|
3
|
+
function t(t) {
|
|
4
|
+
e = t;
|
|
5
|
+
}
|
|
6
|
+
function n() {
|
|
7
|
+
if (!e) return null;
|
|
8
|
+
try {
|
|
9
|
+
let t = e();
|
|
10
|
+
return t && t.product && t.workspaceId && t.actorId ? t : null;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
var r = null;
|
|
16
|
+
function i(e) {
|
|
17
|
+
r = e;
|
|
18
|
+
}
|
|
19
|
+
function a(e) {
|
|
20
|
+
r?.(e);
|
|
21
|
+
}
|
|
22
|
+
var o = null;
|
|
23
|
+
function s(e) {
|
|
24
|
+
return o = e, () => {
|
|
25
|
+
o === e && (o = null);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function c(e) {
|
|
29
|
+
if (!o) return !1;
|
|
30
|
+
try {
|
|
31
|
+
return o(e);
|
|
32
|
+
} catch {
|
|
33
|
+
return !0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
var l = null;
|
|
37
|
+
function u(e) {
|
|
38
|
+
l = e;
|
|
39
|
+
}
|
|
40
|
+
var d = () => !0, f = (e) => !e.sync;
|
|
41
|
+
function p() {
|
|
42
|
+
if (!l) return d;
|
|
43
|
+
let e;
|
|
44
|
+
try {
|
|
45
|
+
e = l();
|
|
46
|
+
} catch {
|
|
47
|
+
return f;
|
|
48
|
+
}
|
|
49
|
+
return (t) => {
|
|
50
|
+
try {
|
|
51
|
+
return e(t);
|
|
52
|
+
} catch {
|
|
53
|
+
return f(t);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { n as getHistoryStamp, p as historyOpenFilter, c as isHistoryConversationBusy, a as notifyAssistantStorageFull, i as setAssistantStorageFullHandler, s as setHistoryBusyProbe, u as setHistoryOpenFilterSource, t as setHistoryStampSource };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt this product's panel into server-backed history (BOFF-7335, contract §6).
|
|
3
|
+
*
|
|
4
|
+
* ★★ OFF unless asked. A product turns it on with the panel's `serverHistory`
|
|
5
|
+
* prop; with it off (the default) nothing is stamped, queued, pulled or read,
|
|
6
|
+
* and the panel behaves exactly as it did before server history existed.
|
|
7
|
+
*
|
|
8
|
+
* The engine is a module-level singleton, and this only holds it on: enabling
|
|
9
|
+
* is ref-counted, so the overlay (which keeps it on while the panel is closed)
|
|
10
|
+
* and the panel itself can both ask, and it switches off when the last one
|
|
11
|
+
* lets go. The engine watches the account, workspace and profile itself.
|
|
12
|
+
*
|
|
13
|
+
* ★ The product id is the one the product provider already gives — the same
|
|
14
|
+
* string the server stores as `productId` — so no product names itself twice.
|
|
15
|
+
* With no product there is nothing to scope the history to, and it stays off.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useAssistantServerHistory(enabled: boolean): void;
|
|
18
|
+
//# sourceMappingURL=useAssistantServerHistory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAssistantServerHistory.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/history/useAssistantServerHistory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAMhE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useAssistantProductOrNull as e } from "../product.js";
|
|
2
|
+
import { assistantHistory as t } from "./historyEngine.js";
|
|
3
|
+
import { useEffect as n } from "react";
|
|
4
|
+
//#region src/shared/assistant/history/useAssistantServerHistory.ts
|
|
5
|
+
function r(r) {
|
|
6
|
+
let i = e()?.product ?? "";
|
|
7
|
+
n(() => {
|
|
8
|
+
if (!(!r || !i)) return t.enable(i);
|
|
9
|
+
}, [r, i]);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { r as useAssistantServerHistory };
|
|
@@ -180,6 +180,24 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
|
|
|
180
180
|
readonly "header.preview": "Preview";
|
|
181
181
|
readonly "header.ready": "Ready";
|
|
182
182
|
readonly "header.title": "AI Assistant";
|
|
183
|
+
readonly "history.badgeLoading": "Loading…";
|
|
184
|
+
readonly "history.badgeNotSaved": "Not saved";
|
|
185
|
+
readonly "history.badgeReadOnly": "Read-only";
|
|
186
|
+
readonly "history.deleteNotSaved": "This device's storage is full, so that delete isn't saved here yet. Keep this page open until it reaches the server.";
|
|
187
|
+
readonly "history.deletedElsewhere": "The conversation you had open was deleted on another device.";
|
|
188
|
+
readonly "history.dismiss": "Dismiss";
|
|
189
|
+
readonly "history.groupDevice": "On this device";
|
|
190
|
+
readonly "history.groupWorkspace": "This workspace";
|
|
191
|
+
readonly "history.legacyReadOnly": "This conversation was saved by an earlier version of the assistant. You can read it, but it can't be continued.";
|
|
192
|
+
readonly "history.loadFailed": "Couldn't load this conversation.";
|
|
193
|
+
readonly "history.loading": "Loading conversation…";
|
|
194
|
+
readonly "history.notSavedInvalid": "Part of this conversation couldn't be saved. It stays on this device.";
|
|
195
|
+
readonly "history.notSavedQuota": "Not saved yet: this workspace has reached its conversation limit. It will be saved when there is room.";
|
|
196
|
+
readonly "history.notSavedScope": "Some turns were sent from another workspace. They stay on this device.";
|
|
197
|
+
readonly "history.showEarlier": "Show earlier messages";
|
|
198
|
+
readonly "history.showOlder": "Show older conversations";
|
|
199
|
+
readonly "history.tryAgain": "Try again";
|
|
200
|
+
readonly "history.untitled": "Untitled conversation";
|
|
183
201
|
readonly "language.inputLabel": "Language";
|
|
184
202
|
readonly "language.replyLabel": "Assistant replies in";
|
|
185
203
|
readonly "language.replyMatch": "Match my language";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18nManifest.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/i18nManifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAU1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B
|
|
1
|
+
{"version":3,"file":"i18nManifest.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/i18nManifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAU1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoQG,CAAC;AAE5C,mDAAmD;AACnD,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,2BAA2B,CAAC;AAW/E;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBtE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIjE,CAAC;AAEL,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,mBAAmB,EACnB,IAAI,GAAG,OAAO,GAAG,mBAAmB,CACrC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,uBAAuB,EAAE,GACxC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC1C,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC9C,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiCzB"}
|
|
@@ -75,6 +75,24 @@ var u = {
|
|
|
75
75
|
"header.preview": "Preview",
|
|
76
76
|
"header.ready": "Ready",
|
|
77
77
|
"header.title": "AI Assistant",
|
|
78
|
+
"history.badgeLoading": "Loading…",
|
|
79
|
+
"history.badgeNotSaved": "Not saved",
|
|
80
|
+
"history.badgeReadOnly": "Read-only",
|
|
81
|
+
"history.deleteNotSaved": "This device's storage is full, so that delete isn't saved here yet. Keep this page open until it reaches the server.",
|
|
82
|
+
"history.deletedElsewhere": "The conversation you had open was deleted on another device.",
|
|
83
|
+
"history.dismiss": "Dismiss",
|
|
84
|
+
"history.groupDevice": "On this device",
|
|
85
|
+
"history.groupWorkspace": "This workspace",
|
|
86
|
+
"history.legacyReadOnly": "This conversation was saved by an earlier version of the assistant. You can read it, but it can't be continued.",
|
|
87
|
+
"history.loadFailed": "Couldn't load this conversation.",
|
|
88
|
+
"history.loading": "Loading conversation…",
|
|
89
|
+
"history.notSavedInvalid": "Part of this conversation couldn't be saved. It stays on this device.",
|
|
90
|
+
"history.notSavedQuota": "Not saved yet: this workspace has reached its conversation limit. It will be saved when there is room.",
|
|
91
|
+
"history.notSavedScope": "Some turns were sent from another workspace. They stay on this device.",
|
|
92
|
+
"history.showEarlier": "Show earlier messages",
|
|
93
|
+
"history.showOlder": "Show older conversations",
|
|
94
|
+
"history.tryAgain": "Try again",
|
|
95
|
+
"history.untitled": "Untitled conversation",
|
|
78
96
|
"language.inputLabel": "Language",
|
|
79
97
|
"language.replyLabel": "Assistant replies in",
|
|
80
98
|
"language.replyMatch": "Match my language",
|