From 60aa36b5e31e1a556a532f7d59591668e6e7b705 Mon Sep 17 00:00:00 2001 From: Ashish-dwi99 Date: Tue, 28 Apr 2026 18:53:21 +0530 Subject: [PATCH] Split public Dhee into Developer Brain --- .gitignore | 3 + CHANGELOG.md | 38 + MANIFEST.in | 4 +- README.md | 398 +- .../app/(dashboard)/activity/page.tsx | 5 - .../(dashboard)/beliefs/[beliefId]/page.tsx | 10 - .../app/(dashboard)/beliefs/page.tsx | 5 - .../app/(dashboard)/contradictions/page.tsx | 5 - .../app/(dashboard)/layout.tsx | 7 - apps/cognitive-debugger/app/globals.css | 63 - apps/cognitive-debugger/app/layout.tsx | 18 - apps/cognitive-debugger/app/page.tsx | 5 - .../__tests__/beliefs-workspace.test.tsx | 190 - .../contradictions-workspace.test.tsx | 106 - .../components/beliefs/activity-workspace.tsx | 101 - .../components/beliefs/beliefs-workspace.tsx | 607 --- .../beliefs/contradictions-workspace.tsx | 151 - .../components/beliefs/stat-card.tsx | 23 - .../components/beliefs/status-badge.tsx | 36 - .../components/layout/dashboard-shell.tsx | 20 - .../components/layout/sidebar.tsx | 137 - .../components/ui/badge.tsx | 29 - .../components/ui/button.tsx | 38 - .../cognitive-debugger/components/ui/card.tsx | 42 - .../cognitive-debugger/components/ui/tabs.tsx | 39 - apps/cognitive-debugger/next-env.d.ts | 6 - apps/cognitive-debugger/next.config.ts | 9 - apps/cognitive-debugger/package.json | 37 - apps/cognitive-debugger/pnpm-lock.yaml | 3395 ------------ apps/cognitive-debugger/postcss.config.js | 6 - apps/cognitive-debugger/tailwind.config.js | 34 - apps/cognitive-debugger/tsconfig.json | 24 - apps/cognitive-debugger/vitest.config.ts | 18 - apps/cognitive-debugger/vitest.setup.ts | 15 - dhee/__init__.py | 2 +- dhee/cli.py | 316 +- dhee/cli_onboard.py | 170 +- dhee/cli_update.py | 39 +- dhee/hooks/claude_code/__main__.py | 296 +- dhee/hooks/claude_code/renderer.py | 39 +- dhee/repo_link.py | 1172 ++++ dhee/ui/README.md | 110 - dhee/ui/__init__.py | 20 - dhee/ui/cli.py | 137 - dhee/ui/server.py | 4780 ----------------- dhee/ui/web/index.html | 17 - dhee/ui/web/package-lock.json | 2034 ------- dhee/ui/web/package.json | 24 - dhee/ui/web/public/dhee-logo.png | Bin 27501 -> 0 bytes dhee/ui/web/src/App.js | 331 -- dhee/ui/web/src/App.tsx | 449 -- dhee/ui/web/src/api.js | 174 - dhee/ui/web/src/api.ts | 378 -- dhee/ui/web/src/components/AssetDrawer.js | 358 -- dhee/ui/web/src/components/AssetDrawer.tsx | 559 -- dhee/ui/web/src/components/ChatMessage.js | 39 - dhee/ui/web/src/components/ChatMessage.tsx | 101 - dhee/ui/web/src/components/LinePanel.js | 387 -- dhee/ui/web/src/components/LinePanel.tsx | 639 --- dhee/ui/web/src/components/NavRail.js | 97 - dhee/ui/web/src/components/NavRail.tsx | 181 - dhee/ui/web/src/components/TweaksPanel.js | 84 - dhee/ui/web/src/components/TweaksPanel.tsx | 175 - .../src/components/WorkspaceManagerModal.js | 371 -- .../src/components/WorkspaceManagerModal.tsx | 865 --- .../src/components/canvas/CanvasControls.js | 88 - .../src/components/canvas/CanvasControls.tsx | 224 - .../src/components/canvas/CanvasSkeleton.js | 31 - .../src/components/canvas/CanvasSkeleton.tsx | 62 - .../src/components/canvas/DirectionHints.js | 44 - .../src/components/canvas/DirectionHints.tsx | 80 - dhee/ui/web/src/components/canvas/Minimap.js | 90 - dhee/ui/web/src/components/canvas/Minimap.tsx | 164 - dhee/ui/web/src/components/canvas/NodeCard.js | 170 - .../ui/web/src/components/canvas/NodeCard.tsx | 299 -- .../canvas/__tests__/layout.smoke.js | 68 - .../canvas/__tests__/layout.smoke.ts | 79 - dhee/ui/web/src/components/canvas/layout.js | 252 - dhee/ui/web/src/components/canvas/layout.ts | 305 -- .../components/canvas/useInfiniteCanvas.js | 383 -- .../components/canvas/useInfiniteCanvas.ts | 471 -- dhee/ui/web/src/components/cards/Cards.js | 172 - dhee/ui/web/src/components/cards/Cards.tsx | 403 -- .../src/components/graph/CanvasNodeCard.js | 53 - .../src/components/graph/CanvasNodeCard.tsx | 110 - dhee/ui/web/src/components/ui/DecayBar.js | 21 - dhee/ui/web/src/components/ui/DecayBar.tsx | 36 - .../ui/web/src/components/ui/SectionHeader.js | 20 - .../web/src/components/ui/SectionHeader.tsx | 36 - dhee/ui/web/src/components/ui/StatPill.js | 15 - dhee/ui/web/src/components/ui/StatPill.tsx | 26 - dhee/ui/web/src/components/ui/TierBadge.js | 41 - dhee/ui/web/src/components/ui/TierBadge.tsx | 49 - dhee/ui/web/src/main.js | 6 - dhee/ui/web/src/main.tsx | 10 - dhee/ui/web/src/styles.css | 148 - dhee/ui/web/src/types.js | 1 - dhee/ui/web/src/types.ts | 600 --- dhee/ui/web/src/views/CanvasView.js | 411 -- dhee/ui/web/src/views/CanvasView.tsx | 762 --- dhee/ui/web/src/views/ChannelView.js | 295 - dhee/ui/web/src/views/ChannelView.tsx | 653 --- dhee/ui/web/src/views/ConflictView.js | 308 -- dhee/ui/web/src/views/ConflictView.tsx | 576 -- dhee/ui/web/src/views/EvolutionView.js | 321 -- dhee/ui/web/src/views/EvolutionView.tsx | 629 --- dhee/ui/web/src/views/LaunchView.js | 294 - dhee/ui/web/src/views/LaunchView.tsx | 601 --- dhee/ui/web/src/views/MemoryView.js | 461 -- dhee/ui/web/src/views/MemoryView.tsx | 863 --- dhee/ui/web/src/views/NotepadView.js | 247 - dhee/ui/web/src/views/NotepadView.tsx | 484 -- dhee/ui/web/src/views/RouterView.js | 535 -- dhee/ui/web/src/views/RouterView.tsx | 932 ---- dhee/ui/web/src/views/TasksView.js | 71 - dhee/ui/web/src/views/TasksView.tsx | 168 - dhee/ui/web/src/views/WorkspaceView.js | 599 --- dhee/ui/web/src/views/WorkspaceView.tsx | 1209 ----- dhee/ui/web/tsconfig.json | 21 - dhee/ui/web/tsconfig.tsbuildinfo | 1 - dhee/ui/web/vite.config.ts | 19 - install.sh | 22 +- pyproject.toml | 22 +- tests/test_cli_onboard_update.py | 122 +- tests/test_project_assets_api.py | 302 -- tests/test_project_workspace_session_api.py | 247 - tests/test_repo_link.py | 548 ++ tests/test_ui_memory_os_api.py | 190 - tests/test_workspace_project_crud_api.py | 342 -- 129 files changed, 2551 insertions(+), 34229 deletions(-) delete mode 100644 apps/cognitive-debugger/app/(dashboard)/activity/page.tsx delete mode 100644 apps/cognitive-debugger/app/(dashboard)/beliefs/[beliefId]/page.tsx delete mode 100644 apps/cognitive-debugger/app/(dashboard)/beliefs/page.tsx delete mode 100644 apps/cognitive-debugger/app/(dashboard)/contradictions/page.tsx delete mode 100644 apps/cognitive-debugger/app/(dashboard)/layout.tsx delete mode 100644 apps/cognitive-debugger/app/globals.css delete mode 100644 apps/cognitive-debugger/app/layout.tsx delete mode 100644 apps/cognitive-debugger/app/page.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/__tests__/beliefs-workspace.test.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/__tests__/contradictions-workspace.test.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/activity-workspace.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/beliefs-workspace.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/contradictions-workspace.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/stat-card.tsx delete mode 100644 apps/cognitive-debugger/components/beliefs/status-badge.tsx delete mode 100644 apps/cognitive-debugger/components/layout/dashboard-shell.tsx delete mode 100644 apps/cognitive-debugger/components/layout/sidebar.tsx delete mode 100644 apps/cognitive-debugger/components/ui/badge.tsx delete mode 100644 apps/cognitive-debugger/components/ui/button.tsx delete mode 100644 apps/cognitive-debugger/components/ui/card.tsx delete mode 100644 apps/cognitive-debugger/components/ui/tabs.tsx delete mode 100644 apps/cognitive-debugger/next-env.d.ts delete mode 100644 apps/cognitive-debugger/next.config.ts delete mode 100644 apps/cognitive-debugger/package.json delete mode 100644 apps/cognitive-debugger/pnpm-lock.yaml delete mode 100644 apps/cognitive-debugger/postcss.config.js delete mode 100644 apps/cognitive-debugger/tailwind.config.js delete mode 100644 apps/cognitive-debugger/tsconfig.json delete mode 100644 apps/cognitive-debugger/vitest.config.ts delete mode 100644 apps/cognitive-debugger/vitest.setup.ts create mode 100644 dhee/repo_link.py delete mode 100644 dhee/ui/README.md delete mode 100644 dhee/ui/__init__.py delete mode 100644 dhee/ui/cli.py delete mode 100644 dhee/ui/server.py delete mode 100644 dhee/ui/web/index.html delete mode 100644 dhee/ui/web/package-lock.json delete mode 100644 dhee/ui/web/package.json delete mode 100644 dhee/ui/web/public/dhee-logo.png delete mode 100644 dhee/ui/web/src/App.js delete mode 100644 dhee/ui/web/src/App.tsx delete mode 100644 dhee/ui/web/src/api.js delete mode 100644 dhee/ui/web/src/api.ts delete mode 100644 dhee/ui/web/src/components/AssetDrawer.js delete mode 100644 dhee/ui/web/src/components/AssetDrawer.tsx delete mode 100644 dhee/ui/web/src/components/ChatMessage.js delete mode 100644 dhee/ui/web/src/components/ChatMessage.tsx delete mode 100644 dhee/ui/web/src/components/LinePanel.js delete mode 100644 dhee/ui/web/src/components/LinePanel.tsx delete mode 100644 dhee/ui/web/src/components/NavRail.js delete mode 100644 dhee/ui/web/src/components/NavRail.tsx delete mode 100644 dhee/ui/web/src/components/TweaksPanel.js delete mode 100644 dhee/ui/web/src/components/TweaksPanel.tsx delete mode 100644 dhee/ui/web/src/components/WorkspaceManagerModal.js delete mode 100644 dhee/ui/web/src/components/WorkspaceManagerModal.tsx delete mode 100644 dhee/ui/web/src/components/canvas/CanvasControls.js delete mode 100644 dhee/ui/web/src/components/canvas/CanvasControls.tsx delete mode 100644 dhee/ui/web/src/components/canvas/CanvasSkeleton.js delete mode 100644 dhee/ui/web/src/components/canvas/CanvasSkeleton.tsx delete mode 100644 dhee/ui/web/src/components/canvas/DirectionHints.js delete mode 100644 dhee/ui/web/src/components/canvas/DirectionHints.tsx delete mode 100644 dhee/ui/web/src/components/canvas/Minimap.js delete mode 100644 dhee/ui/web/src/components/canvas/Minimap.tsx delete mode 100644 dhee/ui/web/src/components/canvas/NodeCard.js delete mode 100644 dhee/ui/web/src/components/canvas/NodeCard.tsx delete mode 100644 dhee/ui/web/src/components/canvas/__tests__/layout.smoke.js delete mode 100644 dhee/ui/web/src/components/canvas/__tests__/layout.smoke.ts delete mode 100644 dhee/ui/web/src/components/canvas/layout.js delete mode 100644 dhee/ui/web/src/components/canvas/layout.ts delete mode 100644 dhee/ui/web/src/components/canvas/useInfiniteCanvas.js delete mode 100644 dhee/ui/web/src/components/canvas/useInfiniteCanvas.ts delete mode 100644 dhee/ui/web/src/components/cards/Cards.js delete mode 100644 dhee/ui/web/src/components/cards/Cards.tsx delete mode 100644 dhee/ui/web/src/components/graph/CanvasNodeCard.js delete mode 100644 dhee/ui/web/src/components/graph/CanvasNodeCard.tsx delete mode 100644 dhee/ui/web/src/components/ui/DecayBar.js delete mode 100644 dhee/ui/web/src/components/ui/DecayBar.tsx delete mode 100644 dhee/ui/web/src/components/ui/SectionHeader.js delete mode 100644 dhee/ui/web/src/components/ui/SectionHeader.tsx delete mode 100644 dhee/ui/web/src/components/ui/StatPill.js delete mode 100644 dhee/ui/web/src/components/ui/StatPill.tsx delete mode 100644 dhee/ui/web/src/components/ui/TierBadge.js delete mode 100644 dhee/ui/web/src/components/ui/TierBadge.tsx delete mode 100644 dhee/ui/web/src/main.js delete mode 100644 dhee/ui/web/src/main.tsx delete mode 100644 dhee/ui/web/src/styles.css delete mode 100644 dhee/ui/web/src/types.js delete mode 100644 dhee/ui/web/src/types.ts delete mode 100644 dhee/ui/web/src/views/CanvasView.js delete mode 100644 dhee/ui/web/src/views/CanvasView.tsx delete mode 100644 dhee/ui/web/src/views/ChannelView.js delete mode 100644 dhee/ui/web/src/views/ChannelView.tsx delete mode 100644 dhee/ui/web/src/views/ConflictView.js delete mode 100644 dhee/ui/web/src/views/ConflictView.tsx delete mode 100644 dhee/ui/web/src/views/EvolutionView.js delete mode 100644 dhee/ui/web/src/views/EvolutionView.tsx delete mode 100644 dhee/ui/web/src/views/LaunchView.js delete mode 100644 dhee/ui/web/src/views/LaunchView.tsx delete mode 100644 dhee/ui/web/src/views/MemoryView.js delete mode 100644 dhee/ui/web/src/views/MemoryView.tsx delete mode 100644 dhee/ui/web/src/views/NotepadView.js delete mode 100644 dhee/ui/web/src/views/NotepadView.tsx delete mode 100644 dhee/ui/web/src/views/RouterView.js delete mode 100644 dhee/ui/web/src/views/RouterView.tsx delete mode 100644 dhee/ui/web/src/views/TasksView.js delete mode 100644 dhee/ui/web/src/views/TasksView.tsx delete mode 100644 dhee/ui/web/src/views/WorkspaceView.js delete mode 100644 dhee/ui/web/src/views/WorkspaceView.tsx delete mode 100644 dhee/ui/web/tsconfig.json delete mode 100644 dhee/ui/web/tsconfig.tsbuildinfo delete mode 100644 dhee/ui/web/vite.config.ts delete mode 100644 tests/test_project_assets_api.py delete mode 100644 tests/test_project_workspace_session_api.py create mode 100644 tests/test_repo_link.py delete mode 100644 tests/test_ui_memory_os_api.py delete mode 100644 tests/test_workspace_project_crud_api.py diff --git a/.gitignore b/.gitignore index c6a0d51..8fbaeb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Private enterprise SKU — never commit to public repo. +enterprise/ + # Python __pycache__/ *.py[cod] diff --git a/CHANGELOG.md b/CHANGELOG.md index 798ae54..c5d17ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,44 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). +## Unreleased — Developer Brain split + +- Public Dhee is now positioned and packaged as **Dhee Developer Brain**: + local memory, handoff, harness setup, and git-backed repo context. +- Removed the public web UI package and `dhee ui` command surface. The + enterprise dashboard and commercial code now live in the private + `dhee-enterprise` repository. +- Added repo-shared context commands: `dhee link`, `dhee unlink`, + `dhee links`, `dhee promote`, `dhee demote`, and `dhee context`. +- Repo-shared context uses append-only `.dhee/context/entries.jsonl` with + conflict detection for concurrent developer edits. + +## [6.1.0] - 2026-04-24 — Injection cleanup + +Three surgical fixes to the `` UserPromptSubmit injection so +stale and irrelevant context never leaks into a turn. + +- `dhee/db/sqlite_analytics.py` — new `close_stale_shared_tasks()` bulk + closes any `status='active'` row whose `updated_at` is older than the + configured window (default 24h). ISO-8601 strings sort lexicographically + in SQLite, so the comparison is index-friendly with no per-row parsing. +- `dhee/core/shared_tasks.py` — `shared_task_snapshot()` now calls the + bulk close before resolving, and adds a strict repo filter via + `_task_matches_repo()` so a task anchored on a sibling repo never + surfaces in the active workspace's snapshot. +- `dhee/router/edit_ledger.py` — `record()` now stamps `DHEE_SESSION_ID` + and the recording cwd; `summarise()` filters by session, repo, and a + 6-hour freshness window, and unconditionally drops `/tmp/`, + `/private/tmp/`, and `/var/folders/` paths. Backward-compat: rows + missing the new fields lapse out via the freshness window. +- `dhee/hooks/claude_code/__main__.py` — `handle_user_prompt` now gates + the `` block on per-turn embedder cosine similarity between + the prompt and the task title + last result digest. Default threshold + 0.50, override via `DHEE_SHARED_RELEVANCE_THRESHOLD`. Fails closed on + embedder errors — better to drop than re-emit the noise. +- Tests: `tests/test_oss_61_quickfix.py` covers the three fixes + end-to-end (auto-close, repo filter, ledger filters, relevance gate). + ## [5.1.0] - 2026-04-21 — gstack adapter Third first-class harness target: `dhee install gstack`. gstack (Garry diff --git a/MANIFEST.in b/MANIFEST.in index 3cf7822..967a0e4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,6 @@ prune dhee/ui/web/node_modules +prune dhee/ui +prune enterprise prune engram-bus prune engram-bridge prune engram-enterprise @@ -7,5 +9,3 @@ prune engram-heartbeat prune engram-identity prune engram-metamemory prune engram-policy - -recursive-include dhee/ui/web/dist * diff --git a/README.md b/README.md index a40de0b..3651aaf 100644 --- a/README.md +++ b/README.md @@ -1,381 +1,101 @@ -

