Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
23 changes: 23 additions & 0 deletions .github/workflows/sensor-dsh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: DeepSeek Harness parser platforms
on:
pull_request:
paths: ['Sensor/**', '.github/workflows/sensor-dsh.yml']
push:
branches: [main]
paths: ['Sensor/**', '.github/workflows/sensor-dsh.yml']
permissions:
contents: read
jobs:
parser:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v7
- run: uv sync --extra dev --python 3.12
working-directory: Sensor
- run: uv run pytest tests/test_dsh_parser.py -q
working-directory: Sensor
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![uber/ADR | Trendshift](https://trendshift.io/api/badge/repositories/100696)](https://trendshift.io/repositories/100696?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-100696)

ADR (Agentic AI Detection and Response) is an enterprise security system for AI agents. It helps organizations secure employee-facing agents such as Cursor, Claude Code, Codex, and GitHub Copilot CLI, as well as customer-facing agents such as AI support agents.
ADR (Agentic AI Detection and Response) is an enterprise security system for AI agents. It helps organizations secure employee-facing agents such as Cursor, Claude Code, Codex, GitHub Copilot CLI, and DeepSeek Harness, as well as customer-facing agents such as AI support agents.

ADR is **deployed in production at Uber**, and the accompanying paper was accepted to **MLSys 2026**: [Paper PDF](docs/adr-paper.pdf) · [Slides PDF](docs/adr-mlsys-2026-slides.pdf)

Expand All @@ -23,7 +23,7 @@ This repository contains the open-source **ADR Discovery**, **ADR Sensor**, **AD
| Path | ADR component | Description |
| -------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------ |
| [Discovery/](Discovery/) | ADR Discovery | Inventory the AI apps, CLI agents, IDE extensions, model runtimes, and MCP servers on an endpoint, and flag unknown surfaces for review |
| [Sensor/](Sensor/) | ADR Observability | Collect and normalize agent telemetry from Claude Code, Cursor, Codex, GitHub Copilot CLI, opencode, Claude Desktop, and others |
| [Sensor/](Sensor/) | ADR Observability | Collect and normalize agent telemetry from Claude Code, Cursor, Codex, GitHub Copilot CLI, DeepSeek Harness, opencode, Claude Desktop, and others |
| [Detection/](Detection/) | ADR Benchmark + Detection | Dual-agent detector, 134 MCP servers, 304 benchmark tasks, baselines, figure scripts |
| [docs/REPRODUCIBILITY.md](docs/REPRODUCIBILITY.md) | Evaluation | Step-by-step workflow to reproduce benchmark detection and paper figures |

Expand Down
32 changes: 30 additions & 2 deletions Sensor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ADR Sensor is a Python library that collects telemetry from AI coding agents to
| **Claude Desktop** | `claude_desktop` | JSONL audit logs | macOS, Windows |
| **OpenAI Codex CLI** | `codex` | JSONL + SQLite path catalogs | macOS, Linux, Windows |
| **GitHub Copilot CLI** | `copilot` | JSONL (`~/.copilot/session-state/`) | macOS, Linux, Windows |
| **DeepSeek Harness** | `dsh` | JSONL/Zstandard (`~/.dsh/sessions/`) | macOS, Linux, Windows |
| **Warp Terminal** | `warp` | SQLite (`warp.sqlite`) | macOS, Windows |
| **opencode** | `opencode` | SQLite (`opencode.db`) or JSON tree | macOS, Linux |

Expand Down Expand Up @@ -91,14 +92,38 @@ MCP tools are namespaced by opencode as `<server>_<tool>`, so any tool that is n
known built-in and contains an underscore is recorded as `tool_type: "mcp_tool"` with
its `server_name` populated.

### DeepSeek Harness

The `dsh` source reads current DeepSeek Harness v3 session logs from
`$DSH_HOME/sessions/`, or `~/.dsh/sessions/` when `DSH_HOME` is unset. DSH uses
Zstandard-compressed logs by default; uncompressed JSONL v3 logs are also
supported. When migrations leave several generations in one session directory,
the parser follows DSH and considers only the highest generation. A session is
ingested only when that generation is v3, so historical or future formats are
not silently interpreted with the wrong schema.

Tool arguments and results, PTC sub-dispatches, failure status, approvals,
provider/model context, sandbox mode, permission preset, typed message content,
and recorded token usage are normalized into the Sensor schema. Tool names do
not carry a universal MCP server identity, so the parser does not guess one.
The standard 14-day file lookback applies; use `--all-history` to include older
sessions. Storage and event behavior were
checked against DSH's pinned
[JSONL persistence contract](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/session/session-persistence-jsonl/README.md)
and [v3 event declarations](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/core/session/src/types.ts).

```bash
adr-sensor --source dsh
```


## Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent Logs │
│ Claude, Cursor, Cline, Codex, Copilot CLI, Warp │
Claude Desktop, opencode
│ Claude Desktop, opencode, DeepSeek Harness
└───────────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
Expand Down Expand Up @@ -153,6 +178,7 @@ adr-sensor --source claude
adr-sensor --source cursor
adr-sensor --source codex
adr-sensor --source copilot
adr-sensor --source dsh
adr-sensor --source claude_desktop
adr-sensor --source opencode

Expand Down Expand Up @@ -363,7 +389,7 @@ builds its `--source` choices from `SOURCES`, so it picks the new agent up for f
| --------------- | ------------------------------------------- |
| Python | 3.9, 3.10, 3.11, 3.12, 3.13 |
| Operating system| macOS, Linux, Windows |
| Dependencies | `tabulate` (runtime only — no native deps) |
| Dependencies | `tabulate`, `zstandard` |

Which sources yield data depends on the host OS and on which agents are installed;
see the platform column in [Supported AI Agents](#supported-ai-agents). Sources that
Expand All @@ -375,6 +401,7 @@ cannot run on the current platform are skipped rather than failing.
| ----------------- | -------------------------- | ----------------------------------------------------------------- |
| `CODEX_HOME` | Codex parser | Codex data root containing `sessions/` and optional `state_*.sqlite` catalogs (default `~/.codex`) |
| `COPILOT_HOME` | Copilot parser | Copilot CLI data root containing `session-state/` (default `~/.copilot`) |
| `DSH_HOME` | DeepSeek Harness parser | Harness data root containing `sessions/` (default `~/.dsh`) |
| `XDG_CACHE_HOME` | `AgentObserver` | Base for `--save-sessions` output (`$XDG_CACHE_HOME/adr_sensor`, default `~/.cache/adr_sensor`) |
| `XDG_DATA_HOME` | opencode parser | Overrides the opencode data directory (default `~/.local/share/opencode`) |
| `OPENCODE_DB` | opencode parser | Overrides the opencode SQLite filename or path (`:memory:` is ignored) |
Expand Down Expand Up @@ -427,6 +454,7 @@ adr-sensor/
│ │ ├── claude_desktop_parser.py
│ │ ├── codex_parser.py
│ │ ├── copilot_parser.py
│ │ ├── dsh_parser.py
│ │ ├── opencode_parser.py
│ │ └── warp_parser.py
│ ├── schemas/
Expand Down
6 changes: 3 additions & 3 deletions Sensor/adr_sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
ADR Sensor - Agentic Detection & Response

Security observability library for AI coding agents. Collects telemetry from
Claude Code, Cursor, Cline, OpenAI Codex CLI, GitHub Copilot CLI, Warp
Terminal, opencode, and Claude Desktop Agent Mode (including Dispatch
sessions) to enable threat detection and security monitoring.
Claude Code, Cursor, Cline, OpenAI Codex CLI, GitHub Copilot CLI, DeepSeek
Harness, Warp Terminal, opencode, and Claude Desktop Agent Mode (including
Dispatch sessions) to enable threat detection and security monitoring.

Usage:
from adr_sensor import AgentObserver
Expand Down
1 change: 1 addition & 0 deletions Sensor/adr_sensor/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def main():
adr-sensor --source cursor Ingest Cursor IDE logs only
adr-sensor --source claude_desktop Ingest Claude Desktop agent-mode logs only (macOS/Windows)
adr-sensor --source copilot Ingest GitHub Copilot CLI logs only
adr-sensor --source dsh Ingest DeepSeek Harness logs only
adr-sensor --source opencode Ingest opencode logs only
adr-sensor --save-sessions Save individual session files
adr-sensor --output-format jsonl Export as JSONL
Expand Down
7 changes: 5 additions & 2 deletions Sensor/adr_sensor/observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from .parsers.codex_parser import CodexParser
from .parsers.copilot_parser import CopilotParser
from .parsers.cursor_parser import CursorParser
from .parsers.dsh_parser import DshParser
from .parsers.opencode_parser import OpencodeParser
from .parsers.warp_parser import WarpParser
from .schemas.agent_event_schema import AgentEvent
Expand Down Expand Up @@ -60,6 +61,7 @@ class AgentObserver:
("warp", "Warp Terminal"),
("codex", "Codex"),
("copilot", "GitHub Copilot CLI"),
("dsh", "DeepSeek Harness"),
("opencode", "opencode"),
)

Expand All @@ -69,7 +71,7 @@ class AgentObserver:
"claude_desktop": ("Darwin", "Windows"),
}

