@burdenoff/microfe-vibecontrols 2026.625.2 → 2026.626.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.
Files changed (72) hide show
  1. package/dist/components/agents/AgentForm.js +129 -127
  2. package/dist/components/agents/AgentForm.js.map +1 -1
  3. package/dist/components/agents/AgentStatusBadge.js +11 -4
  4. package/dist/components/agents/AgentStatusBadge.js.map +1 -1
  5. package/dist/components/sessions/SessionStatusBadge.js +11 -8
  6. package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
  7. package/dist/components/shared/EmptyState.js +14 -8
  8. package/dist/components/shared/EmptyState.js.map +1 -1
  9. package/dist/components/shared/InstallTabs.js +19 -7
  10. package/dist/components/shared/InstallTabs.js.map +1 -1
  11. package/dist/components/shared/PageHeader.js.map +1 -1
  12. package/dist/components/shared/PageSkeleton.js +4 -1
  13. package/dist/components/shared/PageSkeleton.js.map +1 -1
  14. package/dist/components/shared/SessionNotificationBanner.js +12 -5
  15. package/dist/components/shared/SessionNotificationBanner.js.map +1 -1
  16. package/dist/components/shared/StatusBadge.js +2 -2
  17. package/dist/components/shared/StatusBadge.js.map +1 -1
  18. package/dist/pages/ActionsPage.js +209 -204
  19. package/dist/pages/ActionsPage.js.map +1 -1
  20. package/dist/pages/AgentDetailsPage.js +678 -648
  21. package/dist/pages/AgentDetailsPage.js.map +1 -1
  22. package/dist/pages/AgentGraphPage.js +112 -108
  23. package/dist/pages/AgentGraphPage.js.map +1 -1
  24. package/dist/pages/AgentsPage.js +160 -155
  25. package/dist/pages/AgentsPage.js.map +1 -1
  26. package/dist/pages/AnalyticsPage.js +140 -136
  27. package/dist/pages/AnalyticsPage.js.map +1 -1
  28. package/dist/pages/CatalogDetailPage.js +249 -244
  29. package/dist/pages/CatalogDetailPage.js.map +1 -1
  30. package/dist/pages/CatalogPage.js +73 -68
  31. package/dist/pages/CatalogPage.js.map +1 -1
  32. package/dist/pages/ComponentDetailPage.js +195 -164
  33. package/dist/pages/ComponentDetailPage.js.map +1 -1
  34. package/dist/pages/ComponentsPage.js +80 -75
  35. package/dist/pages/ComponentsPage.js.map +1 -1
  36. package/dist/pages/EnvTopologyPage.js +100 -95
  37. package/dist/pages/EnvTopologyPage.js.map +1 -1
  38. package/dist/pages/HealthPage.js +331 -204
  39. package/dist/pages/HealthPage.js.map +1 -1
  40. package/dist/pages/LogsPage.js +54 -49
  41. package/dist/pages/LogsPage.js.map +1 -1
  42. package/dist/pages/OverviewPage.js +251 -241
  43. package/dist/pages/OverviewPage.js.map +1 -1
  44. package/dist/pages/PluginsCatalogPage.js +72 -78
  45. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  46. package/dist/pages/SessionDetailsPage.js +517 -512
  47. package/dist/pages/SessionDetailsPage.js.map +1 -1
  48. package/dist/pages/SessionsPage.js +1084 -1078
  49. package/dist/pages/SessionsPage.js.map +1 -1
  50. package/dist/pages/SettingsPage.js +107 -103
  51. package/dist/pages/SettingsPage.js.map +1 -1
  52. package/dist/pages/TargetDetailsPage.js +131 -123
  53. package/dist/pages/TargetDetailsPage.js.map +1 -1
  54. package/dist/pages/TargetsPage.js +265 -260
  55. package/dist/pages/TargetsPage.js.map +1 -1
  56. package/dist/pages/TemplatesPage.js +77 -71
  57. package/dist/pages/TemplatesPage.js.map +1 -1
  58. package/dist/pages/VibeAuditPage.js +83 -78
  59. package/dist/pages/VibeAuditPage.js.map +1 -1
  60. package/dist/pages/VibeCalendarPage.js +109 -104
  61. package/dist/pages/VibeCalendarPage.js.map +1 -1
  62. package/dist/pages/VibeDeckPage.js +222 -217
  63. package/dist/pages/VibeDeckPage.js.map +1 -1
  64. package/dist/pages/VibeDetailsPage.js +532 -501
  65. package/dist/pages/VibeDetailsPage.js.map +1 -1
  66. package/dist/pages/VibesPage.js +138 -133
  67. package/dist/pages/VibesPage.js.map +1 -1
  68. package/dist/pages/WebhooksPage.js +329 -324
  69. package/dist/pages/WebhooksPage.js.map +1 -1
  70. package/dist/pages/ai/AIPage.js +149 -144
  71. package/dist/pages/ai/AIPage.js.map +1 -1
  72. package/package.json +2 -2
@@ -6,78 +6,79 @@ import { ErrorState as ie } from "../components/shared/ErrorState.js";
6
6
  import { EmptyState as ae } from "../components/shared/EmptyState.js";
7
7
  import { GetHelpButton as oe } from "../components/shared/GetHelpButton.js";
8
8
  import "../components/shared/index.js";
9
- import { isNative as a, nativeNotify as o } from "../utils/nativeBridge.js";
10
- import { VIBE_PERMISSIONS as s } from "../constants/permissions.js";
11
- import { AuditLogTable as se } from "../components/audit/AuditLogTable.js";
12
- import { AuditLogFilters as ce } from "../components/audit/AuditLogFilters.js";
13
- import { AuditLogDetail as le } from "../components/audit/AuditLogDetail.js";
14
- import { AuditLogStats as ue } from "../components/audit/AuditLogStats.js";
15
- import { AuditDeleteDialog as de } from "../components/audit/AuditDeleteDialog.js";
16
- import { AuditExportButton as fe } from "../components/audit/AuditExportButton.js";
9
+ import { isNative as a, nativeNotify as se } from "../utils/nativeBridge.js";
10
+ import { VIBE_PERMISSIONS as o } from "../constants/permissions.js";
11
+ import { AuditLogTable as ce } from "../components/audit/AuditLogTable.js";
12
+ import { AuditLogFilters as le } from "../components/audit/AuditLogFilters.js";
13
+ import { AuditLogDetail as ue } from "../components/audit/AuditLogDetail.js";
14
+ import { AuditLogStats as de } from "../components/audit/AuditLogStats.js";
15
+ import { AuditDeleteDialog as fe } from "../components/audit/AuditDeleteDialog.js";
16
+ import { AuditExportButton as pe } from "../components/audit/AuditExportButton.js";
17
17
  import "../components/audit/index.js";
18
- import { useCallback as c, useEffect as pe, useRef as me, useState as l } from "react";
18
+ import { useCallback as s, useEffect as c, useRef as me, useState as l } from "react";
19
19
  import { useI18n as he } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
