arkaos 3.33.0 → 3.34.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.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.33.0
1
+ 3.34.0
@@ -83,6 +83,13 @@ const links = [[{
83
83
  onSelect: () => {
84
84
  open.value = false
85
85
  }
86
+ }, {
87
+ label: 'Audit',
88
+ icon: 'i-lucide-shield-alert',
89
+ to: '/audit',
90
+ onSelect: () => {
91
+ open.value = false
92
+ }
86
93
  }, {
87
94
  label: 'Trash',
88
95
  icon: 'i-lucide-trash-2',
@@ -0,0 +1,125 @@
1
+ <script setup lang="ts">
2
+ // PR90d v3.34.0 — Audit log page.
3
+ //
4
+ // Lists hook bypass / block events from the enforcement telemetry log.
5
+ // Operators filter by kind (bypass / blocked) and tool name.
6
+
7
+ interface AuditEvent {
8
+ ts: string
9
+ tool: string
10
+ reason: string
11
+ cwd: string
12
+ bypass_used: boolean
13
+ kind: 'bypass' | 'blocked'
14
+ }
15
+
16
+ const { fetchApi } = useApi()
17
+
18
+ const kind = ref<'all' | 'bypass' | 'blocked'>('all')
19
+ const toolFilter = ref('')
20
+ const limit = ref(100)
21
+
22
+ const { data, status, error, refresh } = await fetchApi<{
23
+ events: AuditEvent[]
24
+ total: number
25
+ }>(
26
+ '/api/audit',
27
+ {
28
+ query: computed(() => ({
29
+ limit: limit.value,
30
+ kind: kind.value === 'all' ? undefined : kind.value,
31
+ tool: toolFilter.value.trim() || undefined,
32
+ })),
33
+ },
34
+ )
35
+
36
+ const events = computed<AuditEvent[]>(() => data.value?.events ?? [])
37
+
38
+ const kindOptions = [
39
+ { label: 'All kinds', value: 'all' },
40
+ { label: 'Bypass used', value: 'bypass' },
41
+ { label: 'Blocked', value: 'blocked' },
42
+ ]
43
+
44
+ function formatTs(ts: string): string {
45
+ if (!ts) return '—'
46
+ try {
47
+ return new Date(ts).toLocaleString()
48
+ } catch {
49
+ return ts
50
+ }
51
+ }
52
+
53
+ function kindColor(k: string): 'warning' | 'error' | 'neutral' {
54
+ return k === 'bypass' ? 'warning' : k === 'blocked' ? 'error' : 'neutral'
55
+ }
56
+
57
+ watch([kind, toolFilter, limit], async () => {
58
+ await refresh()
59
+ })
60
+ </script>
61
+
62
+ <template>
63
+ <UDashboardPanel id="audit">
64
+ <template #header>
65
+ <UDashboardNavbar title="Audit log">
66
+ <template #leading>
67
+ <UDashboardSidebarCollapse />
68
+ </template>
69
+ <template #trailing>
70
+ <UBadge v-if="data?.total" :label="`${data.total} event${data.total === 1 ? '' : 's'}`" variant="subtle" />
71
+ </template>
72
+ </UDashboardNavbar>
73
+ </template>
74
+
75
+ <template #body>
76
+ <DashboardState
77
+ :status="status"
78
+ :error="error"
79
+ :empty="!events.length"
80
+ empty-title="No audit events"
81
+ empty-description="Hook bypass + block events show up here. Nothing yet — good news."
82
+ empty-icon="i-lucide-shield-check"
83
+ loading-label="Loading audit"
84
+ :on-retry="() => refresh()"
85
+ >
86
+ <div class="flex flex-wrap items-center gap-3 mb-4">
87
+ <USelect
88
+ v-model="kind"
89
+ :items="kindOptions"
90
+ placeholder="Kind"
91
+ class="min-w-40"
92
+ aria-label="Filter by kind"
93
+ />
94
+ <UInput
95
+ v-model="toolFilter"
96
+ class="max-w-xs"
97
+ icon="i-lucide-wrench"
98
+ placeholder="Filter by tool…"
99
+ />
100
+ <span class="ml-auto text-xs text-muted">
101
+ {{ events.length }} match{{ events.length === 1 ? '' : 'es' }}
102
+ </span>
103
+ </div>
104
+
105
+ <ul class="space-y-2 max-w-5xl">
106
+ <li
107
+ v-for="(ev, idx) in events"
108
+ :key="`${ev.ts}-${idx}`"
109
+ class="rounded-lg border border-default p-3"
110
+ >
111
+ <div class="flex items-center gap-3 flex-wrap text-xs mb-1.5">
112
+ <UBadge :label="ev.kind" :color="kindColor(ev.kind)" variant="subtle" size="sm" />
113
+ <UBadge :label="ev.tool || '—'" variant="outline" size="sm" />
114
+ <span class="text-muted font-mono">{{ formatTs(ev.ts) }}</span>
115
+ </div>
116
+ <p class="text-sm">{{ ev.reason || '(no reason recorded)' }}</p>
117
+ <p v-if="ev.cwd" class="text-xs text-muted font-mono mt-1 truncate" :title="ev.cwd">
118
+ {{ ev.cwd }}
119
+ </p>
120
+ </li>
121
+ </ul>
122
+ </DashboardState>
123
+ </template>
124
+ </UDashboardPanel>
125
+ </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.33.0",
3
+ "version": "3.34.0",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "3.33.0"
3
+ version = "3.34.0"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -2251,6 +2251,58 @@ def _last_commit_days(project_path: str) -> Optional[int]:
2251
2251
  return None
2252
2252
 
2253
2253
 
2254
+ @app.get("/api/audit")
2255
+ def audit_log(
2256
+ limit: int = 100,
2257
+ kind: Optional[str] = None,
2258
+ tool: Optional[str] = None,
2259
+ ):
2260
+ """PR90d v3.34.0 — paginated audit log of bypass/block events.
2261
+
2262
+ Reads the same telemetry file as `_recent_incidents` but returns
2263
+ a larger window and accepts filter params. ``kind`` is one of
2264
+ ``"bypass"`` / ``"blocked"``. ``tool`` filters by tool name
2265
+ (Edit/Write/Bash/…).
2266
+ """
2267
+ log = Path.home() / ".arkaos" / "telemetry" / "enforcement.jsonl"
2268
+ if not log.exists():
2269
+ return {"events": [], "total": 0}
2270
+ try:
2271
+ text = log.read_text(encoding="utf-8", errors="replace")
2272
+ except OSError:
2273
+ return {"events": [], "total": 0}
2274
+ events: list[dict] = []
2275
+ cap = max(0, min(int(limit), 500))
2276
+ for line in reversed(text.splitlines()):
2277
+ if len(events) >= cap:
2278
+ break
2279
+ if not line.strip():
2280
+ continue
2281
+ try:
2282
+ entry = json.loads(line)
2283
+ except json.JSONDecodeError:
2284
+ continue
2285
+ bypass = bool(entry.get("bypass_used"))
2286
+ allowed = entry.get("allow")
2287
+ if not bypass and allowed is not False:
2288
+ continue
2289
+ row_kind = "bypass" if bypass else "blocked"
2290
+ if kind and kind != row_kind:
2291
+ continue
2292
+ row_tool = entry.get("tool", "")
2293
+ if tool and tool != row_tool:
2294
+ continue
2295
+ events.append({
2296
+ "ts": entry.get("ts", ""),
2297
+ "tool": row_tool,
2298
+ "reason": entry.get("reason", ""),
2299
+ "cwd": entry.get("cwd", ""),
2300
+ "bypass_used": bypass,
2301
+ "kind": row_kind,
2302
+ })
2303
+ return {"events": events, "total": len(events)}
2304
+
2305
+
2254
2306
  def _recent_incidents(limit: int = 8) -> list[dict]:
2255
2307
  """Recent enforcement / bypass events from telemetry.
2256
2308