Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
886af83
FEA-1550: Add SQLite to PGlite migration
mikeangstadt Jun 6, 2026
293b960
FEA-1550: Review feedback hardening for SQLite->PGlite migration
mikeangstadt Jun 7, 2026
1e107ac
FEA-1550: Resolve PGlite migration review gaps
mikeangstadt Jun 7, 2026
3b22e02
FEA-1550: Cut Agent Dashboard over to PGlite
mikeangstadt Jun 8, 2026
292d589
FEA-1550: Drop legacy dashboard SQLite and sidecar
mikeangstadt Jun 8, 2026
17630c0
FEA-1550: Restore PGlite dashboard parity
mikeangstadt Jun 8, 2026
efef290
FEA-1550: Restore Agent Dashboard boot-off check
mikeangstadt Jun 8, 2026
396cd5a
FEA-1550: Harden boot-off probe exit handling
mikeangstadt Jun 8, 2026
43e08f7
FEA-1550: Decouple listener tests from PGlite
mikeangstadt Jun 8, 2026
b667624
FEA-1550: Address PR #275 code review findings
mikeangstadt Jun 8, 2026
23c58a8
FEA-1550: Replace renderer-local controls
mikeangstadt Jun 8, 2026
754688f
FEA-1550: Address Agent Monitor review feedback
mikeangstadt Jun 8, 2026
edf1b31
FEA-1550: Port sidecar packs, plans, and PR features to first-party P…
mikeangstadt Jun 8, 2026
af76dfd
FEA-1550: Fix sidebar icon sizing and CSS import order
mikeangstadt Jun 8, 2026
d11071c
FEA-1550: Add missing sidecar animations and font fallbacks to render…
mikeangstadt Jun 8, 2026
cc1980c
FEA-1550: Fix pack install UI contract
mikeangstadt Jun 8, 2026
0252b13
FEA-1550: Stabilize dashboard startup and PRD worktrees
mikeangstadt Jun 8, 2026
193fa56
FEA-1550: Normalize pack catalog IPC contract
mikeangstadt Jun 8, 2026
99f902f
FEA-1550: Fix recent projects query for PGlite
mikeangstadt Jun 8, 2026
39c121c
FEA-1550: Normalize ported screen data contracts
mikeangstadt Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,15 @@ This rule does NOT apply to internal contracts that ship as a single unit with t
- **[mistake]**: Never fabricate history in changelogs, commit messages, or comments. Do not claim code "replaces" or "fixes" a prior implementation unless that implementation verifiably exists in the codebase or git history. (context: changelog|hallucination|fabrication)
- **[mistake]**: Before adding a fallback or recovery path, verify the triggering condition can actually occur. Dead fallbacks that read from files never written or variables never set create false confidence in error handling. (context: dead-code|fallback|unreachable)

### Agent Monitor & Sidecar Security
- **[mistake]**: Treat localhost sidecar routes and iframe messages as privileged surfaces. Mutating routes need origin/trusted-action guards, explicit target origins, and regression coverage. (context: agent-monitor|sidecar|security)
### Agent Monitor Security
- **[mistake]**: Treat localhost Agent Monitor hook/listener routes and renderer IPC as privileged surfaces. Mutating routes need explicit local-only binding, trusted-action guards where applicable, and regression coverage. (context: agent-monitor|listener|ipc|security)

### Process Spawning & Secrets
- **[mistake]**: Keep large or sensitive data out of spawned argv/env. Use stdin or files for prompts, quote shell args, set approved cwd, and pass minimal child environments. (context: spawn|argv|env|secrets)

### Boundary Validation
- **[mistake]**: Runtime-validate gateway, IPC, and persisted payloads before path or file use. TypeScript casts and preload promise types do not protect missing or null fields. (context: validation|ipc|gateway)

### Generated Agent Monitor Runtime
- **[pattern]**: When generated sidecar overlays, snippets, or patch inputs change, update stamp/materialization inputs and verify generated output so stale assets or bypassed patches cannot ship. (context: agent-monitor|generated|build)

### State & Lifecycle
- **[mistake]**: Setting toggles must update persisted state and in-memory side effects together. Avoid one-way restart guards, stale tray state, or stale cloud presence. (context: settings|lifecycle|state)

