Skip to content

feat(agents): add DroidAgentWatcher for Factory Droid sessions#31

Open
panosAthDBX wants to merge 1 commit intoAtaraxy-Labs:mainfrom
panosAthDBX:feat/droid-agent-watcher
Open

feat(agents): add DroidAgentWatcher for Factory Droid sessions#31
panosAthDBX wants to merge 1 commit intoAtaraxy-Labs:mainfrom
panosAthDBX:feat/droid-agent-watcher

Conversation

@panosAthDBX
Copy link
Copy Markdown

Summary

Adds a new agent watcher for Factory Droid sessions, following the existing pattern used by Claude Code, Codex, Amp, OpenCode, and Pi.

  • Watches ~/.factory/sessions/<encoded-path>/<session-id>.jsonl for appended events
  • Parses Droid JSONL entries (session_start, message, todo_state, session_end)
  • Maps entries to running / waiting / done / stale statuses using the same semantics other watchers use
  • Resolves each session to its mux session via the encoded cwd in the folder name (/Users/foo/proj-Users-foo-proj)
  • Detects permission prompts (assistant tool_use + file stops growing past threshold → waiting)
  • Detects stuck processes (running/waiting + no growth past threshold → stale)

Changes

  • New: packages/runtime/src/agents/watchers/droid.ts (DroidAgentWatcher, 461 lines)
  • New: packages/runtime/test/droid-watcher.test.ts (15 tests, all pass)
  • Register and export DroidAgentWatcher from packages/runtime/src/index.ts
  • Register in apps/server/src/main.ts alongside other built-in watchers
  • Add droid: ["droid", "factory"] to the agent pattern map in packages/runtime/src/server/index.ts

Test plan

  • bun test packages/runtime/test/droid-watcher.test.ts — 15/15 pass
  • bun test packages/runtime/test — 382/382 pass (no regressions)
  • Running locally in my tmux setup for ~weeks, tracks Droid sessions correctly alongside Claude Code and Codex

Happy to iterate on style, docstring placement, or the status-mapping thresholds if they should match constants elsewhere in the codebase.

Copy link
Copy Markdown

@inspect-review inspect-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inspect review

Triage: 45 entities analyzed | 0 critical, 0 high, 3 medium, 42 low
Verdict: standard_review

Findings (4)

  1. [low] Mixed time bases in seed vs runtime heuristics: in seed mode toolUseSeenAt and lastGrowthAt are set to mtimeMs (filesystem timestamp), but unchanged-file heuristics compare against Date.now() (wall clock). This can cause immediate or delayed waiting/stale transitions depending on file age/clock skew. Evidence: seed set toolUseSeenAt: ... ? mtimeMs : undefined and lastGrowthAt: ... ? mtimeMs : undefined; unchanged path uses const now = Date.now(); now - prev.toolUseSeenAt and now - prev.lastGrowthAt.
  2. [low] Runtime incompatibility: watcher uses Bun-specific APIs (Bun.file(...)) inside packages/runtime/src/agents/watchers/droid.ts. If runtime is executed under Node (common for server runtimes), this will throw because Bun is undefined. Evidence: text = await Bun.file(filePath).text(); and const buf = await Bun.file(filePath).arrayBuffer();.
  3. [low] Incremental read is not actually incremental and can be a production perf bug: despite the comment 'only new bytes', it reads the entire file into memory every time via Bun.file(filePath).arrayBuffer() and then slices. Large/long-running JSONL sessions will cause repeated full-file reads. Evidence: // --- Incremental read: only new bytes --- followed by const buf = await Bun.file(filePath).arrayBuffer();.
  4. [low] Potential unhandled promise rejection from fs.watch callbacks: processFile is async and is invoked without await/.catch() inside watch(...) handlers. If processFile ever throws (e.g., unexpected runtime error), it can surface as an unhandled rejection. Evidence: watch(dirPath, (...) => { ... this.processFile(...); }) and similarly for the sessionsDir watcher.

Reviewed by inspect | Entity-level triage found 0 high-risk changes

@Palanikannan1437
Copy link
Copy Markdown
Contributor

I've used droid in the past, awesome harness! will test this out on my end and leave feedback if needed, thanks for the PR 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants