feat: Multi-source watcher framework for multi-tool AI log monitoring#2674
feat: Multi-source watcher framework for multi-tool AI log monitoring#2674huang-yi-dae wants to merge 4 commits into
Conversation
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
| @@ -0,0 +1,61 @@ | |||
| # OpenViking Active Daemon | |||
There was a problem hiding this comment.
This file can be placed in docs/zh/agent-integrations
|
Thanks for contribution! We'll review this soon |
|
First off—thank you for this PR. The overall direction is genuinely solid. Using a single daemon to watch multiple coding tools, normalize their outputs into a unified event schema, and feed them into a shared ETL → Before merging, however, I checked out the branch and tested it against the real on-disk data of the tools I currently have installed (Claude Code, Cursor, Codex, OpenCode), and traced the call paths against the OpenViking codebase. I'm sharing my findings because none of these issues are caught by the green test suite. The fixtures seem hand-authored to match the parsers, meaning the suite confirms internal consistency but not compatibility with real tools. Everything outlined below is reproducible. Blockers (Verified against the OpenViking codebase, tool-independent)1. The LLM call path doesn't match any real interface — extraction silently no-ops. 2. Watcher Format Mismatches (Verified empirically on a real machine)The normalization layer assumes a top-level, flat JSONL structure: Claude Code (the core watcher) extracts 0 events from real logs. Cursor stores chats in SQLite, not in Codex and OpenCode are not covered and don't fit the file-append model anyway. Aider and Continue.dev: Unverified locally. Other Real IssuesTime-based flush never fires. Cross-thread queue operations. Incremental reader flaws. Using text-mode Code conventions. The newly added The Primary Ask Before MergingPlease verify each introduced feature end-to-end against a real run of its respective tool, rather than relying solely on synthetic fixtures, and confirm it successfully writes actual output to Currently, the test suite is green, yet the end-to-end pipeline yields zero output on a real machine. Instituting a "real-run check per feature" as a merging gate would have caught all the issues listed above. Suggested Path ForwardThese fixes are mostly minor, and the underlying architecture is robust enough to handle them easily:
One quick housekeeping note: This branch currently includes the entire v1 daemon (meaning it overlaps with #2629) alongside some unrelated I am more than happy to help verify any of these changes or pair-program on the format fixes. The core idea is fantastic, and I really want to see it merged once the pipeline produces reliable end-to-end output on real machines. 首先,感谢提交这个 PR,整体方向非常棒。 用单个 daemon 监听多种编码工具、将其输出归一化为统一的事件结构,再喂给共享的 ETL → 不过在合入前,我拉取了分支,并使用我本机实际安装工具(Claude Code、Cursor、Codex、OpenCode)的真实落盘数据进行了测试,同时对照 OpenViking 代码库梳理了调用链路。我想分享一下我的发现,因为全绿的测试用例完全掩盖了这些问题:测试用的 fixture 似乎是针对 parser 手写的,这导致测试只能证明代码内部自洽,无法保证与真实工具的兼容性。以下所有问题均可复现。 Blocker(对照 OpenViking 代码库核实,与具体工具无关)1. LLM 调用链路不匹配任何真实接口 —— 知识提取静默空转。 2. 构造 Watcher 格式不匹配(在真实机器上实测验证)归一化层假设日志采用顶层扁平的 JSONL 结构: Claude Code(核心 watcher)从真实日志中提取到了 0 条事件。 Cursor 把对话存在 SQLite 中,而不是 Codex 和 OpenCode 未被覆盖,且同样不适用 file-append 模型。 Aider 和 Continue.dev:本机未验证。 其他实际问题基于时间的 flush 永不触发。 跨线程队列操作。 增量读取缺陷。 在文本模式下使用 代码规范。 新增的 合入前我唯一的请求请针对每个引入的特性,基于工具的真实运行环境(而非合成的 fixture)进行端到端验证,并确认它能产生实际的产物写入 目前的现状是:测试全绿,但在真机上的端到端管道产出为零。因此,将“每个功能都需经过真实运行验证”作为准入条件,能有效拦截上述所有问题。 建议的推进路径这些修复项大多工作量不大,当前的架构也完全撑得起这些调整:
最后提一个代码整理上的建议:该分支目前包含了完整的 v1 daemon 代码(导致与 #2629 产生重叠),并且混入了一些与此无关的 我很乐意帮忙验证上述任何修改,或者结对修复格式问题。这个设计的核心理念非常扎实,只要管道能在真机上跑通端到端流程,我非常期待看到它被合入主分支。 |
bfd54ed to
54fe519
Compare
- Background daemon monitors AI coding tool logs (Claude Code, Aider, Cursor, Continue.dev, generic JSONL) via watchdog filesystem observers - Incremental ETL pipeline: watch → filter → reconstruct → LLM extract → deduplicate → route → viking:// storage - Watcher abstraction with BaseFileWatcher ABC and registry-based factory - SQLite cursor persistence for incremental file reads across restarts - Server integration: GET /api/v1/daemon/status, --with-daemon CLI flag - Web UI: daemon status card on home dashboard with per-watcher metrics - Full test suite (18 test files) and documentation
54fe519 to
52b0a34
Compare
Root cause: BatchBuffer.created_at was never set in add_line(), so the time-based trigger never fired and events stayed buffered indefinitely. Additional fixes: - Thread-safe enqueue from watchdog thread via loop.call_soon_threadsafe() - _flush_buffer() now calls callback before clearing buffer (prevents data loss) - KnowledgeRouter uses valid viking://resources/ scope (skills/memories were invalid) - Non-ASCII titles sanitized via sha256 hash to produce valid URI paths - VLM extraction concurrency limited to 2 via semaphore - ClaudeCodeWatcher rewrite: handle nested message.content (text blocks, tool_use) - Project name derived from file path via _post_normalize hook - All exception handlers now include exc_info=True for stack traces - 148 tests passing
…ased AI tool monitoring Extend daemon watcher framework to support SQLite database sources alongside existing JSONL file watchers. BasePollingWatcher uses Thread + Event.wait() polling instead of watchdog Observer, with cursor advancement on all raw events (including filtered) to avoid infinite re-query loops. CursorDBWatcher monitors Cursor IDE's dual-SQLite storage: - Global DB (cursorDiskKV) for bubbleId:* conversation entries - type 1=user, 2=assistant; filters empty text (tool calls/streaming) Includes 28 new tests (12 polling base + 16 cursor_db) and 3 integration tests.
- Mark Phase 1-2 as complete (BasePollingWatcher + CursorDBWatcher) - Add section 4.1: real data E2E validation results against state.vscdb - Add section 4.2: cursor advancement bug fix documentation - Document _discover_composer_ids real data finding (cursorDiskKV, not ItemTable) - Update task checklist and file change list with status
Summary
BasePollingWatcherextends the framework to poll-based sources (databases, APIs) using Thread + Event.wait() instead of watchdog ObservercursorDiskKV→bubbleId:*conversation entries)viking://storageBaseFileWatcherABC +BasePollingWatcherABC + registry-based factory pattern (@register_watcherdecorator) for easy extension to new toolsGET /api/v1/daemon/statusendpoint,--with-daemonCLI flag, FastAPI lifespan-managed lifecycleDaemonConfigwithWatcherConfigarray, backward-compatible singlewatch_dirfallback,OV_DAEMON_*environment variablesArchitecture
Watchers
~/.claude/projects/state.vscdb%APPDATA%\Cursor\User~/.continue/BasePollingWatcher design
Event.wait(interval)for periodic polling (vs watchdog Observer for file watchers)watch_dir(naturally distinct from file watchers'file_pathkeys in shared CursorManager)BatchBuffer()constructed with no args; trigger values held as watcher instance attributes (matching BaseFileWatcher pattern)DaemonService._enqueue_batchalready usescall_soon_threadsafeTest Plan
state.vscdb(20 bubbleId entries → 5 kept, cursor persistence, idempotent re-poll)OV_DAEMON_ENABLED=true openviking serve --with-daemon→ produce conversations → verifyviking://ingestion