ltcai 7.6.0 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +39 -34
  2. package/docs/CHANGELOG.md +39 -0
  3. package/frontend/src/App.tsx +4 -2
  4. package/frontend/src/api/client.ts +9 -2
  5. package/frontend/src/components/LivingBrain.tsx +9 -1
  6. package/frontend/src/components/ProductFlow.tsx +27 -3
  7. package/frontend/src/components/onboarding/RecommendationScreen.tsx +33 -26
  8. package/frontend/src/features/brain/BrainConversation.tsx +214 -94
  9. package/frontend/src/features/brain/BrainHome.tsx +32 -28
  10. package/frontend/src/features/review/ReviewInbox.tsx +2 -2
  11. package/frontend/src/features/review/reviewHelpers.ts +11 -11
  12. package/frontend/src/i18n.ts +162 -26
  13. package/frontend/src/pages/Act.tsx +28 -14
  14. package/frontend/src/pages/Ask.tsx +11 -2
  15. package/frontend/src/pages/Brain.tsx +12 -2
  16. package/frontend/src/routes.ts +6 -6
  17. package/frontend/src/styles.css +592 -25
  18. package/lattice_brain/__init__.py +1 -1
  19. package/lattice_brain/runtime/multi_agent.py +1 -1
  20. package/latticeai/__init__.py +1 -1
  21. package/latticeai/core/marketplace.py +1 -1
  22. package/latticeai/core/workspace_os.py +1 -1
  23. package/latticeai/services/architecture_readiness.py +25 -7
  24. package/latticeai/services/product_readiness.py +159 -0
  25. package/package.json +1 -1
  26. package/scripts/product_readiness.py +37 -0
  27. package/src-tauri/Cargo.lock +1 -1
  28. package/src-tauri/Cargo.toml +1 -1
  29. package/src-tauri/tauri.conf.json +1 -1
  30. package/static/app/asset-manifest.json +28 -28
  31. package/static/app/assets/{Act-CSeeIWB4.js → Act-DOvf59ru.js} +2 -2
  32. package/static/app/assets/{Act-CSeeIWB4.js.map → Act-DOvf59ru.js.map} +1 -1
  33. package/static/app/assets/Brain-C7_0mEiI.js +322 -0
  34. package/static/app/assets/Brain-C7_0mEiI.js.map +1 -0
  35. package/static/app/assets/Capture-B9Tlhzqr.js +2 -0
  36. package/static/app/assets/Capture-B9Tlhzqr.js.map +1 -0
  37. package/static/app/assets/Library-BJPEEm5O.js +2 -0
  38. package/static/app/assets/{Library-C4zmA8O2.js.map → Library-BJPEEm5O.js.map} +1 -1
  39. package/static/app/assets/System-D6t9jo9V.js +2 -0
  40. package/static/app/assets/{System-Da-Kxwiz.js.map → System-D6t9jo9V.js.map} +1 -1
  41. package/static/app/assets/index-C7g26IF6.css +2 -0
  42. package/static/app/assets/index-DbcEYJQ2.js +17 -0
  43. package/static/app/assets/index-DbcEYJQ2.js.map +1 -0
  44. package/static/app/assets/primitives-CD38lt4n.js +2 -0
  45. package/static/app/assets/{primitives-CSsF_Ymb.js.map → primitives-CD38lt4n.js.map} +1 -1
  46. package/static/app/assets/textarea-BZk6ybp5.js +2 -0
  47. package/static/app/assets/{textarea-CJMFSyfQ.js.map → textarea-BZk6ybp5.js.map} +1 -1
  48. package/static/app/index.html +2 -2
  49. package/frontend/src/components/BrainConversation.tsx +0 -533
  50. package/frontend/src/components/FirstRunGuide.tsx +0 -99
  51. package/static/app/assets/Brain-D_Ne4YoR.js +0 -322
  52. package/static/app/assets/Brain-D_Ne4YoR.js.map +0 -1
  53. package/static/app/assets/Capture-YFRAO4bJ.js +0 -2
  54. package/static/app/assets/Capture-YFRAO4bJ.js.map +0 -1
  55. package/static/app/assets/Library-C4zmA8O2.js +0 -2
  56. package/static/app/assets/System-Da-Kxwiz.js +0 -2
  57. package/static/app/assets/index-BwmCpRoW.css +0 -2
  58. package/static/app/assets/index-D1hsexMt.js +0 -17
  59. package/static/app/assets/index-D1hsexMt.js.map +0 -1
  60. package/static/app/assets/primitives-CSsF_Ymb.js +0 -2
  61. package/static/app/assets/textarea-CJMFSyfQ.js +0 -2
@@ -1,5 +1,20 @@
1
1
  import * as React from "react";
2
- import { Cpu, DatabaseZap, FileText, FileUp, FolderPlus, Globe2, Loader2, Repeat2, Search, Settings, ShieldCheck, Sparkles } from "lucide-react";
2
+ import {
3
+ BrainCircuit,
4
+ CheckCircle2,
5
+ Cpu,
6
+ DatabaseZap,
7
+ FileText,
8
+ FileUp,
9
+ FolderPlus,
10
+ Globe2,
11
+ HardDrive,
12
+ Loader2,
13
+ Repeat2,
14
+ Search,
15
+ ShieldCheck,
16
+ Sparkles,
17
+ } from "lucide-react";
3
18
 
4
19
  const INGESTION_TYPE_LABEL_KEY: Record<IngestionSourceType, string> = {
5
20
  file: "brain.ingest.type.file",
@@ -28,7 +43,6 @@ import { BrainOverviewPanel } from "./BrainOverviewPanel";
28
43
 
29
44
  export function BrainConversation({
30
45
  language,
31
- explorationDepth,
32
46
  modelName,
33
47
  messages,
34
48
  starterPrompts,
@@ -84,113 +98,219 @@ export function BrainConversation({
84
98
  return (
85
99
  <section className="brain-conversation" aria-label={t(language, "brain.aria.conversation")}>
86
100
  <div className="brain-conversation-header">
87
- <div>
88
- <h1>{t(language, "brain.title")}</h1>
89
- <span>{t(language, `brain.depth.${explorationDepth}`)}</span>
90
- </div>
91
- <LanguageSwitcher compact />
92
- <div className="brain-ownership-strip" aria-label={t(language, "brain.aria.ownership")}>
93
- <span>{t(language, "brain.local")}</span>
94
- <span>{t(language, "brain.portable")}</span>
95
- <span>{t(language, "brain.private")}</span>
101
+ <div className="brain-conversation-title">
102
+ <h1>
103
+ {t(language, "brain.title")}
104
+ <span className="brain-edition-badge" title={t(language, "brain.edition.tip")}>
105
+ {t(language, "brain.edition")}
106
+ </span>
107
+ </h1>
108
+ <span>{t(language, "brain.chatHome.headerLine")}</span>
96
109
  </div>
97
- <div className="brain-flow-actions" aria-label={t(language, "brain.aria.actions")}>
98
- <button type="button" onClick={() => navigateHash("/capture")}>
99
- <FileUp className="h-3.5 w-3.5" />
100
- {t(language, "brain.action.add")}
101
- </button>
102
- <button type="button" onClick={() => navigateHash("/knowledge-graph")}>
103
- <Search className="h-3.5 w-3.5" />
104
- {t(language, "brain.action.find")}
105
- </button>
106
- <button type="button" onClick={() => navigateHash("/models")}>
107
- <Cpu className="h-3.5 w-3.5" />
108
- {t(language, "brain.action.model")}
109
- </button>
110
- <button type="button" onClick={() => navigateHash("/settings")}>
111
- <Settings className="h-3.5 w-3.5" />
112
- {t(language, "brain.action.settings")}
110
+ <div className="brain-header-tools">
111
+ <LanguageSwitcher compact />
112
+ <div className="brain-model-pill">{modelName}</div>
113
+ <button className="brain-admin-link" type="button" onClick={() => navigateHash("/admin")}>
114
+ <ShieldCheck className="h-3.5 w-3.5" />
115
+ {t(language, "brain.admin")}
113
116
  </button>
114
117
  </div>
115
- <div className="brain-model-pill">{modelName}</div>
116
- <button className="brain-admin-link" type="button" onClick={() => navigateHash("/admin")}>
117
- <ShieldCheck className="h-3.5 w-3.5" />
118
- {t(language, "brain.admin")}
119
- </button>
120
118
  </div>
121
119
 
122
- <div ref={streamRef} className="brain-stream">
123
- <BrainIngestionPanel
124
- language={language}
125
- uploadingDocument={uploadingDocument}
126
- ingestionStates={ingestionStates}
127
- onUploadDocument={onUploadDocument}
128
- onConnectFolder={onConnectFolder}
129
- onIngestNote={onIngestNote}
130
- onIngestWeb={onIngestWeb}
131
- />
132
- <IngestionTimelineSection language={language} emergenceEvents={emergenceEvents} />
133
- <BrainOverviewPanel
134
- memories={memories}
135
- concepts={concepts}
136
- readiness={readiness}
137
- proof={proof}
138
- onOpenDepth={onOpenDepth}
139
- />
140
- <ModelContinuityDemo
141
- language={language}
142
- proof={proof}
143
- modelName={modelName}
144
- onVerify={onVerifyModelContinuity}
145
- />
146
- {messages.length === 0 ? (
147
- <BrainEmptyState
120
+ <div className="brain-chat-home-layout">
121
+ <section className="brain-chat-home-card" aria-label={t(language, "brain.chatHome.aria")}>
122
+ <div className="brain-chat-home-head">
123
+ <div>
124
+ <span>{t(language, "brain.chatHome.kicker")}</span>
125
+ <h2>{t(language, "brain.chatHome.title")}</h2>
126
+ <p>{t(language, "brain.chatHome.body")}</p>
127
+ </div>
128
+ <div className="brain-chat-home-proof" aria-label={t(language, "brain.aria.ownership")}>
129
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.local")}</span>
130
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.private")}</span>
131
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.portable")}</span>
132
+ </div>
133
+ </div>
134
+
135
+ <div ref={streamRef} className="brain-stream">
136
+ {messages.length === 0 ? (
137
+ <BrainEmptyState
138
+ language={language}
139
+ starterPrompts={starterPrompts}
140
+ uploadingDocument={uploadingDocument}
141
+ onDraftChange={onDraftChange}
142
+ onUploadDocument={onUploadDocument}
143
+ />
144
+ ) : (
145
+ messages.map((message, index) => {
146
+ const messageId = `brain-msg-${index}`;
147
+ const proof = message.role === "assistant" ? message.proof : undefined;
148
+ return (
149
+ <div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
150
+ <div className="brain-message-bubble">
151
+ {message.content}
152
+ {proof && proof.citations.length ? (
153
+ <InlineCitationMarkers language={language} proof={proof} messageId={messageId} />
154
+ ) : null}
155
+ </div>
156
+ {proof ? <AnswerProofCard language={language} proof={proof} messageId={messageId} /> : null}
157
+ </div>
158
+ );
159
+ })
160
+ )}
161
+ </div>
162
+
163
+ {memoryFeedback ? (
164
+ <div className="brain-save-feedback" role="status">
165
+ <DatabaseZap className="h-3.5 w-3.5" />
166
+ <span>{memoryFeedback}</span>
167
+ <small>{t(language, "brain.saved.detail")}</small>
168
+ </div>
169
+ ) : null}
170
+
171
+ <BrainComposer
148
172
  language={language}
149
- starterPrompts={starterPrompts}
173
+ draft={draft}
174
+ streaming={streaming}
175
+ imageData={imageData}
150
176
  uploadingDocument={uploadingDocument}
151
177
  onDraftChange={onDraftChange}
178
+ onImageDataChange={onImageDataChange}
152
179
  onUploadDocument={onUploadDocument}
180
+ onSend={onSend}
153
181
  />
154
- ) : (
155
- messages.map((message, index) => {
156
- const messageId = `brain-msg-${index}`;
157
- const proof = message.role === "assistant" ? message.proof : undefined;
158
- return (
159
- <div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
160
- <div className="brain-message-bubble">
161
- {message.content}
162
- {proof && proof.citations.length ? (
163
- <InlineCitationMarkers language={language} proof={proof} messageId={messageId} />
164
- ) : null}
165
- </div>
166
- {proof ? <AnswerProofCard language={language} proof={proof} messageId={messageId} /> : null}
167
- </div>
168
- );
169
- })
170
- )}
182
+
183
+ <details className="brain-utility-drawer">
184
+ <summary>{t(language, "brain.chatHome.utility")}</summary>
185
+ <div className="brain-utility-grid">
186
+ <BrainIngestionPanel
187
+ language={language}
188
+ uploadingDocument={uploadingDocument}
189
+ ingestionStates={ingestionStates}
190
+ onUploadDocument={onUploadDocument}
191
+ onConnectFolder={onConnectFolder}
192
+ onIngestNote={onIngestNote}
193
+ onIngestWeb={onIngestWeb}
194
+ />
195
+ <ProductCommandCenter
196
+ language={language}
197
+ readiness={readiness}
198
+ proof={proof}
199
+ modelName={modelName}
200
+ memories={memories}
201
+ concepts={concepts}
202
+ emergenceEvents={emergenceEvents}
203
+ onOpenDepth={onOpenDepth}
204
+ onVerifyModelContinuity={onVerifyModelContinuity}
205
+ />
206
+ <IngestionTimelineSection language={language} emergenceEvents={emergenceEvents} />
207
+ <ModelContinuityDemo
208
+ language={language}
209
+ proof={proof}
210
+ modelName={modelName}
211
+ onVerify={onVerifyModelContinuity}
212
+ />
213
+ <BrainOverviewPanel
214
+ memories={memories}
215
+ concepts={concepts}
216
+ readiness={readiness}
217
+ proof={proof}
218
+ onOpenDepth={onOpenDepth}
219
+ />
220
+ <BrainCarePanel language={language} />
221
+ </div>
222
+ </details>
223
+ </section>
171
224
  </div>
225
+ </section>
226
+ );
227
+ }
228
+
229
+ function ProductCommandCenter({
230
+ language,
231
+ readiness,
232
+ proof,
233
+ modelName,
234
+ memories,
235
+ concepts,
236
+ emergenceEvents,
237
+ onOpenDepth,
238
+ onVerifyModelContinuity,
239
+ }: {
240
+ language: Language;
241
+ readiness: BrainReadiness;
242
+ proof: BrainProof;
243
+ modelName: string;
244
+ memories: MemoryFragment[];
245
+ concepts: KnowledgeConcept[];
246
+ emergenceEvents: EmergenceEvent[];
247
+ onOpenDepth: (depth: BrainDepth) => void;
248
+ onVerifyModelContinuity: () => void;
249
+ }) {
250
+ const score = Math.max(0, Math.min(100, readiness.score));
251
+ const nextKey =
252
+ readiness.state === "alive"
253
+ ? "brain.command.next.alive"
254
+ : readiness.state === "forming"
255
+ ? "brain.command.next.forming"
256
+ : "brain.command.next.empty";
257
+ const recallable = (proof.recall && proof.recall.items) ? proof.recall.items.length : (proof.proofs ? (proof.proofs.durableItems || 1) : 1);
258
+ const latestSource = emergenceEvents[0]?.label ?? t(language, "brain.command.source.empty");
172
259
 
173
- {memoryFeedback ? (
174
- <div className="brain-save-feedback" role="status">
175
- <DatabaseZap className="h-3.5 w-3.5" />
176
- <span>{memoryFeedback}</span>
177
- <small>{t(language, "brain.saved.detail")}</small>
260
+ return (
261
+ <section className="brain-command-center" aria-label={t(language, "brain.command.aria")}>
262
+ <div className="brain-command-head">
263
+ <div>
264
+ <span>{t(language, "brain.command.kicker")}</span>
265
+ <strong>{t(language, "brain.command.title")}</strong>
178
266
  </div>
179
- ) : null}
267
+ <div className="brain-command-score" role="meter" aria-valuemin={0} aria-valuemax={100} aria-valuenow={score}>
268
+ <span>{t(language, "brain.command.score")}</span>
269
+ <strong>{score}%</strong>
270
+ </div>
271
+ </div>
180
272
 
181
- <BrainCarePanel language={language} />
273
+ <div className="brain-command-next">
274
+ <BrainCircuit className="h-4 w-4" />
275
+ <span>{t(language, "brain.command.next")}</span>
276
+ <strong>{t(language, nextKey)}</strong>
277
+ </div>
182
278
 
183
- <BrainComposer
184
- language={language}
185
- draft={draft}
186
- streaming={streaming}
187
- imageData={imageData}
188
- uploadingDocument={uploadingDocument}
189
- onDraftChange={onDraftChange}
190
- onImageDataChange={onImageDataChange}
191
- onUploadDocument={onUploadDocument}
192
- onSend={onSend}
193
- />
279
+ <div className="brain-command-metrics" aria-label={t(language, "brain.command.metrics")}>
280
+ <span>{t(language, "brain.command.metric.memories", { count: memories.length })}</span>
281
+ <span>{t(language, "brain.command.metric.topics", { count: concepts.length })}</span>
282
+ <span>{t(language, "brain.command.metric.sources", { count: readiness.signals.healthySources })}</span>
283
+ <span>{t(language, "brain.command.metric.proof", { count: recallable })}</span>
284
+ </div>
285
+
286
+ <div className="brain-command-actions">
287
+ <button type="button" aria-label={t(language, "brain.command.action.add")} onClick={() => navigateHash("/capture")}>
288
+ <FileUp className="h-4 w-4" />
289
+ <span>{t(language, "brain.command.action.add")}</span>
290
+ <small>{latestSource}</small>
291
+ </button>
292
+ <button type="button" aria-label={t(language, "brain.command.action.find")} onClick={() => onOpenDepth(3)}>
293
+ <Search className="h-4 w-4" />
294
+ <span>{t(language, "brain.command.action.find")}</span>
295
+ <small>{t(language, "brain.command.action.find.detail")}</small>
296
+ </button>
297
+ <button type="button" aria-label={t(language, "brain.command.action.proof")} onClick={onVerifyModelContinuity}>
298
+ <Repeat2 className="h-4 w-4" />
299
+ <span>{t(language, "brain.command.action.proof")}</span>
300
+ <small>{proof.modelContinuity.proven ? proof.modelContinuity.activeModel || modelName : modelName}</small>
301
+ </button>
302
+ <button type="button" aria-label={t(language, "brain.command.action.own")} onClick={() => navigateHash("/settings")}>
303
+ <HardDrive className="h-4 w-4" />
304
+ <span>{t(language, "brain.command.action.own")}</span>
305
+ <small>{t(language, "brain.command.action.own.detail")}</small>
306
+ </button>
307
+ </div>
308
+
309
+ <div className="brain-command-signals">
310
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.command.signal.local")}</span>
311
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.command.signal.private")}</span>
312
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.command.signal.portable")}</span>
313
+ </div>
194
314
  </section>
195
315
  );
196
316
  }
@@ -494,34 +494,38 @@ export function BrainHome({
494
494
  onInteract={deepen}
495
495
  />
496
496
 
497
- <BrainDepthRings explorationDepth={explorationDepth} onOpenDepth={jumpToDepth} />
498
-
499
- <div className="brain-depth-badge" aria-live="polite">
500
- <span>{t(language, "brain.level")} {explorationDepth}</span>
501
- <strong>{t(language, `brain.depth.${explorationDepth}`)}</strong>
502
- </div>
503
-
504
- <div className="brain-depth-actions" aria-label={t(language, "brain.aria.quickViews")}>
505
- <button type="button" className={explorationDepth === 2 ? "is-active" : ""} onClick={() => jumpToDepth(2)}>{t(language, "brain.view.memories")}</button>
506
- <button type="button" className={explorationDepth === 3 ? "is-active" : ""} onClick={() => jumpToDepth(3)}>{t(language, "brain.view.topics")}</button>
507
- <button type="button" className={explorationDepth === 4 ? "is-active" : ""} onClick={() => jumpToDepth(4)}>{t(language, "brain.view.relationships")}</button>
508
- <button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
509
- </div>
510
-
511
- <div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
512
- {DEPTHS.map((depth) => (
513
- <button
514
- key={depth.level}
515
- type="button"
516
- className={depth.level <= explorationDepth ? "is-revealed" : ""}
517
- aria-current={depth.level === explorationDepth ? "step" : undefined}
518
- onClick={() => jumpToDepth(depth.level)}
519
- >
520
- <span>{depth.level}</span>
521
- <strong>{t(language, `brain.depth.${depth.level}`)}</strong>
522
- </button>
523
- ))}
524
- </div>
497
+ {explorationDepth > 1 ? (
498
+ <>
499
+ <BrainDepthRings explorationDepth={explorationDepth} onOpenDepth={jumpToDepth} />
500
+
501
+ <div className="brain-depth-badge" aria-live="polite">
502
+ <span>{t(language, "brain.level")} {explorationDepth}</span>
503
+ <strong>{t(language, `brain.depth.${explorationDepth}`)}</strong>
504
+ </div>
505
+
506
+ <div className="brain-depth-actions" aria-label={t(language, "brain.aria.quickViews")}>
507
+ <button type="button" className={explorationDepth === 2 ? "is-active" : ""} onClick={() => jumpToDepth(2)}>{t(language, "brain.view.memories")}</button>
508
+ <button type="button" className={explorationDepth === 3 ? "is-active" : ""} onClick={() => jumpToDepth(3)}>{t(language, "brain.view.topics")}</button>
509
+ <button type="button" className={explorationDepth === 4 ? "is-active" : ""} onClick={() => jumpToDepth(4)}>{t(language, "brain.view.relationships")}</button>
510
+ <button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
511
+ </div>
512
+
513
+ <div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
514
+ {DEPTHS.map((depth) => (
515
+ <button
516
+ key={depth.level}
517
+ type="button"
518
+ className={depth.level <= explorationDepth ? "is-revealed" : ""}
519
+ aria-current={depth.level === explorationDepth ? "step" : undefined}
520
+ onClick={() => jumpToDepth(depth.level)}
521
+ >
522
+ <span>{depth.level}</span>
523
+ <strong>{t(language, `brain.depth.${depth.level}`)}</strong>
524
+ </button>
525
+ ))}
526
+ </div>
527
+ </>
528
+ ) : null}
525
529
 
526
530
  <div className="brain-field-layer" aria-hidden={explorationDepth < 2}>
527
531
  <DepthEmergence
@@ -87,12 +87,12 @@ export function ReviewInbox() {
87
87
  </div>
88
88
  <div className="grid gap-2">
89
89
  <Tabs
90
- tabs={reviewStatusFilters}
90
+ tabs={reviewStatusFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
91
91
  value={statusFilter}
92
92
  onChange={(id) => setStatusFilter(id as ReviewStatusFilter)}
93
93
  />
94
94
  <Tabs
95
- tabs={reviewSourceFilters}
95
+ tabs={reviewSourceFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
96
96
  value={sourceFilter}
97
97
  onChange={(id) => setSourceFilter(id as ReviewSourceFilter)}
98
98
  />
@@ -4,19 +4,19 @@ import type { Badge } from "@/components/ui/badge";
4
4
 
5
5
  export type ReviewAction = "approve" | "dismiss" | "snooze" | "unsnooze" | "run_now";
6
6
 
7
- export const reviewStatusFilters: Array<{ id: ReviewStatusFilter; label: string }> = [
8
- { id: "pending", label: "Pending" },
9
- { id: "snoozed", label: "Snoozed" },
10
- { id: "all", label: "All" },
11
- { id: "approved", label: "Approved" },
12
- { id: "dismissed", label: "Dismissed" },
7
+ export const reviewStatusFilters: Array<{ id: ReviewStatusFilter; labelKey: string }> = [
8
+ { id: "pending", labelKey: "review.filter.status.pending" },
9
+ { id: "snoozed", labelKey: "review.filter.status.snoozed" },
10
+ { id: "all", labelKey: "review.filter.status.all" },
11
+ { id: "approved", labelKey: "review.filter.status.approved" },
12
+ { id: "dismissed", labelKey: "review.filter.status.dismissed" },
13
13
  ];
14
14
 
15
- export const reviewSourceFilters: Array<{ id: ReviewSourceFilter; label: string }> = [
16
- { id: "all", label: "All sources" },
17
- { id: "workflow_run", label: "Workflow" },
18
- { id: "trigger", label: "Trigger" },
19
- { id: "kg_change_digest", label: "KG digest" },
15
+ export const reviewSourceFilters: Array<{ id: ReviewSourceFilter; labelKey: string }> = [
16
+ { id: "all", labelKey: "review.filter.source.all" },
17
+ { id: "workflow_run", labelKey: "review.filter.source.workflow_run" },
18
+ { id: "trigger", labelKey: "review.filter.source.trigger" },
19
+ { id: "kg_change_digest", labelKey: "review.filter.source.kg_change_digest" },
20
20
  ];
21
21
 
22
22
  export function reviewStatusVariant(status: string): React.ComponentProps<typeof Badge>["variant"] {