- Dhee -

+# Dhee Developer Brain -

Dhee — the shared information layer for screen-aware AI agents

+Dhee is a local memory layer for AI coding agents. -

- Git for AI agents. One memory line. Every agent — Claude Code, Codex, Cursor, - browser bots — reads from it, writes to it, coordinates through it. In real time. -

+It gives Claude Code, Codex, and MCP-compatible tools a durable developer brain: +personal memory, repo-shared context, session handoff, and git-backed team +knowledge without requiring a hosted service. -

- PyPI - MIT - Python 3.9+ -

+The public Dhee project stays simple: -

- Quick Start · - Why Dhee · - How It Works · - The Channel · - Integrations -

+- one-command install +- local encrypted key storage +- automatic Claude Code/Codex harness setup +- folder and git-repo context linking +- repo-shared context through `.dhee/context/` +- MCP tools for memory, handoff, and shared context ---- +The enterprise dashboard, org controls, code-intelligence Repo Brain, +commercial licensing, Sentry telemetry, and paid team workflows live in the +private `dhee-enterprise` repository. -## The problem - -Every AI agent shows up like a new employee. - -Claude Code, Codex, Cursor, Gemini CLI, browser agents — they all work on the same codebase. **None of them share what they learn.** - -- 9:00 AM — *"Here's the codebase…"* you explain it to Claude Code. -- 11:30 AM — *"Here's the codebase…"* you explain it to Codex. Again. -- 2:15 PM — *"Here's the codebase…"* you explain it to Cursor. Still again. - -Claude Code hits its session limit? You copy-paste context into Codex, re-upload the spec PDF, re-explain the decisions you already made. Every developer using multiple agents is paying this tax every day. - -## The solution — git for AI agents - -Dhee is a **shared information layer**. One workspace → many projects → every agent session plugs into the same live memory. - -- An agent reads a file, processes an asset, runs a tool — the digest lands on the shared line in real time. -- Another agent, different tool, different project in the same workspace — sees it instantly. No re-upload, no re-explain. -- Backend agent broadcasts an API contract change → a task auto-spawns in the frontend project. Claude Code picks it up and ships the UI. -- Every workspace is a repo for the agents' *knowledge*, the way git is a repo for code. - -**The philosophy:** share a single information layer; agents connect and collaborate through it. - ---- - -## Quick start +## Quick Start ```bash curl -fsSL https://raw.githubusercontent.com/Sankhya-AI/Dhee/main/install.sh | sh ``` -The installer: - -1. Creates `~/.dhee` with a self-contained venv. -2. Installs `dhee[app]` from PyPI. -3. Prompts you for a **provider** (OpenAI default, Gemini, NVIDIA, or Ollama). -4. Prompts for your **API key** (stored encrypted under `~/.dhee/secret_store.enc.json`). -5. Wires Claude Code hooks + MCP server + context router. -6. Builds the web UI. +Then restart your agent session. -Then: +Useful commands: ```bash -dhee ui # opens http://127.0.0.1:8080/ in your browser -dhee update # pull the latest release + rebuild UI +dhee install # configure supported local agent harnesses +dhee link /path/to/repo # share context through this git repo +dhee links # list linked repos +dhee context refresh # refresh repo context after pull/checkout +dhee context check # detect unresolved shared-context conflicts +dhee handoff # compact continuity for the current repo/session +dhee key set openai # store a provider key locally +dhee update # update the local install ``` -Non-interactive flow for CI / scripted installs: +## Repo-Shared Context -```bash -DHEE_PROVIDER=openai DHEE_API_KEY=sk-... \ - curl -fsSL https://raw.githubusercontent.com/Sankhya-AI/Dhee/main/install.sh | sh -``` - -
-Other install options +When you run: -**Via pip:** ```bash -pip install dhee -dhee install --harness all # configure Claude Code + Codex +dhee link /path/to/repo ``` -**From source:** -```bash -git clone https://github.com/Sankhya-AI/Dhee.git -cd Dhee -./scripts/bootstrap_dev_env.sh -source .venv-dhee/bin/activate -dhee install --harness all -``` - -**Via Docker:** -```bash -docker compose up -d # uses OPENAI_API_KEY from env -``` -
- -After install: -- Claude Code uses native hooks for routing, memory updates, and shared-task context injection. -- Codex uses native `config.toml` + Dhee-managed instructions + incremental event-stream sync, so post-tool results and uploaded artifacts become shared reusable context without manual re-sync. -- `dhee harness status` shows the live state and `dhee harness disable --harness codex` turns a harness off cleanly. - -### Third-party skill packs: `dhee install gstack` +Dhee creates: -Running [gstack](https://github.com/garrytan/gstack)? `dhee install gstack` -wires its siloed `~/.gstack/projects/*` memory into the same Dhee pipeline -as everything else — semantic search, consolidation, correction, episodic -recall — without touching any gstack files. See -[docs/adapters/gstack.md](docs/adapters/gstack.md). - -Project docs (CLAUDE.md, AGENTS.md, SKILL.md, etc.) still auto-ingest on first use. Run `dhee ingest` manually any time to re-chunk. - ---- - -## Benchmarks - -> **#1 on LongMemEval recall.** R@1 94.8%, R@5 99.4%, R@10 99.8% — full 500 questions, no held-out split, no cherry-picking. - -| System | R@1 | R@3 | R@5 | R@10 | -|:-------|:----|:----|:----|:-----| -| **Dhee v3.4.0** | **94.8%** | **99.0%** | **99.4%** | **99.8%** | -| [MemPalace](https://github.com/MemPalace/mempalace#benchmarks) (raw) | — | — | 96.6% | — | -| [MemPalace](https://github.com/MemPalace/mempalace#benchmarks) (hybrid v4, held-out 450q) | — | — | 98.4% | — | -| [agentmemory](https://github.com/rohitg00/agentmemory#benchmarks) | — | — | 95.2% | 98.6% | - -Stack: NVIDIA `llama-nemotron-embed-vl-1b-v2` embedder + `llama-3.2-nv-rerankqa-1b-v2` reranker, top-k 10. - -**Proof is in-tree, not screenshots.** Exact command, metrics, and per-question output are committed under [`benchmarks/longmemeval/`](benchmarks/longmemeval/). Recompute R@k yourself — any mismatch is a bug you can open. - ---- - -## What Dhee does on every turn - -``` - ┌──────────────────────────┐ - │ Your fat context │ - │ CLAUDE.md, AGENTS.md, │ - │ SKILL.md, GBrain, docs │ - │ session history, tools │ - └──────────┬───────────────┘ - │ - Dhee ingests once - (chunk + embed + index) - │ - ▼ - ┌───────────────────────────────────────────────────────────────┐ - │ Dhee memory + cognition │ - │ │ - │ Doc chunks · Short-term · Long-term · Insights · Beliefs │ - │ Policies · Intentions · Performance · Episodes · Edits │ - └────────────────────────────┬──────────────────────────────────┘ - │ - ┌────────────────────┴────────────────────┐ - │ │ - Session start Each user prompt - (full assembly) (doc chunks only) - │ │ - ▼ ▼ - ┌───────────────┐ ┌───────────────┐ - │ Relevant docs │ │ Matching rules │ - │ + insights │ │ above threshold│ - │ + performance │ │ or nothing │ - │ + warnings │ │ │ - └───────┬───────┘ └───────┬───────┘ - │ │ - └────────────────────┬────────────────────┘ - │ - ▼ - ┌──────────────────────────────┐ - │ Token-budgeted XML │ - │ │ - │ │ - │ What worked last time│ - │ │ - └──────────────────────────────┘ - │ - LLM sees only what it - needs, when it needs it. +```text +/.dhee/config.json +/.dhee/context/manifest.json +/.dhee/context/entries.jsonl ``` -And on the tool-use side, the **router** digests raw output at source — a 10 MB `git log` becomes a 40-token summary + a pointer. The model expands the pointer only when the digest isn't enough. - ---- - -## vs alternatives - -| | **Dhee** | CLAUDE.md | Mem0 | Letta | MemPalace | agentmemory | -|:--|:-:|:-:|:-:|:-:|:-:|:-:| -| **Token cost per turn** | **router-replay projected¹** | 2,000+ | varies | ~1K+ | varies | ~1,900 | -| **LongMemEval R@5** | **99.4%** | N/A | N/A | N/A | 96.6% | 95.2% | -| **Self-evolving retrieval policy** | **Yes** | No | No | No | No | No | -| **Auto-digest tool output** | **Yes (router)** | No | No | No | No | No | -| **Works across every MCP agent** | **Yes** | No | Partial | No | Yes | Yes | -| **Typed cognition (insights/beliefs/policies)** | **Yes** | No | No | Partial | No | No | -| **Proof in-tree (reproducible)** | **Yes** | — | — | — | Yes | Partial | -| **External DB required** | No (SQLite) | No | Qdrant/pgvector | Postgres+vector | No | No | -| **License** | MIT | — | Apache-2 | Apache-2 | MIT | MIT | - -Dhee is the only one that **routes tool output at source *and* self-evolves its retrieval policy from an expansion ledger *and* leads on LongMemEval recall.** - -¹ Run `dhee router report` to see the actual per-turn token curve on your sessions. The replay corpus is already built from your real activity — `dhee replay-corpus export` derives it from the durable samskara log, no synthetic data. A redacted public corpus with reproducible numbers lands in the next release. - ---- - -## Self-evolution — the part nobody else does - -Most memory layers are static: you write rules, they retrieve. Dhee watches what happens and tunes itself. - -- **Intent classification**: Every `Read`/`Bash`/`Agent` call is bucketed by intent (source code, test, config, doc, data, build). Each intent gets its own retrieval depth. -- **Expansion tracking**: When the model calls `dhee_expand_result(ptr)`, Dhee logs which tool / intent / depth required expansion. A digest that's always expanded is too shallow. A digest that's never expanded might be too deep. -- **Policy tuning**: `dhee router tune` reads the expansion ledger, applies thresholds (>30% expansion → deepen; <5% expansion → make shallower), and persists the new policy atomically to `~/.dhee/router_policy.json`. -- **No config file to maintain.** The policy is the behavior. The behavior is learned. - -This means: **the longer a team uses Dhee, the better it gets at serving that specific team's workflow.** Frontend-heavy teams get deeper JS/TS digests. Data teams get richer CSV/JSONL summaries. You don't have to pick — Dhee picks for you, based on what you actually expand. - ---- - -## Perfect recall, five years in +Those files can be committed with the repo. Teammates who pull the repo get the +same shared context after installing Dhee. -Doc bloat is every AI team's silent tax. Your CLAUDE.md grew from 50 lines to 500 to 2,000 in 18 months. Your skills directory has 30 files. Your prompt library has a thousand entries. Every new agent session loads all of it, every turn, at full token cost. +Shared context is append-only and git-friendly. If two developers edit the same +context at the same time, Dhee keeps both versions and reports a conflict +instead of silently overwriting one developer's work. -Dhee turns that library into searchable, decay-aware, self-promoting memory: - -| Phase | What Dhee does | -|:------|:---------------| -| **Ingest** | Heading-scoped chunking with SHA tracking. Re-ingest is a no-op if unchanged. | -| **Hot path** | Vector search + heading-breadcrumb matching. Filters by threshold + token budget. | -| **Promotion** | Frequently-referenced memories auto-promote from short-term to long-term. | -| **Decay** | Unused memories fade on an Ebbinghaus curve. Your 50th memory costs the same as your 50,000th. | -| **Insight synthesis** | What-worked / what-failed from session checkpoints becomes transferable learnings. | -| **Prospective** | `"Remember to run auth tests after login.py changes"` fires when the trigger matches — days, weeks, or months later. | - -**Target shape:** after a year of accumulation the per-turn injection stays bounded by token budget — *only* the matching slice of docs, insights, and policies above threshold reaches the model. The full canonical-tier retention guarantee (100% canonical survival across supersede chains on a decades-replay corpus) lands with movements 2–3 of the public plan. The substrate (propositional facts, supersede-ready schema, decay/promotion) ships today. - ---- - -## Future-proof your fat skills - -Got a GBrain? An AGENTS.md with 15 sections? A Skills library that your team reluctantly prunes every sprint because it "got too big for context"? Stop pruning. Dhee was built for this. - ---- - -## Why Dhee - -| What you feel today | What Dhee replaces it with | -|---|---| -| Every new agent session starts from scratch | Every agent joins a live memory line | -| Copy-pasting context between Claude Code and Codex | Cross-runtime broadcasts with auto-created tasks | -| Re-uploading the same PDF / design export / schema | Project-scoped asset drawer; upload once, all agents benefit | -| Fat `CLAUDE.md` burned on every call | Heading-scoped retrieval — the agent sees the 200 tokens that matter, not the 5,000 that don't | -| Tool output dumps bloating context | Router digests + pointers; raw stays out of the conversation | -| "Session limit reached" forces a painful restart | Line survives; resume in another runtime with full context | - -**Measured savings:** 428k tokens → 197k tokens on a real dev session (−54%). Your own numbers are visible in `dhee router stats` anytime. - ---- - -## The channel — where agents collaborate - -`dhee ui` opens a three-column workspace: - -**Left rail.** Workspace picker + project tree. "+ new / manage" button opens a full CRUD dialog — create `Office`, `Personal`, `Sankhya AI Labs`; each with many projects (`frontend`, `backend`, `design`, …). Rename, re-root, delete. **"Connected agents"** block shows every runtime with a live dot: `claude-code · live`, `codex · live`, `browser agent · live`. - -**Centre.** The live information line. Every tool call an agent makes lands here, newest first, attributed: - -``` -10:02 · claude-code · backend "read schema.sql (R-abc)" -10:04 · codex · frontend "broadcast · api contract changed" -10:09 · browser agent "verified fix on staging checkout" -10:14 · claude-code "merged PR #412 · closing thread" +```bash +dhee context check --repo /path/to/repo ``` -Kind filters: `all / broadcasts / tool events / notes`. - -**Right rail.** Broadcast composer — title + target-project picker. Broadcasting into another project **auto-creates a task there**. Asset drawer — drag a spec PDF, design export, or schema into the project; every agent in the workspace sees it, with a "processed by codex · 2m ago" feed under each asset. - -**Canvas view.** Openswarm-inspired infinite canvas. DOM cards per entity (workspace → project → session → task → result), smooth pan / momentum / pinch zoom, minimap, direction hints, skeleton loader. One view of the whole brain. - ---- - -## How it works - -Three substrates, one product: - -### 1. The information line (push-based pub/sub) +The installed `pre-push` hook blocks unresolved Dhee context conflicts. -Every write path — the MCP router, the Claude Code PostToolUse hook, project-asset uploads, human broadcasts — fans out through an in-process bus after the DB write. SSE subscribers get the message in the same event-loop tick. No 1-second polling. Dedup on `(workspace_id, dedup_key)` so retries from emitters are silent no-ops at the database level. +## Public vs Enterprise -### 2. Shared context (workspace → project → session → asset) +Public Dhee is the developer brain: memory, handoff, local configuration, and +git-backed context. -Workspaces own projects. Projects own sessions and project assets. Agent sessions tag every tool call with `workspace_id` + `project_id` so context is routable, not a global soup. Drop a PDF into `design`; the backend-project codex session doesn't see it unless it reads it — at which point the "processed by" linkage shows up in the design project's asset drawer. +Dhee Enterprise is closed source: dashboard, team/org management, Repo Brain +summaries, telemetry, billing, license enforcement, and security scanning. -### 3. The context router (token saver) +This separation keeps the open-source package lightweight and trustworthy while +letting the commercial product move faster. -Heavy tool output (Bash, Grep, Agent, huge Read) goes through the router. A digest goes to the LLM; the raw is stored behind a pointer. Fat `CLAUDE.md` gets heading-chunked and only the slices relevant to *this turn* get injected. When you hit Claude Code's session limit, `dhee handoff` hands the whole context — including the shared line — to Codex intact. - -All three compose: the router produces digests → digests land on the line → other agents' sessions read them back in the next turn. - ---- - -## Integrations — screen-aware agents - -Dhee is a layer, not a runtime. It plugs into whatever agent you already use: - -- **Claude Code** — hooks + MCP server (`dhee install`, auto-wired by the installer). -- **Codex CLI** — MCP server wired via `~/.codex/config.toml`; session mirror reads rollouts in real time. -- **Cursor** — MCP server. -- **Gemini CLI**, **Aider**, **Cline** — MCP-compatible; drop `dhee-mcp` into the config. -- **Browser agents** — localhost REST API; register an agent session, publish to the line, subscribe via SSE. - -When any of them process the screen — read a file, open a URL, execute a tool — the output is workspace-scoped, digested, and broadcast. That's what makes every runtime **screen-aware** through Dhee: they stop being isolated chat tabs and start behaving like teammates who remember what the others just did. - ---- - -## CLI cheatsheet +## Development ```bash -# Setup / maintenance -dhee onboard # interactive provider + key wizard (also runs inside install.sh) -dhee update # pull latest release + rebuild UI -dhee install # wire hooks + MCP into Claude Code / Codex / Cursor -dhee doctor # diagnose installation - -# Daily use -dhee ui # open the channel view in your browser -dhee ui --no-open # same, don't auto-launch the browser -dhee router stats # what was digested today, how many tokens saved -dhee handoff # hand the current session context to another runtime +git clone https://github.com/Sankhya-AI/Dhee.git +cd Dhee +pip install -e ".[dev]" +pytest ``` ---- - -## Packaging +## Configuration -- **PyPI:** [`pip install dhee`](https://pypi.org/project/dhee/) — ships the prebuilt web UI, no Node required. -- **Source tree:** `pip install -e .` in a clone; `dhee update` will then run `git pull` + `pip install -e .` + rebuild the UI. +Gemini uses `GEMINI_API_KEY` or `GOOGLE_API_KEY`. +OpenAI uses `OPENAI_API_KEY`. ---- - -## What ships - -- **Workspaces & projects** — full CRUD in the UI, REST API, and cascading deletes. -- **Information line** — SSE stream with project/channel filters, dedup-on-write, optional `?backfill=N` for reconnects. -- **Asset drawer** — SHA-256-deduped project/workspace assets with per-asset "processed by" feeds. -- **Canvas** — deterministic hierarchical layout, infinite-pan DOM canvas, minimap, direction hints, loading skeleton. -- **Router** — digest-and-pointer for Read/Bash/Grep/Agent; heading-scoped `CLAUDE.md` injection. -- **Secret store** — Fernet-encrypted local vault for provider keys, env vars still win for back-compat. -- **1,200+ tests** — `pytest tests/` runs green (handful of pre-existing non-blocking failures in adjacent subsystems). - ---- - -## FAQ - -**Can I use Dhee without Claude Code?** Yes. Any MCP-compatible agent works. Claude Code just gets the tightest integration (native PostToolUse hook for per-turn digest capture). - -**Does the line share across machines?** Today: single-host, in-process pub/sub. Hosted Dhee (Redis pub/sub or Postgres LISTEN/NOTIFY, end-to-end encrypted) is in active development — it's the piece that turns the local tool into a team product. - -**What happens to my data?** Everything lives under `~/.dhee/`. SQLite for structured data (history, workspaces, projects, assets, line messages), Fernet-encrypted JSON for secrets, content-addressed pointer store for raw tool outputs. Nothing leaves your machine unless you point a hosted MCP at it. - -**Production-ready?** The local CLI + UI are shipping. Multi-host hosted Dhee is the next milestone — that's where your memory lives online and connects natively to every agent on the market. - ---- +Never commit secrets. Dhee stores keys locally under `~/.dhee/`. ## License -MIT. Make it yours. - ---- - - -Dhee is built by Sankhya AI Labs. -Questions, bugs, feature requests: GitHub Issues. - +MIT. Built by Sankhya AI Labs. diff --git a/apps/cognitive-debugger/app/(dashboard)/activity/page.tsx b/apps/cognitive-debugger/app/(dashboard)/activity/page.tsx deleted file mode 100644 index 2d18a2b..0000000 --- a/apps/cognitive-debugger/app/(dashboard)/activity/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ActivityWorkspace } from "@/components/beliefs/activity-workspace"; - -export default function ActivityPage() { - return ; -} diff --git a/apps/cognitive-debugger/app/(dashboard)/beliefs/[beliefId]/page.tsx b/apps/cognitive-debugger/app/(dashboard)/beliefs/[beliefId]/page.tsx deleted file mode 100644 index c42ac24..0000000 --- a/apps/cognitive-debugger/app/(dashboard)/beliefs/[beliefId]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { BeliefsWorkspace } from "@/components/beliefs/beliefs-workspace"; - -export default async function BeliefDetailPage({ - params, -}: { - params: Promise<{ beliefId: string }>; -}) { - const { beliefId } = await params; - return ; -} diff --git a/apps/cognitive-debugger/app/(dashboard)/beliefs/page.tsx b/apps/cognitive-debugger/app/(dashboard)/beliefs/page.tsx deleted file mode 100644 index c02f462..0000000 --- a/apps/cognitive-debugger/app/(dashboard)/beliefs/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { BeliefsWorkspace } from "@/components/beliefs/beliefs-workspace"; - -export default function BeliefsPage() { - return ; -} diff --git a/apps/cognitive-debugger/app/(dashboard)/contradictions/page.tsx b/apps/cognitive-debugger/app/(dashboard)/contradictions/page.tsx deleted file mode 100644 index 1c5655f..0000000 --- a/apps/cognitive-debugger/app/(dashboard)/contradictions/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ContradictionsWorkspace } from "@/components/beliefs/contradictions-workspace"; - -export default function ContradictionsPage() { - return ; -} diff --git a/apps/cognitive-debugger/app/(dashboard)/layout.tsx b/apps/cognitive-debugger/app/(dashboard)/layout.tsx deleted file mode 100644 index 50b5ebd..0000000 --- a/apps/cognitive-debugger/app/(dashboard)/layout.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import type { ReactNode } from "react"; - -import { DashboardShell } from "@/components/layout/dashboard-shell"; - -export default function AppLayout({ children }: { children: ReactNode }) { - return {children}; -} diff --git a/apps/cognitive-debugger/app/globals.css b/apps/cognitive-debugger/app/globals.css deleted file mode 100644 index 2cdc9bf..0000000 --- a/apps/cognitive-debugger/app/globals.css +++ /dev/null @@ -1,63 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --app-bg: #dbe6e8; - --card-bg: #ffffff; - --card-foreground: #1f2b20; - --border: #d9d0bf; - --muted: #667167; - --soft-bg: #f5f3ee; - --pattern-a: #fafafa; - --pattern-b: #ffffff; - --success-bg: #eef6ef; - --warning-bg: #fbf5df; - --danger-bg: #fbebe8; - --info-bg: #eff4f5; - } - - * { - @apply box-border; - } - - html, - body { - @apply h-full; - } - - body { - @apply m-0 min-h-screen bg-transparent font-sans text-ink; - background: linear-gradient(180deg, #dce8ea 0%, #eef2ef 100%); - } - - a { - @apply text-inherit no-underline; - } -} - -@layer utilities { - .main-pattern { - background: - linear-gradient(-45deg, var(--pattern-a) 25%, var(--pattern-b) 25%, var(--pattern-b) 50%, var(--pattern-a) 50%, var(--pattern-a) 75%, var(--pattern-b) 75%, var(--pattern-b) 100%); - background-size: 4px 4px; - } - - .soft-scrollbar { - scrollbar-width: thin; - scrollbar-color: #c9c1b3 transparent; - } - - .soft-scrollbar::-webkit-scrollbar { - width: 10px; - height: 10px; - } - - .soft-scrollbar::-webkit-scrollbar-thumb { - background: #c9c1b3; - border-radius: 999px; - border: 2px solid transparent; - background-clip: content-box; - } -} diff --git a/apps/cognitive-debugger/app/layout.tsx b/apps/cognitive-debugger/app/layout.tsx deleted file mode 100644 index 8957d78..0000000 --- a/apps/cognitive-debugger/app/layout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { Metadata } from "next"; - -import "./globals.css"; - -export const metadata: Metadata = { - title: "Dhee Cognitive Debugger", - description: "Inspectable operator console for Dhee belief state.", -}; - -export default function RootLayout({ - children, -}: Readonly<{ children: React.ReactNode }>) { - return ( - - {children} - - ); -} diff --git a/apps/cognitive-debugger/app/page.tsx b/apps/cognitive-debugger/app/page.tsx deleted file mode 100644 index 2e290ec..0000000 --- a/apps/cognitive-debugger/app/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { redirect } from "next/navigation"; - -export default function HomePage() { - redirect("/beliefs"); -} diff --git a/apps/cognitive-debugger/components/beliefs/__tests__/beliefs-workspace.test.tsx b/apps/cognitive-debugger/components/beliefs/__tests__/beliefs-workspace.test.tsx deleted file mode 100644 index 705858f..0000000 --- a/apps/cognitive-debugger/components/beliefs/__tests__/beliefs-workspace.test.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import React from "react"; -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { BeliefsWorkspace } from "@/components/beliefs/beliefs-workspace"; - -const push = vi.fn(); - -vi.mock("next/navigation", () => ({ - useRouter: () => ({ push }), -})); - -vi.mock("framer-motion", () => ({ - motion: { - tr: ({ children, ...props }: React.ComponentProps<"tr">) => {children}, - div: ({ children, ...props }: React.ComponentProps<"div">) =>
{children}
, - }, -})); - -const overview = { - user_id: "u1", - users: ["u1"], - counts: { - active: 1, - stale: 0, - contradicted: 0, - pinned: 0, - tombstoned: 0, - }, - contradictions: 0, - influence: { - total: 1, - by_type: { included: 1 }, - }, -}; - -const belief = { - id: "belief-1", - claim: "Auth uses JWT rotation", - domain: "system_state", - confidence: 0.82, - status: "held", - truth_status: "held", - freshness_status: "current", - lifecycle_status: "active", - protection_level: "normal", - origin: "user", - successor_id: null, - created_at: 1712336400, - updated_at: 1712336400, - source_memory_ids: [], - source_episode_ids: [], - tags: [], - source_count: 1, - contradiction_count: 0, - evidence_for: 1, - evidence_against: 0, - stability: 0.9, - is_listable: true, -}; - -const api = vi.hoisted(() => ({ - fetchOverview: vi.fn(), - fetchBeliefs: vi.fn(), - fetchBeliefDetail: vi.fn(), - fetchBeliefEvidence: vi.fn(), - fetchBeliefHistory: vi.fn(), - fetchBeliefImpact: vi.fn(), - correctBelief: vi.fn(), - markStale: vi.fn(), - pinBelief: vi.fn(), - tombstoneBelief: vi.fn(), -})); - -vi.mock("@/lib/api", () => ({ - fetchOverview: api.fetchOverview, - fetchBeliefs: api.fetchBeliefs, - fetchBeliefDetail: api.fetchBeliefDetail, - fetchBeliefEvidence: api.fetchBeliefEvidence, - fetchBeliefHistory: api.fetchBeliefHistory, - fetchBeliefImpact: api.fetchBeliefImpact, - correctBelief: api.correctBelief, - markStale: api.markStale, - pinBelief: api.pinBelief, - tombstoneBelief: api.tombstoneBelief, -})); - -describe("BeliefsWorkspace", () => { - beforeEach(() => { - push.mockReset(); - api.fetchOverview.mockReset(); - api.fetchBeliefs.mockReset(); - api.fetchBeliefDetail.mockReset(); - api.fetchBeliefEvidence.mockReset(); - api.fetchBeliefHistory.mockReset(); - api.fetchBeliefImpact.mockReset(); - api.correctBelief.mockReset(); - api.markStale.mockReset(); - api.pinBelief.mockReset(); - api.tombstoneBelief.mockReset(); - - api.fetchOverview.mockResolvedValue(overview); - api.fetchBeliefs.mockResolvedValue({ - user_id: "u1", - total: 1, - page: 1, - page_size: 100, - items: [belief], - }); - api.fetchBeliefDetail.mockResolvedValue(belief); - api.fetchBeliefEvidence.mockResolvedValue({ - belief_id: belief.id, - items: [ - { - id: "e1", - belief_id: belief.id, - content: "Operator confirmed JWT refresh rotation in auth service.", - supports: true, - source: "user", - confidence: 0.9, - created_at: 1712336400, - }, - ], - }); - api.fetchBeliefHistory.mockResolvedValue({ - belief_id: belief.id, - items: [ - { - seq: 1, - id: "h1", - belief_id: belief.id, - event_type: "proposed", - reason: "Imported from operator memory", - payload: {}, - confidence_after: 0.82, - truth_status_after: "held", - created_at: 1712336400, - }, - ], - }); - api.fetchBeliefImpact.mockResolvedValue({ - belief_id: belief.id, - items: [ - { - id: "i1", - belief_id: belief.id, - user_id: "u1", - influence_type: "included", - query: "auth incident", - metadata: { surface: "context" }, - created_at: 1712336400, - }, - ], - }); - }); - - it("renders belief detail tabs and routes to a corrected successor", async () => { - api.correctBelief.mockResolvedValue({ - old_belief: belief, - new_belief: { ...belief, id: "belief-2", claim: "Auth uses cookie sessions" }, - }); - - render(); - - expect((await screen.findAllByText("Auth uses JWT rotation")).length).toBeGreaterThan(0); - expect(screen.getByRole("tab", { name: "Evidence" })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: "History" })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: "Impact" })).toBeInTheDocument(); - - fireEvent.change(screen.getByPlaceholderText("Corrected successor belief text"), { - target: { value: "Auth uses cookie sessions" }, - }); - fireEvent.change(screen.getByPlaceholderText("Reason for mutation"), { - target: { value: "Operator verified session migration" }, - }); - fireEvent.click(screen.getByRole("button", { name: "Create corrected successor" })); - - await waitFor(() => { - expect(api.correctBelief).toHaveBeenCalledWith( - "belief-1", - "Auth uses cookie sessions", - "Operator verified session migration", - ); - }); - - await waitFor(() => { - expect(push).toHaveBeenCalledWith("/beliefs/belief-2"); - }); - }); -}); diff --git a/apps/cognitive-debugger/components/beliefs/__tests__/contradictions-workspace.test.tsx b/apps/cognitive-debugger/components/beliefs/__tests__/contradictions-workspace.test.tsx deleted file mode 100644 index c1ec9bf..0000000 --- a/apps/cognitive-debugger/components/beliefs/__tests__/contradictions-workspace.test.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import React from "react"; -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { ContradictionsWorkspace } from "@/components/beliefs/contradictions-workspace"; - -const api = vi.hoisted(() => ({ - fetchOverview: vi.fn(), - fetchContradictions: vi.fn(), - markStale: vi.fn(), - mergeBeliefs: vi.fn(), -})); - -vi.mock("@/lib/api", () => ({ - fetchOverview: api.fetchOverview, - fetchContradictions: api.fetchContradictions, - markStale: api.markStale, - mergeBeliefs: api.mergeBeliefs, -})); - -describe("ContradictionsWorkspace", () => { - beforeEach(() => { - api.fetchOverview.mockReset(); - api.fetchContradictions.mockReset(); - api.markStale.mockReset(); - api.mergeBeliefs.mockReset(); - - api.fetchOverview.mockResolvedValue({ - user_id: "u1", - users: ["u1"], - counts: { - active: 2, - stale: 0, - contradicted: 2, - pinned: 0, - tombstoned: 0, - }, - contradictions: 1, - influence: { total: 0, by_type: {} }, - }); - api.fetchContradictions.mockResolvedValue({ - user_id: "u1", - items: [ - { - belief_a: { - id: "belief-a", - claim: "Repo uses FastAPI", - domain: "system_state", - confidence: 0.8, - truth_status: "held", - freshness_status: "current", - lifecycle_status: "active", - protection_level: "normal", - updated_at: 1712336400, - }, - belief_b: { - id: "belief-b", - claim: "Repo does not use FastAPI", - domain: "system_state", - confidence: 0.6, - truth_status: "challenged", - freshness_status: "current", - lifecycle_status: "active", - protection_level: "normal", - updated_at: 1712336400, - }, - shared_source_overlap: 1, - recommended_resolution: "keep_a", - }, - ], - }); - api.markStale.mockResolvedValue({}); - api.mergeBeliefs.mockResolvedValue({}); - }); - - it("resolves a contradiction by marking the losing belief stale", async () => { - render(); - - expect(await screen.findByText("Contradictory belief pair")).toBeInTheDocument(); - - fireEvent.click(screen.getByRole("button", { name: "Keep A" })); - - await waitFor(() => { - expect(api.markStale).toHaveBeenCalledWith( - "belief-b", - "Marked stale in contradiction review; kept belief-a", - ); - }); - }); - - it("merges a contradiction pair into the survivor belief", async () => { - render(); - - expect(await screen.findByText("Contradictory belief pair")).toBeInTheDocument(); - - fireEvent.click(screen.getByRole("button", { name: "Merge into A" })); - - await waitFor(() => { - expect(api.mergeBeliefs).toHaveBeenCalledWith( - "belief-a", - "belief-b", - "Merged from contradictions review", - ); - }); - }); -}); diff --git a/apps/cognitive-debugger/components/beliefs/activity-workspace.tsx b/apps/cognitive-debugger/components/beliefs/activity-workspace.tsx deleted file mode 100644 index dbb62d1..0000000 --- a/apps/cognitive-debugger/components/beliefs/activity-workspace.tsx +++ /dev/null @@ -1,101 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { RefreshCw } from "lucide-react"; - -import { fetchActivity, fetchOverview, type ActivityRow, type OverviewResponse } from "@/lib/api"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { StatusBadge } from "@/components/beliefs/status-badge"; -import { formatRelativeTime } from "@/lib/utils"; - -export function ActivityWorkspace() { - const [overview, setOverview] = useState(null); - const [selectedUser, setSelectedUser] = useState(""); - const [items, setItems] = useState([]); - const [error, setError] = useState(""); - - const activeUser = selectedUser || overview?.user_id || ""; - - const refresh = async (userId?: string) => { - const info = await fetchOverview(userId); - setOverview(info); - setSelectedUser((current) => current || info.user_id); - const activity = await fetchActivity(info.user_id); - setItems(activity.items); - }; - - useEffect(() => { - refresh().catch((err) => setError(err instanceof Error ? err.message : "Unable to load activity.")); - }, []); - - useEffect(() => { - if (!activeUser) return; - refresh(activeUser).catch((err) => setError(err instanceof Error ? err.message : "Unable to load activity.")); - }, [activeUser]); - - return ( -
-
-
-

Activity

-

- Recent belief mutations and coarse influence traces, sorted by recency. -

-
-
- - -
-
- - {error ? ( - - {error} - - ) : null} - - - - Recent activity - Belief events and influence traces from the append-only audit log. - - - {items.length === 0 ? ( -

No activity recorded yet.

- ) : ( - items.map((item) => ( -
-
-
- - -
-
{formatRelativeTime(item.created_at)}
-
-
{item.belief_claim || "Unknown belief"}
-
{item.reason || "No reason recorded."}
-
- {item.actor || "system"} · {item.belief_domain || "unknown domain"} -
-
- )) - )} -
-
-
- ); -} diff --git a/apps/cognitive-debugger/components/beliefs/beliefs-workspace.tsx b/apps/cognitive-debugger/components/beliefs/beliefs-workspace.tsx deleted file mode 100644 index 66c5769..0000000 --- a/apps/cognitive-debugger/components/beliefs/beliefs-workspace.tsx +++ /dev/null @@ -1,607 +0,0 @@ -"use client"; - -import { Fragment, startTransition, useDeferredValue, useEffect, useMemo, useState } from "react"; -import { useRouter } from "next/navigation"; -import { motion } from "framer-motion"; -import { Pin, RefreshCw, Trash2 } from "lucide-react"; - -import { - Belief, - EvidenceRow, - HistoryRow, - ImpactRow, - fetchBeliefDetail, - fetchBeliefEvidence, - fetchBeliefHistory, - fetchBeliefImpact, - fetchBeliefs, - fetchOverview, - correctBelief, - markStale, - pinBelief, - tombstoneBelief, - type OverviewResponse, -} from "@/lib/api"; -import { formatRelativeTime, toPercent } from "@/lib/utils"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { StatusBadge } from "@/components/beliefs/status-badge"; -import { StatCard } from "@/components/beliefs/stat-card"; - -type Filters = { - search: string; - domain: string; - truthStatus: string; - freshnessStatus: string; - lifecycleStatus: string; - protectionLevel: string; - origin: string; - minConfidence: string; - maxConfidence: string; -}; - -const defaultFilters: Filters = { - search: "", - domain: "", - truthStatus: "", - freshnessStatus: "", - lifecycleStatus: "", - protectionLevel: "", - origin: "", - minConfidence: "0", - maxConfidence: "1", -}; - -export function BeliefsWorkspace({ selectedBeliefId }: { selectedBeliefId?: string }) { - const router = useRouter(); - const [overview, setOverview] = useState(null); - const [selectedUser, setSelectedUser] = useState(""); - const [beliefs, setBeliefs] = useState([]); - const [total, setTotal] = useState(0); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(""); - const [detail, setDetail] = useState(null); - const [evidence, setEvidence] = useState([]); - const [history, setHistory] = useState([]); - const [impact, setImpact] = useState([]); - const [filters, setFilters] = useState(defaultFilters); - const [pendingCorrection, setPendingCorrection] = useState(""); - const [pendingReason, setPendingReason] = useState(""); - const deferredSearch = useDeferredValue(filters.search); - - const activeUser = selectedUser || overview?.user_id || ""; - const effectiveSelectedId = selectedBeliefId || beliefs[0]?.id || ""; - - const refreshOverview = async (userId?: string) => { - const data = await fetchOverview(userId); - setOverview(data); - setSelectedUser((current) => current || data.user_id); - return data; - }; - - const refreshBeliefs = async (userId: string) => { - const response = await fetchBeliefs({ - user_id: userId, - search: deferredSearch, - domain: filters.domain, - truth_status: filters.truthStatus, - freshness_status: filters.freshnessStatus, - lifecycle_status: filters.lifecycleStatus, - protection_level: filters.protectionLevel, - origin: filters.origin, - min_confidence: filters.minConfidence, - max_confidence: filters.maxConfidence, - page: 1, - page_size: 100, - }); - setBeliefs(response.items); - setTotal(response.total); - return response.items; - }; - - const refreshDetail = async (beliefId: string) => { - if (!beliefId) { - setDetail(null); - setEvidence([]); - setHistory([]); - setImpact([]); - return; - } - const [belief, evidenceResult, historyResult, impactResult] = await Promise.all([ - fetchBeliefDetail(beliefId), - fetchBeliefEvidence(beliefId), - fetchBeliefHistory(beliefId), - fetchBeliefImpact(beliefId), - ]); - setDetail(belief); - setEvidence(evidenceResult.items); - setHistory(historyResult.items); - setImpact(impactResult.items); - }; - - useEffect(() => { - let cancelled = false; - setIsLoading(true); - setError(""); - - (async () => { - try { - const info = await refreshOverview(); - const rows = await refreshBeliefs(info.user_id); - const nextSelectedId = selectedBeliefId || rows[0]?.id; - if (!cancelled && nextSelectedId) { - await refreshDetail(nextSelectedId); - } - } catch (err) { - if (!cancelled) { - setError(err instanceof Error ? err.message : "Unable to load beliefs."); - } - } finally { - if (!cancelled) { - setIsLoading(false); - } - } - })(); - - return () => { - cancelled = true; - }; - }, []); - - useEffect(() => { - if (!activeUser) return; - let cancelled = false; - setIsLoading(true); - (async () => { - try { - await refreshOverview(activeUser); - const rows = await refreshBeliefs(activeUser); - const nextSelectedId = selectedBeliefId || rows[0]?.id; - if (!cancelled) { - await refreshDetail(nextSelectedId); - } - } catch (err) { - if (!cancelled) { - setError(err instanceof Error ? err.message : "Unable to refresh beliefs."); - } - } finally { - if (!cancelled) { - setIsLoading(false); - } - } - })(); - return () => { - cancelled = true; - }; - }, [ - activeUser, - deferredSearch, - filters.domain, - filters.truthStatus, - filters.freshnessStatus, - filters.lifecycleStatus, - filters.protectionLevel, - filters.origin, - filters.minConfidence, - filters.maxConfidence, - selectedBeliefId, - ]); - - useEffect(() => { - if (!effectiveSelectedId) return; - refreshDetail(effectiveSelectedId).catch((err) => { - setError(err instanceof Error ? err.message : "Unable to load belief detail."); - }); - }, [effectiveSelectedId]); - - const domains = useMemo( - () => Array.from(new Set(beliefs.map((belief) => belief.domain))).sort(), - [beliefs], - ); - - const handleRowClick = (beliefId: string) => { - startTransition(() => { - router.push(`/beliefs/${beliefId}`); - }); - }; - - const handleRefresh = async () => { - if (!activeUser) return; - setIsLoading(true); - try { - await refreshOverview(activeUser); - await refreshBeliefs(activeUser); - await refreshDetail(effectiveSelectedId); - } catch (err) { - setError(err instanceof Error ? err.message : "Unable to refresh beliefs."); - } finally { - setIsLoading(false); - } - }; - - const mutateDetail = async (action: () => Promise, onSuccess?: (result: T) => void) => { - try { - const result = await action(); - await handleRefresh(); - onSuccess?.(result); - } catch (err) { - setError(err instanceof Error ? err.message : "Mutation failed."); - } - }; - - return ( -
-
-
-
-
- - -
-
-

- Cognitive Debugger -

-

- Inspect Dhee's working beliefs, trace their evidence, review history, and correct state without mutating the past. -

-
-
-
- - -
-
-
- -
- - - - - -
- - - - Filters - Search and narrow the current cognition state. - - - setFilters((current) => ({ ...current, search: event.target.value }))} - placeholder="Search belief text" - className="h-10 rounded-md border border-stone bg-white px-3 text-sm lg:col-span-2" - /> - - - - - - - setFilters((current) => ({ ...current, minConfidence: event.target.value }))} - placeholder="Min confidence" - type="number" - min="0" - max="1" - step="0.05" - className="h-10 rounded-md border border-stone bg-white px-3 text-sm" - /> - setFilters((current) => ({ ...current, maxConfidence: event.target.value }))} - placeholder="Max confidence" - type="number" - min="0" - max="1" - step="0.05" - className="h-10 rounded-md border border-stone bg-white px-3 text-sm" - /> - - - - {error ? ( - - {error} - - ) : null} - -
- - -
-
- Beliefs - {total} matching beliefs -
- {isLoading ?
Loading…
: null} -
-
- - - - - - - - - - - - - - - - {beliefs.map((belief) => { - const selected = belief.id === effectiveSelectedId; - return ( - handleRowClick(belief.id)} - > - - - - - - - - - - ); - })} - -
StatementDomainConfidenceStatusesUpdatedSourcesConflictsOrigin
-
{belief.claim}
-
{belief.domain} -
{toPercent(belief.confidence)}
-
-
- - - - -
-
{formatRelativeTime(belief.updated_at)}{belief.source_count}{belief.contradiction_count} - -
-
-
- - - - Belief Detail - - {detail ? "Trace evidence, revisions, and impact before you mutate state." : "Select a belief to inspect it."} - - - - {detail ? ( - -
-
{detail.claim}
-
- - - - - -
-
-
Domain: {detail.domain}
-
Confidence: {toPercent(detail.confidence)}
-
Sources: {detail.source_count}
-
Updated: {formatRelativeTime(detail.updated_at)}
-
-
- - - -
-
-