ltcai 8.0.0 → 8.2.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 (183) hide show
  1. package/README.md +33 -72
  2. package/docs/CHANGELOG.md +52 -874
  3. package/docs/DEVELOPMENT.md +26 -3
  4. package/docs/LEGACY_COMPATIBILITY.md +3 -1
  5. package/docs/TRUST_MODEL.md +26 -26
  6. package/docs/WHY_LATTICE.md +28 -25
  7. package/docs/kg-schema.md +6 -4
  8. package/lattice_brain/__init__.py +1 -1
  9. package/lattice_brain/embeddings.py +9 -1
  10. package/lattice_brain/graph/schema.py +6 -0
  11. package/lattice_brain/runtime/multi_agent.py +1 -1
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/api/computer_use.py +2 -0
  14. package/latticeai/api/memory.py +13 -0
  15. package/latticeai/app_factory.py +43 -162
  16. package/latticeai/brain/__init__.py +1 -1
  17. package/latticeai/core/config.py +10 -0
  18. package/latticeai/core/local_embeddings.py +8 -0
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/mcp_registry.py +140 -1
  21. package/latticeai/core/tool_registry.py +10 -0
  22. package/latticeai/core/workspace_graph_trace.py +132 -0
  23. package/latticeai/core/workspace_memory.py +75 -0
  24. package/latticeai/core/workspace_os.py +135 -1283
  25. package/latticeai/core/workspace_os_utils.py +132 -0
  26. package/latticeai/core/workspace_permissions.py +99 -0
  27. package/latticeai/core/workspace_plugins.py +97 -0
  28. package/latticeai/core/workspace_runs.py +612 -0
  29. package/latticeai/core/workspace_skills.py +114 -0
  30. package/latticeai/core/workspace_snapshots.py +195 -0
  31. package/latticeai/core/workspace_timeline.py +107 -0
  32. package/latticeai/runtime/audit_runtime.py +64 -0
  33. package/latticeai/runtime/config_runtime.py +4 -0
  34. package/latticeai/runtime/sso_runtime.py +52 -0
  35. package/latticeai/services/architecture_readiness.py +6 -5
  36. package/latticeai/services/memory_service.py +215 -7
  37. package/latticeai/services/model_capability_registry.py +68 -0
  38. package/latticeai/services/model_engines.py +603 -0
  39. package/latticeai/services/model_loading.py +482 -0
  40. package/latticeai/services/model_recommendation.py +5 -0
  41. package/latticeai/services/model_runtime.py +146 -837
  42. package/latticeai/services/product_readiness.py +19 -18
  43. package/package.json +2 -2
  44. package/scripts/build_frontend_assets.mjs +11 -2
  45. package/src-tauri/Cargo.lock +1 -1
  46. package/src-tauri/Cargo.toml +1 -1
  47. package/src-tauri/tauri.conf.json +1 -1
  48. package/static/app/asset-manifest.json +11 -102
  49. package/static/app/assets/Act-D9jIknFd.js +1 -0
  50. package/static/app/assets/Brain-CFOtWbPN.js +321 -0
  51. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
  52. package/static/app/assets/Library-C5Q2yWee.js +1 -0
  53. package/static/app/assets/System-BLbjdr1_.js +1 -0
  54. package/static/app/assets/core-CwxXejkd.js +1 -2
  55. package/static/app/assets/index-BqammyNu.js +16 -0
  56. package/static/app/assets/index-ty1iGgZu.css +2 -0
  57. package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
  58. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
  59. package/static/app/index.html +3 -10
  60. package/static/app/theme-boot.js +8 -0
  61. package/static/sw.js +1 -1
  62. package/tools/__init__.py +2 -1
  63. package/tools/computer.py +21 -0
  64. package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
  65. package/docs/HANDOVER_v3.6.0.md +0 -46
  66. package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
  67. package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
  68. package/docs/V2_ARCHITECTURE.md +0 -561
  69. package/docs/V3_2_AUDIT.md +0 -82
  70. package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
  71. package/docs/V3_FRONTEND.md +0 -140
  72. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
  73. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
  74. package/docs/V4_1_VALIDATION_REPORT.md +0 -47
  75. package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
  76. package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
  77. package/docs/V4_2_VALIDATION_REPORT.md +0 -89
  78. package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
  79. package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
  80. package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
  81. package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
  82. package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
  83. package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
  84. package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
  85. package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
  86. package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
  87. package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
  88. package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
  89. package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
  90. package/docs/V4_3_VALIDATION_REPORT.md +0 -58
  91. package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
  92. package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
  93. package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
  94. package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
  95. package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
  96. package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
  97. package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
  98. package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
  99. package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
  100. package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
  101. package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
  102. package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
  103. package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
  104. package/docs/V4_5_1_UX_REPORT.md +0 -45
  105. package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
  106. package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
  107. package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
  108. package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
  109. package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
  110. package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
  111. package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
  112. package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
  113. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
  114. package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
  115. package/frontend/index.html +0 -24
  116. package/frontend/openapi.json +0 -15425
  117. package/frontend/src/App.tsx +0 -243
  118. package/frontend/src/api/client.ts +0 -580
  119. package/frontend/src/api/openapi.ts +0 -17892
  120. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  121. package/frontend/src/components/FeedbackState.tsx +0 -45
  122. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  123. package/frontend/src/components/LivingBrain.tsx +0 -220
  124. package/frontend/src/components/ProductFlow.tsx +0 -171
  125. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  126. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  127. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  128. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  129. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  130. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  131. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  132. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  133. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  134. package/frontend/src/components/primitives.tsx +0 -392
  135. package/frontend/src/components/ui/badge.tsx +0 -27
  136. package/frontend/src/components/ui/button.tsx +0 -37
  137. package/frontend/src/components/ui/card.tsx +0 -22
  138. package/frontend/src/components/ui/input.tsx +0 -16
  139. package/frontend/src/components/ui/textarea.tsx +0 -16
  140. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  141. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  142. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  143. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  144. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  145. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  146. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  147. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  148. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  149. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  150. package/frontend/src/features/brain/brainData.ts +0 -246
  151. package/frontend/src/features/brain/graphLayout.ts +0 -37
  152. package/frontend/src/features/brain/types.ts +0 -150
  153. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  154. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  155. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  156. package/frontend/src/i18n.ts +0 -1303
  157. package/frontend/src/lib/utils.ts +0 -33
  158. package/frontend/src/main.tsx +0 -23
  159. package/frontend/src/pages/Act.tsx +0 -458
  160. package/frontend/src/pages/Ask.tsx +0 -14
  161. package/frontend/src/pages/Brain.tsx +0 -914
  162. package/frontend/src/pages/Capture.tsx +0 -258
  163. package/frontend/src/pages/Library.tsx +0 -486
  164. package/frontend/src/pages/System.tsx +0 -621
  165. package/frontend/src/routes.ts +0 -92
  166. package/frontend/src/store/appStore.ts +0 -94
  167. package/frontend/src/styles.css +0 -6579
  168. package/static/app/assets/Act-DOvf59ru.js +0 -2
  169. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  170. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  171. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  172. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  173. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  174. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  175. package/static/app/assets/System-D6t9jo9V.js +0 -2
  176. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  177. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  178. package/static/app/assets/index-C7g26IF6.css +0 -2
  179. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  180. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  181. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  182. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  183. package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
@@ -1,100 +0,0 @@
1
- import { RotateCcw } from "lucide-react";
2
- import type { ApiResult, ReviewItem } from "@/api/client";
3
- import { ActionButton, KeyValueList } from "@/components/primitives";
4
- import { Badge } from "@/components/ui/badge";
5
- import { Button } from "@/components/ui/button";
6
- import { t } from "@/i18n";
7
- import { useAppStore } from "@/store/appStore";
8
- import {
9
- formatSnoozedUntil,
10
- hasRunBefore,
11
- isActionableReview,
12
- reviewSourceDetail,
13
- reviewSourceLabel,
14
- reviewStatusVariant,
15
- type ReviewAction,
16
- } from "./reviewHelpers";
17
-
18
- type ReviewCardProps = {
19
- item: ReviewItem;
20
- feedback?: string;
21
- onAction: (item: ReviewItem, action: ReviewAction, hadRunBefore?: boolean) => Promise<ApiResult<ReviewItem>>;
22
- };
23
-
24
- export function ReviewCard({ item, feedback, onAction }: ReviewCardProps) {
25
- const mode = useAppStore((state) => state.mode);
26
- const language = useAppStore((state) => state.language);
27
- const provenance = item.provenance || {};
28
- const payload = item.payload || {};
29
- const hadRun = hasRunBefore(item);
30
- const snoozed = item.effective_status === "snoozed";
31
- const actionable = isActionableReview(item);
32
-
33
- return (
34
- <div className="rounded-lg border border-border bg-background/55 p-4">
35
- <div className="flex flex-wrap items-start justify-between gap-3">
36
- <div className="min-w-0 flex-1">
37
- <div className="font-medium">{item.title}</div>
38
- {item.summary ? <p className="mt-1 text-sm leading-6 text-muted-foreground">{item.summary}</p> : null}
39
- </div>
40
- <div className="flex flex-wrap items-center gap-2">
41
- <Badge variant="muted">{reviewSourceLabel(item.source)}</Badge>
42
- <Badge variant={reviewStatusVariant(item.effective_status)}>{item.effective_status}</Badge>
43
- </div>
44
- </div>
45
-
46
- {snoozed ? (
47
- <div className="mt-3 flex flex-wrap items-center justify-between gap-3 rounded-md border border-border bg-muted/24 p-3 text-sm">
48
- <div>
49
- <div className="font-medium">{formatSnoozedUntil(item.snoozed_until)}</div>
50
- <p className="mt-1 text-muted-foreground">{t(language, "review.snoozed.detail")}</p>
51
- </div>
52
- <Button size="sm" variant="outline" onClick={() => onAction(item, "unsnooze")} disabled={!actionable}>
53
- <RotateCcw className="h-3.5 w-3.5" /> {t(language, "review.unsnooze")}
54
- </Button>
55
- </div>
56
- ) : null}
57
-
58
- {mode !== "basic" ? (
59
- <div className="mt-3">
60
- <KeyValueList
61
- data={{
62
- workflow: provenance.workflow_id,
63
- trigger: provenance.trigger_id,
64
- run: payload.last_run_id || provenance.run_id,
65
- source_detail: reviewSourceDetail(provenance, item.source),
66
- snoozed_until: item.snoozed_until,
67
- created_at: item.created_at,
68
- updated_at: item.updated_at,
69
- }}
70
- limit={8}
71
- />
72
- </div>
73
- ) : null}
74
-
75
- {actionable ? (
76
- <div className="mt-4 grid gap-2">
77
- <p className="text-xs leading-5 text-muted-foreground">
78
- {t(language, "review.runNow.detail")}
79
- </p>
80
- <div className="flex flex-wrap gap-2" aria-label={t(language, "review.actions.aria")}>
81
- <ActionButton
82
- label={t(language, "review.runNow")}
83
- successLabel={hadRun ? t(language, "review.regenerated") : t(language, "review.executed")}
84
- action={() => onAction(item, "run_now", hadRun)}
85
- invalidate={[]}
86
- />
87
- <ActionButton label={t(language, "review.approve")} action={() => onAction(item, "approve")} invalidate={[]} />
88
- {!snoozed ? <ActionButton label={t(language, "review.snoozeDay")} action={() => onAction(item, "snooze")} invalidate={[]} /> : null}
89
- <ActionButton label={t(language, "review.dismiss")} action={() => onAction(item, "dismiss")} invalidate={[]} variant="destructive" />
90
- </div>
91
- </div>
92
- ) : null}
93
- {feedback ? (
94
- <p className={`mt-2 text-xs ${/fail|error|unavailable/i.test(feedback) ? "text-amber-300" : "text-emerald-300"}`}>
95
- {feedback} - {t(language, "review.feedback.open")}
96
- </p>
97
- ) : null}
98
- </div>
99
- );
100
- }
@@ -1,127 +0,0 @@
1
- import * as React from "react";
2
- import { useQuery, useQueryClient } from "@tanstack/react-query";
3
- import { latticeApi, type ReviewItem, type ReviewSourceFilter, type ReviewStatusFilter } from "@/api/client";
4
- import { EmptyState, LoadingPanel, Tabs } from "@/components/primitives";
5
- import { Badge } from "@/components/ui/badge";
6
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
7
- import { t } from "@/i18n";
8
- import { useAppStore } from "@/store/appStore";
9
- import { ReviewCard } from "./ReviewCard";
10
- import {
11
- defaultSnoozeUntil,
12
- reviewSourceFilters,
13
- reviewStatusFilters,
14
- type ReviewAction,
15
- } from "./reviewHelpers";
16
-
17
- export function ReviewInbox() {
18
- const language = useAppStore((state) => state.language);
19
- const qc = useQueryClient();
20
- const [statusFilter, setStatusFilter] = React.useState<ReviewStatusFilter>("pending");
21
- const [sourceFilter, setSourceFilter] = React.useState<ReviewSourceFilter>("all");
22
- const [runFeedback, setRunFeedback] = React.useState<Record<string, string>>({});
23
- const reviews = useQuery({
24
- queryKey: ["automationReviews", statusFilter, sourceFilter],
25
- queryFn: () => latticeApi.automationReviews({
26
- ...(statusFilter !== "all" ? { status: statusFilter } : {}),
27
- ...(sourceFilter !== "all" ? { source: sourceFilter } : {}),
28
- }),
29
- });
30
- const items = reviews.data?.data.items || [];
31
-
32
- const actOnReview = async (
33
- item: ReviewItem,
34
- action: ReviewAction,
35
- hadRunBefore = false,
36
- ) => {
37
- const call =
38
- action === "approve" ? () => latticeApi.approveReviewItem(item.id) :
39
- action === "dismiss" ? () => latticeApi.dismissReviewItem(item.id) :
40
- action === "snooze" ? () => latticeApi.snoozeReviewItem(item.id, defaultSnoozeUntil()) :
41
- action === "unsnooze" ? () => latticeApi.unsnoozeReviewItem(item.id) :
42
- () => latticeApi.runNowReviewItem(item.id);
43
- const result = await call();
44
- if (!result.ok) {
45
- setRunFeedback((prev) => ({
46
- ...prev,
47
- [item.id]: result.error || t(language, "review.action.failed", { action }),
48
- }));
49
- return result;
50
- }
51
- if (result.ok) {
52
- if (action === "run_now") {
53
- const payload = result.data.payload || {};
54
- const provenance = result.data.provenance || {};
55
- const runId = String(payload.last_run_id || provenance.run_id || "");
56
- setRunFeedback((prev) => ({
57
- ...prev,
58
- [item.id]: runId
59
- ? `${hadRunBefore ? t(language, "review.regenerated") : t(language, "review.executed")} · ${runId}`
60
- : hadRunBefore ? t(language, "review.regenerated") : t(language, "review.executed"),
61
- }));
62
- } else {
63
- setRunFeedback((prev) => {
64
- const next = { ...prev };
65
- delete next[item.id];
66
- return next;
67
- });
68
- }
69
- await qc.invalidateQueries({ queryKey: ["automationReviews"] });
70
- }
71
- return result;
72
- };
73
-
74
- if (reviews.isLoading) return <LoadingPanel title={t(language, "review.inbox.title")} />;
75
-
76
- return (
77
- <Card>
78
- <CardHeader className="gap-3">
79
- <div className="flex flex-wrap items-start justify-between gap-3">
80
- <div>
81
- <CardTitle>{t(language, "review.inbox.title")}</CardTitle>
82
- <CardDescription>{t(language, "review.inbox.description")}</CardDescription>
83
- </div>
84
- {reviews.data ? (
85
- <Badge variant={reviews.data.ok ? "success" : "warning"}>{reviews.data.ok ? t(language, "review.status.connected") : t(language, "review.status.unavailable")}</Badge>
86
- ) : null}
87
- </div>
88
- <div className="grid gap-2">
89
- <Tabs
90
- tabs={reviewStatusFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
91
- value={statusFilter}
92
- onChange={(id) => setStatusFilter(id as ReviewStatusFilter)}
93
- />
94
- <Tabs
95
- tabs={reviewSourceFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
96
- value={sourceFilter}
97
- onChange={(id) => setSourceFilter(id as ReviewSourceFilter)}
98
- />
99
- </div>
100
- </CardHeader>
101
- <CardContent>
102
- {reviews.isError || (reviews.data && !reviews.data.ok) ? (
103
- <EmptyState
104
- title={t(language, "review.inbox.loadError")}
105
- detail={reviews.data?.error || t(language, "review.inbox.unavailable")}
106
- />
107
- ) : !items.length ? (
108
- <EmptyState
109
- title={t(language, "review.inbox.empty")}
110
- detail={statusFilter === "snoozed" ? t(language, "review.inbox.emptySnoozed") : t(language, "review.inbox.emptyPending")}
111
- />
112
- ) : (
113
- <div className="grid gap-3">
114
- {items.map((item) => (
115
- <ReviewCard
116
- key={item.id}
117
- item={item}
118
- feedback={runFeedback[item.id]}
119
- onAction={actOnReview}
120
- />
121
- ))}
122
- </div>
123
- )}
124
- </CardContent>
125
- </Card>
126
- );
127
- }
@@ -1,69 +0,0 @@
1
- import type * as React from "react";
2
- import type { ReviewItem, ReviewSourceFilter, ReviewStatusFilter } from "@/api/client";
3
- import type { Badge } from "@/components/ui/badge";
4
-
5
- export type ReviewAction = "approve" | "dismiss" | "snooze" | "unsnooze" | "run_now";
6
-
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
- ];
14
-
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
- ];
21
-
22
- export function reviewStatusVariant(status: string): React.ComponentProps<typeof Badge>["variant"] {
23
- if (status === "pending") return "warning";
24
- if (status === "snoozed") return "muted";
25
- if (status === "approved") return "success";
26
- if (status === "dismissed") return "danger";
27
- return "muted";
28
- }
29
-
30
- export function reviewSourceLabel(source?: string) {
31
- if (source === "workflow_run") return "Workflow run";
32
- if (source === "trigger") return "Trigger";
33
- if (source === "kg_change_digest") return "KG digest";
34
- return source || "Automation";
35
- }
36
-
37
- export function reviewSourceDetail(provenance: Record<string, unknown>, source?: string) {
38
- const detail = provenance.source_detail;
39
- if (detail != null && String(detail).trim()) return String(detail);
40
- const triggerId = provenance.trigger_id;
41
- if (triggerId != null && String(triggerId).trim()) return String(triggerId);
42
- return reviewSourceLabel(source);
43
- }
44
-
45
- export function defaultSnoozeUntil() {
46
- const until = new Date();
47
- until.setDate(until.getDate() + 1);
48
- return until.toISOString();
49
- }
50
-
51
- export function formatSnoozedUntil(value?: string | null) {
52
- if (!value) return "Snoozed";
53
- const date = new Date(value);
54
- if (Number.isNaN(date.getTime())) return `Snoozed until ${value}`;
55
- return `Snoozed until ${new Intl.DateTimeFormat(undefined, {
56
- dateStyle: "medium",
57
- timeStyle: "short",
58
- }).format(date)}`;
59
- }
60
-
61
- export function isActionableReview(item: ReviewItem) {
62
- return item.effective_status === "pending" || item.effective_status === "snoozed";
63
- }
64
-
65
- export function hasRunBefore(item: ReviewItem) {
66
- const payload = item.payload || {};
67
- const provenance = item.provenance || {};
68
- return Boolean(payload.last_run_id || provenance.run_id);
69
- }