20
  import { Shield as u } from "lucide-react";
21
21
  import { usePermissions as ge } from "@burdenoff/fe-libs/shared/providers/shell";
22
22
  import { jsx as d, jsxs as f } from "react/jsx-runtime";
23
- import { AccessDenied as _e } from "@burdenoff/fe-libs/shared/components";
23
+ import { PagePurpose as _e } from "@burdenoff/fe-libs/ui";
24
+ import { AccessDenied as ve } from "@burdenoff/fe-libs/shared/components";
24
25
  //#region src/pages/VibeAuditPage.tsx
25
26
  var p = 50;
26
27
  function m() {
27
28
  ne();
28
- let { t: m } = he(), { hasPermission: h, isLoading: g, permissions: _ } = ge(), [v, ve] = l(""), [y, ye] = l(""), [b, be] = l("all"), [x, xe] = l("all"), [S, Se] = l("all"), [C, w] = l("all"), [T, E] = l("all"), [D, Ce] = l(!1), [O, k] = l(0), [A, we] = l(() => typeof window > "u" ? null : new URLSearchParams(window.location.search).get("sessionId")), [j, M] = l(null), [N, P] = l(/* @__PURE__ */ new Set()), [F, I] = l([]), [L, R] = l(null), [z, B] = l(null), V = me(null), H = c((e, t) => {
29
+ let { t: m } = he(), { hasPermission: h, isLoading: g, permissions: _ } = ge(), [v, ye] = l(""), [be, xe] = l(""), [y, Se] = l("all"), [b, Ce] = l("all"), [x, we] = l("all"), [S, C] = l("all"), [w, T] = l("all"), [E, Te] = l(!1), [D, O] = l(0), [k, A] = l(() => typeof window > "u" ? null : new URLSearchParams(window.location.search).get("sessionId")), [j, M] = l(null), [N, P] = l(/* @__PURE__ */ new Set()), [F, I] = l([]), [L, R] = l(null), [z, B] = l(null), V = me(null), H = s((e, t) => {
29
30
  let n = m(e);
30
31
  return n === e ? t : n;
31
- }, [m]), U = h(s.AUDIT_LIST), W = h(s.AUDIT_DELETE), Te = h(s.AUDIT_EXPORT), G = g || !U, K = {};
32
- y && (K.search = y), b && b !== "all" && (K.action = b), x && x !== "all" && (K.resourceType = x), S && S !== "all" && (K.status = S), D && (K.hasCommand = !0), C && C !== "all" ? (K.resourceId = C, K.resourceType ||= "AGENT") : T && T !== "all" && (K.resourceId = T, K.resourceType ||= "VIBE"), A && (K.sessionId = A);
33
- let { data: q, loading: Ee, error: De, refetch: Oe } = te({
32
+ }, [m]), U = h(o.AUDIT_LIST), W = h(o.AUDIT_DELETE), Ee = h(o.AUDIT_EXPORT), G = g || !U, K = {};
33
+ be && (K.search = be), y && y !== "all" && (K.action = y), b && b !== "all" && (K.resourceType = b), x && x !== "all" && (K.status = x), E && (K.hasCommand = !0), S && S !== "all" ? (K.resourceId = S, K.resourceType ||= "AGENT") : w && w !== "all" && (K.resourceId = w, K.resourceType ||= "VIBE"), k && (K.sessionId = k);
34
+ let { data: q, loading: De, error: Oe, refetch: ke } = te({
34
35
  variables: {
35
36
  filter: K,
36
- pagination: { limit: Math.min(Math.max((O + 1) * p, p), 100) }
37
+ pagination: { limit: Math.min(Math.max((D + 1) * p, p), 100) }
37
38
  },
38
39
  skip: G,
39
40
  fetchPolicy: "cache-and-network",
40
41
  notifyOnNetworkStatusChange: !0
41
- }), { data: ke } = r({
42
+ }), { data: Ae } = r({
42
43
  variables: { pagination: { limit: 200 } },
43
44
  fetchPolicy: "cache-first",
44
45
  skip: G
45
- }), Ae = (ke?.vibecontrolsAgents ?? []).map((e) => ({
46
+ }), je = (Ae?.vibecontrolsAgents ?? []).map((e) => ({
46
47
  id: e.id,
47
48
  name: e.name
48
- })), { data: je } = ee({
49
+ })), { data: Me } = ee({
49
50
  variables: { pagination: { limit: 200 } },
50
51
  fetchPolicy: "cache-first",
51
52
  skip: G
52
- }), Me = (je?.vibes?.items ?? []).map((e) => ({
53
+ }), Ne = (Me?.vibes?.items ?? []).map((e) => ({
53
54
  id: e.id,
54
55
  name: e.name
55
- })), Ne = q?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90, Pe = q?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null, { data: J } = n({
56
+ })), Pe = q?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90, Fe = q?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null, { data: J } = n({
56
57
  variables: { id: j },
57
58
  skip: !j
58
- }), [Fe, { loading: Ie }] = t(), [Le] = e();
59
- pe(() => {
59
+ }), [Ie, { loading: Le }] = t(), [Re] = e();
60
+ c(() => {
60
61
  let e = window.setTimeout(() => {
61
- ye(v), k(0);
62
+ xe(v), O(0);
62
63
  }, 300);
63
64
  return () => window.clearTimeout(e);
64
- }, [v]), pe(() => () => {
65
+ }, [v]), c(() => () => {
65
66
  V.current && clearTimeout(V.current);
66
67
  }, []);
67
- let Y = c(() => {
68
+ let Y = s(() => {
68
69
  R(null), V.current &&= (clearTimeout(V.current), null);
69
- }, []), X = c(() => {
70
+ }, []), X = s(() => {
70
71
  B(null);
71
- }, []), Z = (q?.vibeAuditLogs.items ?? []).slice(O * p, (O + 1) * p), Re = q?.vibeAuditLogs.total ?? 0, Q = Z.map((e) => e.id), $ = Q.length > 0 && Q.every((e) => N.has(e)), ze = Q.some((e) => N.has(e)) && !$, Be = async (e) => {
72
+ }, []), Z = (q?.vibeAuditLogs.items ?? []).slice(D * p, (D + 1) * p), ze = q?.vibeAuditLogs.total ?? 0, Q = Z.map((e) => e.id), $ = Q.length > 0 && Q.every((e) => N.has(e)), Be = Q.some((e) => N.has(e)) && !$, Ve = async (e) => {
72
73
  if (F.length !== 0) {
73
74
  Y(), X();
74
75
  try {
75
- if (F.length === 1) await Fe({ variables: {
76
+ if (F.length === 1) await Ie({ variables: {
76
77
  id: F[0],
77
78
  input: { reason: e }
78
79
  } }), B("Deleted 1 audit log");
79
80
  else {
80
- let t = await Le({ variables: {
81
+ let t = await Re({ variables: {
81
82
  ids: F,
82
83
  input: { reason: e }
83
84
  } }), n = t.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0, r = t.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;
@@ -86,32 +87,32 @@ function m() {
86
87
  I([]), P((e) => {
87
88
  let t = new Set(e);
88
89
  return F.forEach((e) => t.delete(e)), t;
89
- }), j && F.includes(j) && M(null), await Oe(), a() && o("success");
90
+ }), j && F.includes(j) && M(null), await ke(), a() && se("success");
90
91
  } catch (e) {
91
- console.error("Failed to delete audit log:", e), R(H("audit.deleteFailed", "Failed to delete audit log. Please try again.")), a() && o("error"), V.current = setTimeout(() => {
92
+ console.error("Failed to delete audit log:", e), R(H("audit.deleteFailed", "Failed to delete audit log. Please try again.")), a() && se("error"), V.current = setTimeout(() => {
92
93
  R(null), V.current = null;
93
94
  }, 3e4);
94
95
  }
95
96
  }
96
- }, Ve = c((e) => {
97
+ }, He = s((e) => {
97
98
  W && (Y(), X(), I([e]));
98
99
  }, [
99
100
  W,
100
101
  Y,
101
102
  X
102
- ]), He = c((e, t) => {
103
+ ]), Ue = s((e, t) => {
103
104
  P((n) => {
104
105
  let r = new Set(n);
105
106
  return t ? r.add(e) : r.delete(e), r;
106
107
  });
107
- }, []), Ue = c((e) => {
108
+ }, []), We = s((e) => {
108
109
  P((t) => {
109
110
  let n = new Set(t);
110
111
  return Q.forEach((t) => {
111
112
  e ? n.add(t) : n.delete(t);
112
113
  }), n;
113
114
  });
114
- }, [Q]), We = c(() => {
115
+ }, [Q]), Ge = s(() => {
115
116
  !W || N.size === 0 || (Y(), X(), I(Array.from(N)));
116
117
  }, [
117
118
  W,
@@ -122,7 +123,7 @@ function m() {
122
123
  return g ? /* @__PURE__ */ d("div", {
123
124
  className: "mx-auto max-w-[1400px] p-4 sm:p-6",
124
125
  children: /* @__PURE__ */ d(i, { message: H("audit.loadingPermissions", "Loading permissions...") })
125
- }) : _ && _.permissionStrings?.length > 0 && !U ? /* @__PURE__ */ d(_e, {
126
+ }) : _ && _.permissionStrings?.length > 0 && !U ? /* @__PURE__ */ d(ve, {
126
127
  title: H("common.accessDeniedTitle", "Access Denied"),
127
128
  description: H("common.accessDeniedDescription", "You don't have permission to access this resource.")
128
129
  }) : /* @__PURE__ */ f("div", {
@@ -131,16 +132,20 @@ function m() {
131
132
  /* @__PURE__ */ d(re, {
132
133
  title: H("nav.vibeAudit", "Audit"),
133
134
  description: H("audit.description", "Comprehensive audit trail of all VibeControls operations"),
134
- actions: Te ? /* @__PURE__ */ d(fe, { filter: K }) : void 0
135
+ actions: Ee ? /* @__PURE__ */ d(pe, { filter: K }) : void 0
136
+ }),
137
+ /* @__PURE__ */ d(_e, {
138
+ icon: /* @__PURE__ */ d(u, { className: "size-4" }),
139
+ children: H("audit.purpose", "The audit trail records who did what across VibeControls — every agent registered, session started, setting changed and more — with timestamps and actors. Use it to investigate an incident, satisfy a compliance review, or simply understand a change after the fact. Filter and export the log for evidence.")
135
140
  }),
136
141
  /* @__PURE__ */ f("div", {
137
142
  className: "mb-4 flex items-start gap-2 rounded-md border border-status-info-border bg-status-info-bg-subtle px-3 py-2 text-xs text-status-info-text",
138
143
  children: [/* @__PURE__ */ d(u, {
139
144
  size: 14,
140
145
  className: "mt-0.5 flex-shrink-0"
141
- }), /* @__PURE__ */ f("span", { children: [H("audit.retentionBanner", `Audit logs are kept for ${Ne} days. Older entries are automatically purged.`), Pe ? ` ${H("audit.retentionNextCleanup", `Next cleanup: ${new Date(Pe).toLocaleString()}.`)}` : ""] })]
146
+ }), /* @__PURE__ */ f("span", { children: [H("audit.retentionBanner", `Audit logs are kept for ${Pe} days. Older entries are automatically purged.`), Fe ? ` ${H("audit.retentionNextCleanup", `Next cleanup: ${new Date(Fe).toLocaleString()}.`)}` : ""] })]
142
147
  }),
143
- A ? /* @__PURE__ */ f("div", {
148
+ k ? /* @__PURE__ */ f("div", {
144
149
  className: "mb-4 flex items-center justify-between gap-2 rounded-md border border-action-primary-border bg-[var(--color-accent-soft)] px-3 py-2 text-xs text-text-primary",
145
150
  children: [/* @__PURE__ */ f("span", {
146
151
  className: "truncate",
@@ -149,13 +154,13 @@ function m() {
149
154
  " ",
150
155
  /* @__PURE__ */ d("span", {
151
156
  className: "font-mono",
152
- children: A
157
+ children: k
153
158
  })
154
159
  ]
155
160
  }), /* @__PURE__ */ d("button", {
156
161
  type: "button",
157
162
  onClick: () => {
158
- we(null), k(0);
163
+ A(null), O(0);
159
164
  },
160
165
  className: "flex-shrink-0 rounded bg-bg-sunken px-2 py-1 font-medium text-text-secondary hover:text-text-primary",
161
166
  children: H("audit.clearSessionScope", "Clear")
@@ -163,40 +168,40 @@ function m() {
163
168
  }) : null,
164
169
  /* @__PURE__ */ d("div", {
165
170
  "data-tour": "audit-log-stats",
166
- children: /* @__PURE__ */ d(ue, {
171
+ children: /* @__PURE__ */ d(de, {
167
172
  stats: q?.vibeAuditLogStats,
168
- loading: Ee && !q
173
+ loading: De && !q
169
174
  })
170
175
  }),
171
- /* @__PURE__ */ d(ce, {
176
+ /* @__PURE__ */ d(le, {
172
177
  searchQuery: v,
173
- onSearchChange: ve,
174
- actionFilter: b,
178
+ onSearchChange: ye,
179
+ actionFilter: y,
175
180
  onActionChange: (e) => {
176
- be(e), k(0);
181
+ Se(e), O(0);
177
182
  },
178
- resourceTypeFilter: x,
183
+ resourceTypeFilter: b,
179
184
  onResourceTypeChange: (e) => {
180
- xe(e), k(0);
185
+ Ce(e), O(0);
181
186
  },
182
- statusFilter: S,
187
+ statusFilter: x,
183
188
  onStatusChange: (e) => {
184
- Se(e), k(0);
189
+ we(e), O(0);
185
190
  },
186
- hasCommandFilter: D,
191
+ hasCommandFilter: E,
187
192
  onHasCommandChange: (e) => {
188
- Ce(e), k(0);
193
+ Te(e), O(0);
189
194
  },
190
- agentFilter: C,
195
+ agentFilter: S,
191
196
  onAgentChange: (e) => {
192
- w(e), e !== "all" && E("all"), k(0);
197
+ C(e), e !== "all" && T("all"), O(0);
193
198
  },
194
- agents: Ae,
195
- vibeFilter: T,
199
+ agents: je,
200
+ vibeFilter: w,
196
201
  onVibeChange: (e) => {
197
- E(e), e !== "all" && w("all"), k(0);
202
+ T(e), e !== "all" && C("all"), O(0);
198
203
  },
199
- vibes: Me
204
+ vibes: Ne
200
205
  }),
201
206
  Z.length > 0 ? /* @__PURE__ */ d("div", {
202
207
  className: "rounded-lg border border-border-default bg-bg-surface p-4",
@@ -217,7 +222,7 @@ function m() {
217
222
  className: "flex flex-wrap items-center gap-2",
218
223
  children: /* @__PURE__ */ f("button", {
219
224
  type: "button",
220
- onClick: We,
225
+ onClick: Ge,
221
226
  disabled: !W || N.size === 0,
222
227
  className: "rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50",
223
228
  children: ["Delete Selected", N.size > 0 ? ` (${N.size})` : ""]
@@ -225,10 +230,10 @@ function m() {
225
230
  })]
226
231
  })
227
232
  }) : null,
228
- Ee && !q ? /* @__PURE__ */ d(i, { message: H("audit.loadingLogs", "Loading audit logs...") }) : De ? /* @__PURE__ */ d(ie, {
229
- message: De.message,
230
- onRetry: () => Oe()
231
- }) : Re === 0 ? /* @__PURE__ */ d(ae, {
233
+ De && !q ? /* @__PURE__ */ d(i, { message: H("audit.loadingLogs", "Loading audit logs...") }) : Oe ? /* @__PURE__ */ d(ie, {
234
+ message: Oe.message,
235
+ onRetry: () => ke()
236
+ }) : ze === 0 ? /* @__PURE__ */ d(ae, {
232
237
  icon: /* @__PURE__ */ d(u, {
233
238
  size: 48,
234
239
  className: "text-text-secondary"
@@ -238,30 +243,30 @@ function m() {
238
243
  action: /* @__PURE__ */ d(oe, { prompt: "What is the Audit Log in VibeControls and how do I use it?" })
239
244
  }) : /* @__PURE__ */ d("div", {
240
245
  "data-tour": "audit-log-table",
241
- children: /* @__PURE__ */ d(se, {
246
+ children: /* @__PURE__ */ d(ce, {
242
247
  items: Z,
243
- total: Re,
248
+ total: ze,
244
249
  hasMore: q?.vibeAuditLogs.hasMore ?? !1,
245
- page: O,
250
+ page: D,
246
251
  pageSize: p,
247
- onPageChange: k,
252
+ onPageChange: O,
248
253
  onSelectLog: M,
249
- onDeleteLog: Ve,
254
+ onDeleteLog: He,
250
255
  selectedLogId: j,
251
256
  selectedLogIds: N,
252
257
  allVisibleSelected: $,
253
- partiallySelected: ze,
254
- onToggleLogSelection: He,
255
- onToggleAllVisible: Ue
258
+ partiallySelected: Be,
259
+ onToggleLogSelection: Ue,
260
+ onToggleAllVisible: We
256
261
  })
257
262
  }),
258
- j && J?.vibeAuditLog ? /* @__PURE__ */ d(le, {
263
+ j && J?.vibeAuditLog ? /* @__PURE__ */ d(ue, {
259
264
  log: J.vibeAuditLog,
260
265
  onClose: () => M(null),
261
- footer: J.vibeAuditLog.resourceType === "SESSION" && J.vibeAuditLog.resourceId && J.vibeAuditLog.resourceId !== A ? /* @__PURE__ */ d("button", {
266
+ footer: J.vibeAuditLog.resourceType === "SESSION" && J.vibeAuditLog.resourceId && J.vibeAuditLog.resourceId !== k ? /* @__PURE__ */ d("button", {
262
267
  type: "button",
263
268
  onClick: () => {
264
- we(J.vibeAuditLog?.resourceId ?? null), M(null), k(0);
269
+ A(J.vibeAuditLog?.resourceId ?? null), M(null), O(0);
265
270
  },
266
271
  className: "w-full rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover",
267
272
  children: H("audit.showOnlyThisSession", "Show only this session's activity")
@@ -287,11 +292,11 @@ function m() {
287
292
  children: H("common.dismiss", "Dismiss")
288
293
  })]
289
294
  }) : null,
290
- /* @__PURE__ */ d(de, {
295
+ /* @__PURE__ */ d(fe, {
291
296
  isOpen: F.length > 0,
292
297
  onClose: () => I([]),
293
- onConfirm: Be,
294
- loading: Ie,
298
+ onConfirm: Ve,
299
+ loading: Le,
295
300
  title: F.length > 1 ? "Delete Audit Logs" : "Delete Audit Log",
296
301
  description: F.length > 1 ? `This will permanently delete ${F.length} audit logs and record the reason in the audit trail.` : "This action will be recorded in the audit trail.",
297
302
  confirmLabel: F.length > 1 ? "Delete Logs" : "Delete"
@@ -1 +1 @@
1
- {"version":3,"file":"VibeAuditPage.js","names":[],"sources":["../../src/pages/VibeAuditPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Shield } from 'lucide-react';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuditDeleteDialog,\n AuditExportButton,\n AuditLogDetail,\n AuditLogFilters,\n AuditLogStats,\n AuditLogTable,\n} from '@/components/audit';\nimport {\n EmptyState,\n ErrorState,\n GetHelpButton,\n LoadingState,\n PageHeader,\n} from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n useBulkDeleteVibeAuditLogsMutation,\n useDeleteVibeAuditLogMutation,\n useGetVibeAuditLogQuery,\n useListAgentsQuery,\n useListVibesQuery,\n useVibeAuditDashboardQuery,\n} from '@/generated/wspace-operations';\nimport type {\n VibeAuditAction,\n VibeAuditLogFilterInput,\n VibeAuditResourceType,\n VibeAuditStatus,\n} from '@/generated/wspace-types';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nconst PAGE_SIZE = 50;\n\nexport function VibeAuditPage() {\n useVibeControls();\n\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');\n const [actionFilter, setActionFilter] = useState<string | null>('all');\n const [resourceTypeFilter, setResourceTypeFilter] = useState<string | null>('all');\n const [statusFilter, setStatusFilter] = useState<string | null>('all');\n const [agentFilter, setAgentFilter] = useState<string | null>('all');\n const [vibeFilter, setVibeFilter] = useState<string | null>('all');\n const [hasCommandFilter, setHasCommandFilter] = useState(false);\n const [page, setPage] = useState(0);\n // F2 — session-history drill-down. Initialised from a ?sessionId= URL param\n // (reciprocal link from the in-session History tab) and also settable by\n // clicking \"Show only this session\" on a SESSION-typed audit entry.\n const [sessionDrillId, setSessionDrillId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return new URLSearchParams(window.location.search).get('sessionId');\n });\n const [selectedLogId, setSelectedLogId] = useState<string | null>(null);\n const [selectedLogIds, setSelectedLogIds] = useState<Set<string>>(new Set());\n const [deleteTargetIds, setDeleteTargetIds] = useState<string[]>([]);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n const [deleteSuccess, setDeleteSuccess] = useState<string | null>(null);\n\n const deleteErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const canListAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_LIST);\n const canDeleteAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_DELETE);\n const canExportAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_EXPORT);\n const shouldSkipQueries = permissionsLoading || !canListAudit;\n\n const filter: VibeAuditLogFilterInput = {};\n if (debouncedSearchQuery) filter.search = debouncedSearchQuery;\n if (actionFilter && actionFilter !== 'all') filter.action = actionFilter as VibeAuditAction;\n if (resourceTypeFilter && resourceTypeFilter !== 'all')\n filter.resourceType = resourceTypeFilter as VibeAuditResourceType;\n if (statusFilter && statusFilter !== 'all') filter.status = statusFilter as VibeAuditStatus;\n if (hasCommandFilter) filter.hasCommand = true;\n // Agent / Vibe filters: backend exposes `resourceId` + `resourceType`.\n // We narrow `resourceType` accordingly so the server returns only audit\n // rows whose target is the chosen agent / vibe. If the user already\n // chose a resourceType that conflicts (e.g. SESSION), the resourceId\n // filter would over-narrow — but that combination is documented as\n // intentional power-user behaviour.\n if (agentFilter && agentFilter !== 'all') {\n filter.resourceId = agentFilter;\n if (!filter.resourceType) filter.resourceType = 'AGENT' as VibeAuditResourceType;\n } else if (vibeFilter && vibeFilter !== 'all') {\n filter.resourceId = vibeFilter;\n if (!filter.resourceType) filter.resourceType = 'VIBE' as VibeAuditResourceType;\n }\n // Session drill-down — the server matches resourceType=SESSION AND\n // resourceId=sessionId. Works for killed (soft-deleted) sessions too.\n if (sessionDrillId) filter.sessionId = sessionDrillId;\n\n const { data, loading, error, refetch } = useVibeAuditDashboardQuery({\n variables: {\n filter,\n pagination: { limit: Math.min(Math.max((page + 1) * PAGE_SIZE, PAGE_SIZE), 100) },\n },\n skip: shouldSkipQueries,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n });\n const agentsList = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string }) => ({ id: a.id, name: a.name })\n );\n\n const { data: vibesData } = useListVibesQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n });\n const vibesList = (vibesData?.vibes?.items ?? []).map((v: { id: string; name: string }) => ({\n id: v.id,\n name: v.name,\n }));\n\n const retentionDays = data?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90;\n const nextCleanupAt = data?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null;\n\n const { data: detailData } = useGetVibeAuditLogQuery({\n variables: { id: selectedLogId! },\n skip: !selectedLogId,\n });\n\n const [deleteAuditLog, { loading: deleteLoading }] = useDeleteVibeAuditLogMutation();\n const [bulkDeleteAuditLogs] = useBulkDeleteVibeAuditLogsMutation();\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n setPage(0);\n }, 300);\n\n return () => window.clearTimeout(timeoutId);\n }, [searchQuery]);\n\n useEffect(() => {\n return () => {\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n }\n };\n }, []);\n\n const clearDeleteError = useCallback(() => {\n setDeleteError(null);\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n deleteErrorTimer.current = null;\n }\n }, []);\n\n const clearDeleteSuccess = useCallback(() => {\n setDeleteSuccess(null);\n }, []);\n\n const allItems = data?.vibeAuditLogs.items ?? [];\n const items = allItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n const totalAuditItems = data?.vibeAuditLogs.total ?? 0;\n const visibleLogIds = items.map((item: (typeof items)[number]) => item.id);\n const allVisibleSelected =\n visibleLogIds.length > 0 && visibleLogIds.every((id: string) => selectedLogIds.has(id));\n const partiallySelected =\n visibleLogIds.some((id: string) => selectedLogIds.has(id)) && !allVisibleSelected;\n\n const handleDelete = async (reason: string) => {\n if (deleteTargetIds.length === 0) return;\n\n clearDeleteError();\n clearDeleteSuccess();\n\n try {\n if (deleteTargetIds.length === 1) {\n await deleteAuditLog({ variables: { id: deleteTargetIds[0], input: { reason } } });\n setDeleteSuccess('Deleted 1 audit log');\n } else {\n const result = await bulkDeleteAuditLogs({\n variables: { ids: deleteTargetIds, input: { reason } },\n });\n const deletedCount = result.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0;\n const failedCount = result.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;\n setDeleteSuccess(\n `Deleted ${deletedCount} audit logs${failedCount > 0 ? ` • ${failedCount} failed` : ''}`\n );\n }\n\n setDeleteTargetIds([]);\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n deleteTargetIds.forEach((id) => next.delete(id));\n return next;\n });\n if (selectedLogId && deleteTargetIds.includes(selectedLogId)) {\n setSelectedLogId(null);\n }\n await refetch();\n if (isNative()) void nativeNotify('success');\n } catch (deleteAuditError) {\n console.error('Failed to delete audit log:', deleteAuditError);\n setDeleteError(tr('audit.deleteFailed', 'Failed to delete audit log. Please try again.'));\n if (isNative()) void nativeNotify('error');\n deleteErrorTimer.current = setTimeout(() => {\n setDeleteError(null);\n deleteErrorTimer.current = null;\n }, 30000);\n }\n };\n\n const handleOpenDelete = useCallback(\n (id: string) => {\n if (!canDeleteAudit) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds([id]);\n },\n [canDeleteAudit, clearDeleteError, clearDeleteSuccess]\n );\n\n const handleToggleLogSelection = useCallback((id: string, checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n }, []);\n\n const handleToggleAllVisible = useCallback(\n (checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n visibleLogIds.forEach((id: string) => {\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return next;\n });\n },\n [visibleLogIds]\n );\n\n const handleOpenBulkDelete = useCallback(() => {\n if (!canDeleteAudit || selectedLogIds.size === 0) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds(Array.from(selectedLogIds));\n }, [canDeleteAudit, clearDeleteError, clearDeleteSuccess, selectedLogIds]);\n\n if (permissionsLoading) {\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <LoadingState message={tr('audit.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canListAudit) {\n return (\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'common.accessDeniedDescription',\n \"You don't have permission to access this resource.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.vibeAudit', 'Audit')}\n description={tr(\n 'audit.description',\n 'Comprehensive audit trail of all VibeControls operations'\n )}\n actions={canExportAudit ? <AuditExportButton filter={filter} /> : undefined}\n />\n\n {/* Retention banner — sourced from the backend `retentionPolicy`\n so the displayed window updates if the tier changes. */}\n <div className=\"mb-4 flex items-start gap-2 rounded-md border border-status-info-border bg-status-info-bg-subtle px-3 py-2 text-xs text-status-info-text\">\n <Shield size={14} className=\"mt-0.5 flex-shrink-0\" />\n <span>\n {tr(\n 'audit.retentionBanner',\n `Audit logs are kept for ${retentionDays} days. Older entries are automatically purged.`\n )}\n {nextCleanupAt\n ? ` ${tr('audit.retentionNextCleanup', `Next cleanup: ${new Date(nextCleanupAt).toLocaleString()}.`)}`\n : ''}\n </span>\n </div>\n\n {/* Session drill-down banner — shown when scoped to one session's activity. */}\n {sessionDrillId ? (\n <div className=\"mb-4 flex items-center justify-between gap-2 rounded-md border border-action-primary-border bg-[var(--color-accent-soft)] px-3 py-2 text-xs text-text-primary\">\n <span className=\"truncate\">\n {tr('audit.sessionScopeBanner', 'Showing activity for session')}{' '}\n <span className=\"font-mono\">{sessionDrillId}</span>\n </span>\n <button\n type=\"button\"\n onClick={() => {\n setSessionDrillId(null);\n setPage(0);\n }}\n className=\"flex-shrink-0 rounded bg-bg-sunken px-2 py-1 font-medium text-text-secondary hover:text-text-primary\"\n >\n {tr('audit.clearSessionScope', 'Clear')}\n </button>\n </div>\n ) : null}\n\n {/* AuditLogStats is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"audit-log-stats\">\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n </div>\n\n <AuditLogFilters\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n actionFilter={actionFilter}\n onActionChange={(value) => {\n setActionFilter(value);\n setPage(0);\n }}\n resourceTypeFilter={resourceTypeFilter}\n onResourceTypeChange={(value) => {\n setResourceTypeFilter(value);\n setPage(0);\n }}\n statusFilter={statusFilter}\n onStatusChange={(value) => {\n setStatusFilter(value);\n setPage(0);\n }}\n hasCommandFilter={hasCommandFilter}\n onHasCommandChange={(value) => {\n setHasCommandFilter(value);\n setPage(0);\n }}\n agentFilter={agentFilter}\n onAgentChange={(value) => {\n setAgentFilter(value);\n // Agent + Vibe are mutually exclusive via resourceId — clearing one when\n // the other is set keeps the resolved filter input coherent.\n if (value !== 'all') setVibeFilter('all');\n setPage(0);\n }}\n agents={agentsList}\n vibeFilter={vibeFilter}\n onVibeChange={(value) => {\n setVibeFilter(value);\n if (value !== 'all') setAgentFilter('all');\n setPage(0);\n }}\n vibes={vibesList}\n />\n\n {items.length > 0 ? (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedLogIds.size} selected\n </span>\n {selectedLogIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedLogIds(new Set())}\n className=\"text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n Clear selection\n </button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleOpenBulkDelete}\n disabled={!canDeleteAudit || selectedLogIds.size === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Delete Selected{selectedLogIds.size > 0 ? ` (${selectedLogIds.size})` : ''}\n </button>\n </div>\n </div>\n </div>\n ) : null}\n\n {loading && !data ? (\n <LoadingState message={tr('audit.loadingLogs', 'Loading audit logs...')} />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => refetch()} />\n ) : totalAuditItems === 0 ? (\n <EmptyState\n icon={<Shield size={48} className=\"text-text-secondary\" />}\n title={tr('audit.noLogs', 'No audit logs')}\n description={tr(\n 'audit.emptyDescription',\n 'Audit logs will appear here as operations are performed in VibeControls.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Audit Log in VibeControls and how do I use it?\" />\n }\n />\n ) : (\n <div data-tour=\"audit-log-table\">\n <AuditLogTable\n items={items}\n total={totalAuditItems}\n hasMore={data?.vibeAuditLogs.hasMore ?? false}\n page={page}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n onSelectLog={setSelectedLogId}\n onDeleteLog={handleOpenDelete}\n selectedLogId={selectedLogId}\n selectedLogIds={selectedLogIds}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n onToggleLogSelection={handleToggleLogSelection}\n onToggleAllVisible={handleToggleAllVisible}\n />\n </div>\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail\n log={detailData.vibeAuditLog}\n onClose={() => setSelectedLogId(null)}\n footer={\n detailData.vibeAuditLog.resourceType === 'SESSION' &&\n detailData.vibeAuditLog.resourceId &&\n detailData.vibeAuditLog.resourceId !== sessionDrillId ? (\n <button\n type=\"button\"\n onClick={() => {\n setSessionDrillId(detailData.vibeAuditLog?.resourceId ?? null);\n setSelectedLogId(null);\n setPage(0);\n }}\n className=\"w-full rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover\"\n >\n {tr('audit.showOnlyThisSession', \"Show only this session's activity\")}\n </button>\n ) : null\n }\n />\n ) : null}\n\n {deleteError ? (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg\"\n >\n {deleteError}\n <button\n type=\"button\"\n onClick={clearDeleteError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n {deleteSuccess ? (\n <div\n role=\"status\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg\"\n >\n {deleteSuccess}\n <button\n type=\"button\"\n onClick={clearDeleteSuccess}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n <AuditDeleteDialog\n isOpen={deleteTargetIds.length > 0}\n onClose={() => setDeleteTargetIds([])}\n onConfirm={handleDelete}\n loading={deleteLoading}\n title={deleteTargetIds.length > 1 ? 'Delete Audit Logs' : 'Delete Audit Log'}\n description={\n deleteTargetIds.length > 1\n ? `This will permanently delete ${deleteTargetIds.length} audit logs and record the reason in the audit trail.`\n : 'This action will be recorded in the audit trail.'\n }\n confirmLabel={deleteTargetIds.length > 1 ? 'Delete Logs' : 'Delete'}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VibeAuditPage.js","names":[],"sources":["../../src/pages/VibeAuditPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Shield } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuditDeleteDialog,\n AuditExportButton,\n AuditLogDetail,\n AuditLogFilters,\n AuditLogStats,\n AuditLogTable,\n} from '@/components/audit';\nimport {\n EmptyState,\n ErrorState,\n GetHelpButton,\n LoadingState,\n PageHeader,\n} from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n useBulkDeleteVibeAuditLogsMutation,\n useDeleteVibeAuditLogMutation,\n useGetVibeAuditLogQuery,\n useListAgentsQuery,\n useListVibesQuery,\n useVibeAuditDashboardQuery,\n} from '@/generated/wspace-operations';\nimport type {\n VibeAuditAction,\n VibeAuditLogFilterInput,\n VibeAuditResourceType,\n VibeAuditStatus,\n} from '@/generated/wspace-types';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nconst PAGE_SIZE = 50;\n\nexport function VibeAuditPage() {\n useVibeControls();\n\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');\n const [actionFilter, setActionFilter] = useState<string | null>('all');\n const [resourceTypeFilter, setResourceTypeFilter] = useState<string | null>('all');\n const [statusFilter, setStatusFilter] = useState<string | null>('all');\n const [agentFilter, setAgentFilter] = useState<string | null>('all');\n const [vibeFilter, setVibeFilter] = useState<string | null>('all');\n const [hasCommandFilter, setHasCommandFilter] = useState(false);\n const [page, setPage] = useState(0);\n // F2 — session-history drill-down. Initialised from a ?sessionId= URL param\n // (reciprocal link from the in-session History tab) and also settable by\n // clicking \"Show only this session\" on a SESSION-typed audit entry.\n const [sessionDrillId, setSessionDrillId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return new URLSearchParams(window.location.search).get('sessionId');\n });\n const [selectedLogId, setSelectedLogId] = useState<string | null>(null);\n const [selectedLogIds, setSelectedLogIds] = useState<Set<string>>(new Set());\n const [deleteTargetIds, setDeleteTargetIds] = useState<string[]>([]);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n const [deleteSuccess, setDeleteSuccess] = useState<string | null>(null);\n\n const deleteErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const canListAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_LIST);\n const canDeleteAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_DELETE);\n const canExportAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_EXPORT);\n const shouldSkipQueries = permissionsLoading || !canListAudit;\n\n const filter: VibeAuditLogFilterInput = {};\n if (debouncedSearchQuery) filter.search = debouncedSearchQuery;\n if (actionFilter && actionFilter !== 'all') filter.action = actionFilter as VibeAuditAction;\n if (resourceTypeFilter && resourceTypeFilter !== 'all')\n filter.resourceType = resourceTypeFilter as VibeAuditResourceType;\n if (statusFilter && statusFilter !== 'all') filter.status = statusFilter as VibeAuditStatus;\n if (hasCommandFilter) filter.hasCommand = true;\n // Agent / Vibe filters: backend exposes `resourceId` + `resourceType`.\n // We narrow `resourceType` accordingly so the server returns only audit\n // rows whose target is the chosen agent / vibe. If the user already\n // chose a resourceType that conflicts (e.g. SESSION), the resourceId\n // filter would over-narrow — but that combination is documented as\n // intentional power-user behaviour.\n if (agentFilter && agentFilter !== 'all') {\n filter.resourceId = agentFilter;\n if (!filter.resourceType) filter.resourceType = 'AGENT' as VibeAuditResourceType;\n } else if (vibeFilter && vibeFilter !== 'all') {\n filter.resourceId = vibeFilter;\n if (!filter.resourceType) filter.resourceType = 'VIBE' as VibeAuditResourceType;\n }\n // Session drill-down — the server matches resourceType=SESSION AND\n // resourceId=sessionId. Works for killed (soft-deleted) sessions too.\n if (sessionDrillId) filter.sessionId = sessionDrillId;\n\n const { data, loading, error, refetch } = useVibeAuditDashboardQuery({\n variables: {\n filter,\n pagination: { limit: Math.min(Math.max((page + 1) * PAGE_SIZE, PAGE_SIZE), 100) },\n },\n skip: shouldSkipQueries,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n });\n const agentsList = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string }) => ({ id: a.id, name: a.name })\n );\n\n const { data: vibesData } = useListVibesQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n });\n const vibesList = (vibesData?.vibes?.items ?? []).map((v: { id: string; name: string }) => ({\n id: v.id,\n name: v.name,\n }));\n\n const retentionDays = data?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90;\n const nextCleanupAt = data?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null;\n\n const { data: detailData } = useGetVibeAuditLogQuery({\n variables: { id: selectedLogId! },\n skip: !selectedLogId,\n });\n\n const [deleteAuditLog, { loading: deleteLoading }] = useDeleteVibeAuditLogMutation();\n const [bulkDeleteAuditLogs] = useBulkDeleteVibeAuditLogsMutation();\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n setPage(0);\n }, 300);\n\n return () => window.clearTimeout(timeoutId);\n }, [searchQuery]);\n\n useEffect(() => {\n return () => {\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n }\n };\n }, []);\n\n const clearDeleteError = useCallback(() => {\n setDeleteError(null);\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n deleteErrorTimer.current = null;\n }\n }, []);\n\n const clearDeleteSuccess = useCallback(() => {\n setDeleteSuccess(null);\n }, []);\n\n const allItems = data?.vibeAuditLogs.items ?? [];\n const items = allItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n const totalAuditItems = data?.vibeAuditLogs.total ?? 0;\n const visibleLogIds = items.map((item: (typeof items)[number]) => item.id);\n const allVisibleSelected =\n visibleLogIds.length > 0 && visibleLogIds.every((id: string) => selectedLogIds.has(id));\n const partiallySelected =\n visibleLogIds.some((id: string) => selectedLogIds.has(id)) && !allVisibleSelected;\n\n const handleDelete = async (reason: string) => {\n if (deleteTargetIds.length === 0) return;\n\n clearDeleteError();\n clearDeleteSuccess();\n\n try {\n if (deleteTargetIds.length === 1) {\n await deleteAuditLog({ variables: { id: deleteTargetIds[0], input: { reason } } });\n setDeleteSuccess('Deleted 1 audit log');\n } else {\n const result = await bulkDeleteAuditLogs({\n variables: { ids: deleteTargetIds, input: { reason } },\n });\n const deletedCount = result.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0;\n const failedCount = result.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;\n setDeleteSuccess(\n `Deleted ${deletedCount} audit logs${failedCount > 0 ? ` • ${failedCount} failed` : ''}`\n );\n }\n\n setDeleteTargetIds([]);\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n deleteTargetIds.forEach((id) => next.delete(id));\n return next;\n });\n if (selectedLogId && deleteTargetIds.includes(selectedLogId)) {\n setSelectedLogId(null);\n }\n await refetch();\n if (isNative()) void nativeNotify('success');\n } catch (deleteAuditError) {\n console.error('Failed to delete audit log:', deleteAuditError);\n setDeleteError(tr('audit.deleteFailed', 'Failed to delete audit log. Please try again.'));\n if (isNative()) void nativeNotify('error');\n deleteErrorTimer.current = setTimeout(() => {\n setDeleteError(null);\n deleteErrorTimer.current = null;\n }, 30000);\n }\n };\n\n const handleOpenDelete = useCallback(\n (id: string) => {\n if (!canDeleteAudit) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds([id]);\n },\n [canDeleteAudit, clearDeleteError, clearDeleteSuccess]\n );\n\n const handleToggleLogSelection = useCallback((id: string, checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n }, []);\n\n const handleToggleAllVisible = useCallback(\n (checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n visibleLogIds.forEach((id: string) => {\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return next;\n });\n },\n [visibleLogIds]\n );\n\n const handleOpenBulkDelete = useCallback(() => {\n if (!canDeleteAudit || selectedLogIds.size === 0) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds(Array.from(selectedLogIds));\n }, [canDeleteAudit, clearDeleteError, clearDeleteSuccess, selectedLogIds]);\n\n if (permissionsLoading) {\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <LoadingState message={tr('audit.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canListAudit) {\n return (\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'common.accessDeniedDescription',\n \"You don't have permission to access this resource.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.vibeAudit', 'Audit')}\n description={tr(\n 'audit.description',\n 'Comprehensive audit trail of all VibeControls operations'\n )}\n actions={canExportAudit ? <AuditExportButton filter={filter} /> : undefined}\n />\n\n <PagePurpose icon={<Shield className=\"size-4\" />}>\n {tr(\n 'audit.purpose',\n 'The audit trail records who did what across VibeControls — every agent registered, session started, setting changed and more — with timestamps and actors. Use it to investigate an incident, satisfy a compliance review, or simply understand a change after the fact. Filter and export the log for evidence.'\n )}\n </PagePurpose>\n\n {/* Retention banner — sourced from the backend `retentionPolicy`\n so the displayed window updates if the tier changes. */}\n <div className=\"mb-4 flex items-start gap-2 rounded-md border border-status-info-border bg-status-info-bg-subtle px-3 py-2 text-xs text-status-info-text\">\n <Shield size={14} className=\"mt-0.5 flex-shrink-0\" />\n <span>\n {tr(\n 'audit.retentionBanner',\n `Audit logs are kept for ${retentionDays} days. Older entries are automatically purged.`\n )}\n {nextCleanupAt\n ? ` ${tr('audit.retentionNextCleanup', `Next cleanup: ${new Date(nextCleanupAt).toLocaleString()}.`)}`\n : ''}\n </span>\n </div>\n\n {/* Session drill-down banner — shown when scoped to one session's activity. */}\n {sessionDrillId ? (\n <div className=\"mb-4 flex items-center justify-between gap-2 rounded-md border border-action-primary-border bg-[var(--color-accent-soft)] px-3 py-2 text-xs text-text-primary\">\n <span className=\"truncate\">\n {tr('audit.sessionScopeBanner', 'Showing activity for session')}{' '}\n <span className=\"font-mono\">{sessionDrillId}</span>\n </span>\n <button\n type=\"button\"\n onClick={() => {\n setSessionDrillId(null);\n setPage(0);\n }}\n className=\"flex-shrink-0 rounded bg-bg-sunken px-2 py-1 font-medium text-text-secondary hover:text-text-primary\"\n >\n {tr('audit.clearSessionScope', 'Clear')}\n </button>\n </div>\n ) : null}\n\n {/* AuditLogStats is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"audit-log-stats\">\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n </div>\n\n <AuditLogFilters\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n actionFilter={actionFilter}\n onActionChange={(value) => {\n setActionFilter(value);\n setPage(0);\n }}\n resourceTypeFilter={resourceTypeFilter}\n onResourceTypeChange={(value) => {\n setResourceTypeFilter(value);\n setPage(0);\n }}\n statusFilter={statusFilter}\n onStatusChange={(value) => {\n setStatusFilter(value);\n setPage(0);\n }}\n hasCommandFilter={hasCommandFilter}\n onHasCommandChange={(value) => {\n setHasCommandFilter(value);\n setPage(0);\n }}\n agentFilter={agentFilter}\n onAgentChange={(value) => {\n setAgentFilter(value);\n // Agent + Vibe are mutually exclusive via resourceId — clearing one when\n // the other is set keeps the resolved filter input coherent.\n if (value !== 'all') setVibeFilter('all');\n setPage(0);\n }}\n agents={agentsList}\n vibeFilter={vibeFilter}\n onVibeChange={(value) => {\n setVibeFilter(value);\n if (value !== 'all') setAgentFilter('all');\n setPage(0);\n }}\n vibes={vibesList}\n />\n\n {items.length > 0 ? (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedLogIds.size} selected\n </span>\n {selectedLogIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedLogIds(new Set())}\n className=\"text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n Clear selection\n </button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleOpenBulkDelete}\n disabled={!canDeleteAudit || selectedLogIds.size === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Delete Selected{selectedLogIds.size > 0 ? ` (${selectedLogIds.size})` : ''}\n </button>\n </div>\n </div>\n </div>\n ) : null}\n\n {loading && !data ? (\n <LoadingState message={tr('audit.loadingLogs', 'Loading audit logs...')} />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => refetch()} />\n ) : totalAuditItems === 0 ? (\n <EmptyState\n icon={<Shield size={48} className=\"text-text-secondary\" />}\n title={tr('audit.noLogs', 'No audit logs')}\n description={tr(\n 'audit.emptyDescription',\n 'Audit logs will appear here as operations are performed in VibeControls.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Audit Log in VibeControls and how do I use it?\" />\n }\n />\n ) : (\n <div data-tour=\"audit-log-table\">\n <AuditLogTable\n items={items}\n total={totalAuditItems}\n hasMore={data?.vibeAuditLogs.hasMore ?? false}\n page={page}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n onSelectLog={setSelectedLogId}\n onDeleteLog={handleOpenDelete}\n selectedLogId={selectedLogId}\n selectedLogIds={selectedLogIds}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n onToggleLogSelection={handleToggleLogSelection}\n onToggleAllVisible={handleToggleAllVisible}\n />\n </div>\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail\n log={detailData.vibeAuditLog}\n onClose={() => setSelectedLogId(null)}\n footer={\n detailData.vibeAuditLog.resourceType === 'SESSION' &&\n detailData.vibeAuditLog.resourceId &&\n detailData.vibeAuditLog.resourceId !== sessionDrillId ? (\n <button\n type=\"button\"\n onClick={() => {\n setSessionDrillId(detailData.vibeAuditLog?.resourceId ?? null);\n setSelectedLogId(null);\n setPage(0);\n }}\n className=\"w-full rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover\"\n >\n {tr('audit.showOnlyThisSession', \"Show only this session's activity\")}\n </button>\n ) : null\n }\n />\n ) : null}\n\n {deleteError ? (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg\"\n >\n {deleteError}\n <button\n type=\"button\"\n onClick={clearDeleteError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n {deleteSuccess ? (\n <div\n role=\"status\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg\"\n >\n {deleteSuccess}\n <button\n type=\"button\"\n onClick={clearDeleteSuccess}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n <AuditDeleteDialog\n isOpen={deleteTargetIds.length > 0}\n onClose={() => setDeleteTargetIds([])}\n onConfirm={handleDelete}\n loading={deleteLoading}\n title={deleteTargetIds.length > 1 ? 'Delete Audit Logs' : 'Delete Audit Log'}\n description={\n deleteTargetIds.length > 1\n ? `This will permanently delete ${deleteTargetIds.length} audit logs and record the reason in the audit trail.`\n : 'This action will be recorded in the audit trail.'\n }\n confirmLabel={deleteTargetIds.length > 1 ? 'Delete Logs' : 'Delete'}\n />\n </div>\n );\n}\n"],"mappings":"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"}