Expand Down
60 changes: 28 additions & 32 deletions apps/desktop/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,12 @@ The Diagnostics tab shows the current in-memory gateway log plus a bounded previ

## Agent Monitor

> **Status (FEA-1504):** Agent Monitor has three boot modes. The default user
> experience is the legacy sidecar-backed dashboard (`agentMonitorEnabled=true`,
> `agentDashboardDesignSystemEnabled=false`): pnpm-managed upstream packages are
> materialized into `.generated/agent-monitor`, shipped unpacked, and rendered in
> the legacy iframe shell. The in-process design-system dashboard is a Labs
> opt-in only. When `agentDashboardDesignSystemEnabled` is not the literal
> boolean `true`, the main process must not load `src/main/database/`,
> `src/main/collectors/`, `AgentHookListener`, `desktop:db:*`, or the `app://`
> design renderer path.
> **Status (FEA-1550):** Agent Monitor is an in-process, PGlite-backed desktop
> feature. The legacy generated runtime tree, embedded web shell, and
> `agentDashboardDesignSystemEnabled` boot split have been removed. When
> `agentMonitorEnabled=false`, the main process must not start collectors,
> `AgentHookListener`, the `desktop:db:*` IPC handlers, cloud session sync, or
> dashboard-derived cost reads.

The desktop app provides local Claude Code (and opt-in Codex) session/agent
observability. It powers the **Dashboard** and the agent nav items (Sessions,
Expand All @@ -155,23 +152,20 @@ is gated by the persisted `agentMonitorEnabled` desktop setting, which
**defaults ON**; when disabled, the agent nav items are hidden and only the
Gateway section remains.

- **Legacy sidecar (default):** `src/main/agent-monitor-sidecar.ts` launches the
generated Claude-Code-Agent-Monitor runtime tree. `build:agent-monitor`
materializes the tree from pnpm-managed upstream packages; package/stage logic
must keep `.generated/agent-monitor` available for default users.
- **Design-system runtime (Labs opt-in):** `src/main/agent-dashboard-design-system-runtime.ts`
- **Runtime:** `src/main/agent-dashboard-design-system-runtime.ts`
is the only module allowed to import `src/main/database/`,
`src/main/collectors/`, `AgentHookListener`, or register `desktop:db:*`. It is
reached only through `await import()` after boot mode resolves to
`design-system`.
- **Disabled mode:** `agentMonitorEnabled=false` starts no sidecar, no
design-system runtime, no dashboard-derived sync source, and no
dashboard-derived cost source.
reached only through `await import()` after the Agent Monitor setting is
enabled.
- **Disabled mode:** `agentMonitorEnabled=false` starts no design-system
runtime, no dashboard-derived sync source, and no dashboard-derived cost
source.
- **Hook listener:** in design-system mode, `src/main/agent-monitor-listener.ts`
binds `127.0.0.1:4820` in the main process and accepts the hook payload
(`POST /api/hooks/event`, `GET /api/health`). Each event is gated by the
FEA-1407 sandbox check, harness-stamped from `__provider`, and applied by the
lifecycle state machine.
(`POST /api/hooks/event`, `GET /api/health`). Each event is
Comment thread
mikeangstadt marked this conversation as resolved.
harness-stamped from `__provider` and applied by the lifecycle state machine.
Local import is ungated — all hook events are written to the local DB
regardless of the sandbox directory (FEA-1550).
- **Collection layer (`src/main/collectors/`):** design-system mode uses
`CollectorManager` for best-effort boot bulk import and live file watchers for
all five agent CLIs, writing through the first-party `importSession` into the
Expand All @@ -182,12 +176,12 @@ Gateway section remains.
`~/.claude/settings.json` at install time, so 4820 means hooks need zero
per-hook env. 4820 is outside `PORT_PROBE_ORDER`, so it never collides with
the gateway. (FEA-1500 tracks migrating this transport later.)
- **Durable DB:** `app.getPath("userData")/agent-dashboard.sqlite` (schema in
`src/main/database/schema.ts`), Node's built-in `node:sqlite`. Persisted
collector caches live under `<userData>/agent-monitor/`.
- **UI:** a first-party React app in the main window (`src/renderer/`) — NO
iframe. The left sidebar drives the **Dashboard** + agent nav items; live
updates arrive via the `desktop:db:changed` IPC push after each write.
- **Durable DB:** `app.getPath("userData")/agent-dashboard.pgdata` (PGlite,
schema in `src/main/database/pglite.ts`). Persisted collector caches live
under `<userData>/agent-dashboard-ingest/`.
- **UI:** a first-party React app in the main window (`src/renderer/`). The
left sidebar drives the **Dashboard** + agent nav items; live updates arrive
via the `desktop:db:changed` IPC push after each write.
- **Hooks are explicit opt-in (consent-bearing).** The user enables/disables
tracking via the toggle → `src/main/agent-monitor-hooks.ts` writes/removes the
hook entries in `~/.claude/settings.json` (and, opt-in, `~/.codex/hooks.json`).
Expand All @@ -206,10 +200,12 @@ Gateway section remains.
tree.
- **Security model (by design):** the collectors + listener read the agent-CLI
home dirs (`~/.claude`, `~/.codex`, …) **directly**, outside the gateway
`isPathAllowed` sandbox, but every captured session is dropped unless its
`cwd` is inside the FEA-1407 sandbox base directory (fail-closed). The listener
is bound to `127.0.0.1` only; no cloud egress from collectors. Hooks only
mutate global Claude/Codex config on explicit user opt-in and are reversible.
`isPathAllowed` sandbox. Local import and hook capture are ungated — all
sessions are written to the local PGlite DB regardless of working directory
(FEA-1550). Cloud sync sends all local sessions without sandbox filtering;
payloads are sanitized before upload. The listener is bound to `127.0.0.1`
only; no cloud egress from collectors. Hooks only mutate global Claude/Codex
config on explicit user opt-in and are reversible.
- **Multi-harness support (5 agent tools):** ingests sessions from **Claude
Code** (hooks live + file historical), **OpenAI Codex** (rollout JSONL under
`~/.codex/sessions/`), **Cursor** (agent transcripts under `~/.cursor/projects/`),
Expand Down
14 changes: 0 additions & 14 deletions apps/desktop/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@ extraResources:
to: trayIconTemplate.png
- from: resources/trayIconTemplate@2x.png
to: trayIconTemplate@2x.png
# Generated Claude-Code-Agent-Monitor runtime tree, shipped unpacked
# (outside the asar) so the spawned Node server, the built client, and the
# hook scripts resolve as real files. Built by scripts/build-agent-monitor.mjs
# before packaging (chained into `build`). `client/dist/**/*` (NOT
# `client/**/*`) — the server resolves ../client/dist relative to server/, so
# the server/ <-> client/dist/ layout must be preserved.
- from: .generated/agent-monitor
to: agent-monitor
filter:
- server/**/*
- client/dist/**/*
- scripts/**/*
- package.json
- LICENSE
# First-party agent-monitor hook handlers (FEA-1503), shipped unpacked outside
# the asar so they resolve as real files. agent-monitor-hooks.ts copies them
# into userData at install time; the installed hook command runs them via the
Expand Down
26 changes: 5 additions & 21 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
{
"name": "desktop",
"version": "0.15.117",
"version": "0.16.0",
"description": "ClosedLoop Desktop",
"author": "ClosedLoop AI <support@closedloop.ai>",
"private": true,
"type": "module",
"main": "dist/main/index.js",
"scripts": {
"dev": "pnpm build && ELECTRON_BIN=$(bash scripts/patch-electron-plist.sh | tail -1) && \"$ELECTRON_BIN\" .",
"start": "pnpm build:agent-monitor && ELECTRON_BIN=$(bash scripts/patch-electron-plist.sh | tail -1) && \"$ELECTRON_BIN\" .",
"start": "ELECTRON_BIN=$(bash scripts/patch-electron-plist.sh | tail -1) && \"$ELECTRON_BIN\" .",
"clean:dist": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
"clean:package": "node -e \"require('fs').rmSync('dist-dmg',{recursive:true,force:true})\"",
"prebuild": "node -e \"const{execSync:e}=require('child_process'),{writeFileSync:w}=require('fs');const h=e('git rev-parse HEAD').toString().trim();w('src/shared/build-info.ts','// AUTO-GENERATED — do not edit\\nexport const BUILD_COMMIT_HASH = \\\"'+h+'\\\";\\n');\"",
"build": "pnpm clean:dist && pnpm prebuild && tsc -p tsconfig.json && pnpm build:renderer && pnpm build:agent-monitor",
"build": "pnpm clean:dist && pnpm prebuild && tsc -p tsconfig.json && pnpm build:renderer",
"build:renderer": "vite build --config vite.renderer.config.ts",
"build:agent-monitor": "node scripts/build-agent-monitor.mjs",
"dashboard:reset": "node scripts/reset-dashboard-db.mjs",
"dashboard:reset-packs": "node scripts/reset-dashboard-db.mjs --packs-only",
"stage:package": "node scripts/stage-packaging-app.mjs",
"typecheck": "tsc -p tsconfig.json --noEmit && pnpm typecheck:renderer",
"typecheck:renderer": "tsc -p tsconfig.renderer.json --noEmit",
Expand All @@ -25,28 +23,15 @@
"test:boot:design-system-off": "node --import tsx scripts/assert-design-system-boot-off.mjs",
"measure:agent-dashboard-storage": "node scripts/measure-agent-dashboard-storage.mjs",
"verify:electron-binary": "node scripts/ensure-electron-binary.mjs",
"test": "tsx --test --test-concurrency=1 test/*.test.ts && node --test \"scripts/agent-monitor-packs/__tests__/*.test.js\" \"scripts/agent-monitor-pull-requests/__tests__/*.test.js\"",
"pretest:contract": "pnpm build:agent-monitor",
"test:contract": "node --test \"test-e2e/agent-monitor/specs/api-contract/*.test.mjs\"",
"pretest:e2e": "pnpm build:agent-monitor",
"test:e2e": "playwright test --config test-e2e/agent-monitor/playwright.config.ts",
"pretest:audit": "pnpm build:agent-monitor && pnpm audit:scan && pnpm audit:classify",
"audit:scan": "node test-e2e/agent-monitor/inventory/scan-tiles.mjs",
"audit:classify": "node test-e2e/agent-monitor/inventory/coverage-classifier.mjs",
"audit:coverage": "bash scripts/check-audit-coverage.sh",
"test:audit": "node --test \"test-e2e/agent-monitor/specs/audit/*.test.mjs\"",
"pretest:audit:ui": "pnpm build:agent-monitor",
"test:audit:ui": "playwright test --config test-e2e/agent-monitor/playwright.audit.config.ts",
"preaudit:report": "pnpm build:agent-monitor",
"audit:report": "node test-e2e/agent-monitor/inventory/run-report.mjs",
"test": "tsx --test --test-concurrency=1 test/*.test.ts",
Comment thread
mikeangstadt marked this conversation as resolved.
"package": "pnpm clean:package && pnpm build && pnpm stage:package && node scripts/run-electron-builder.mjs",
"release": "pnpm clean:package && pnpm build && pnpm stage:package && node scripts/run-electron-builder.mjs --publish always"
},
"dependencies": {
"@closedloop-ai/design-system": "0.1.1-dev.26892521643.1",
"@closedloop-ai/loops-api": ">=0.3.1",
"@electric-sql/pglite": "^0.4.6",
Comment thread
mikeangstadt marked this conversation as resolved.
"@pydantic/genai-prices": "0.0.62",
"agent-dashboard": "github:hoangsonww/Claude-Code-Agent-Monitor#840c518d7fa69231de049e41b893938228b67e40",
"busboy": "^1.6.0",
"electron-log": "^5.4.3",
"electron-store": "^8.2.0",
Expand All @@ -72,7 +57,6 @@
"@typescript-eslint/eslint-plugin": "^8.57.1",
"@typescript-eslint/parser": "^8.57.1",
"@vitejs/plugin-react": "^5.1.3",
"agent-dashboard-client": "github:hoangsonww/Claude-Code-Agent-Monitor#840c518d7fa69231de049e41b893938228b67e40&path:/client",
"autoprefixer": "10.4.20",
"electron": "^35.0.2",
"electron-builder": "^26.8.1",
Expand Down
Loading
Loading