CONTENT_AWARE_INCREMENTAL_SOURCES = frozenset({"codex", "copilot"})
CONTENT_AWARE_INCREMENTAL_SOURCES = frozenset({"codex", "copilot", "dsh"})

def __init__(self, output_dir: Optional[Path] = None, max_age_days: Optional[int] = None):
"""Initialize the AgentObserver.
Expand All @@ -87,6 +89,7 @@ def __init__(self, output_dir: Optional[Path] = None, max_age_days: Optional[int
self.copilot_parser = (
CopilotParser(max_age_days=max_age_days) if max_age_days is not None else CopilotParser()
)
self.dsh_parser = DshParser(max_age_days=max_age_days) if max_age_days is not None else DshParser()
self.cline_parser = ClineParser(max_age_days=max_age_days) if max_age_days is not None else ClineParser()
self.warp_parser = WarpParser(max_age_days=max_age_days) if max_age_days is not None else WarpParser()
self.opencode_parser = (
Expand Down Expand Up @@ -129,7 +132,7 @@ def ingest_all(

Args:
source_filter: Which source to ingest. One of 'all', 'claude', 'cursor',
'claude_desktop', 'cline', 'warp', 'codex', 'copilot', 'opencode'.
'claude_desktop', 'cline', 'warp', 'codex', 'copilot', 'dsh', 'opencode'.

Returns:
Tuple of (agent_events, system_configs).
Expand Down
2 changes: 2 additions & 0 deletions Sensor/adr_sensor/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .codex_parser import CodexParser
from .copilot_parser import CopilotParser
from .cursor_parser import CursorParser
from .dsh_parser import DshParser
from .opencode_parser import OpencodeParser
from .warp_parser import WarpParser

Expand All @@ -22,6 +23,7 @@
"CodexParser",
"CopilotParser",
"CursorParser",
"DshParser",
"OpencodeParser",
"WarpParser",
]
Loading
Loading