Commit Graph

31 Commits

Author SHA1 Message Date
Prateek 367590454d fix: address Bugbot followup — stale paths in JSDoc, unused exports
- Remove developer-specific paths from JSDoc comments in integration tests
- Remove unused sessionExists and capturePane helper exports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:41:19 +05:30
prateek 4b6d62d142
feat: agent plugins, OpenCode plugin, integration tests, CI (#5)
* feat: implement agent plugins (claude-code, codex, aider)

Implement the Agent interface from @agent-orchestrator/core for three
AI coding tools, enabling the orchestrator to launch, detect activity,
and introspect agent sessions.

Claude Code plugin (full implementation):
- JSONL session introspection (summary, session ID, cost, last message type)
- Activity detection via terminal output pattern matching
- Process detection via tmux pane TTY → ps lookup chain
- Launch command generation with permissions/model/prompt support

Codex and Aider plugins (functional stubs):
- Launch command generation with tool-specific flags
- Process detection via TTY lookup
- Stub introspection (no JSONL support yet)

Closes INT-1329

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review — shell escaping, activity detection, tests

- Replace JSON.stringify with POSIX shell escaping (single quotes) for
  all agent launch commands to prevent command injection
- Remove `unset CLAUDECODE &&` shell syntax from getLaunchCommand (breaks
  execFile); moved to getEnvironment as empty string
- Fix detectActivity returning "exited" for empty pane when process is
  confirmed alive — now returns "idle"
- Tighten BLOCKED_PATTERNS to specific error framing (^Error:, ENOENT:,
  APIError:, etc.) to avoid false positives on code output
- Check blocked patterns before input patterns to prevent "EACCES:
  permission denied" matching INPUT_PATTERNS on the word "permission"
- Remove pgrep fallback in findClaudeProcess (could match wrong session)
- Fix extractCost double-counting: prefer costUSD, fall back to
  estimatedCostUsd only when costUSD is absent
- Trim ACTIVE_PATTERNS to stable indicators only
- Add comprehensive vitest tests (127 tests across 3 plugins)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Codex review + lint fixes

- Use `ps -eo pid,tty,args` instead of `comm` for process detection so
  CLI wrappers (node, python) are correctly identified
- Coerce PID from handle.data with Number() + isFinite() to handle
  string-serialized PIDs
- Fix token double-counting: prefer `usage` object, only fall back to
  flat `inputTokens`/`outputTokens` when `usage` is absent
- Add `--` before Codex prompt to prevent prompts starting with `-`
  from being parsed as CLI flags
- Check blocked patterns before input patterns so "EACCES: permission
  denied" isn't misclassified as waiting_input
- Fix lint: merge duplicate imports, strict equality, remove non-null
  assertions
- Format with prettier

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: multi-pane tmux, EPERM handling, strict process matching

Codex review iteration 1 fixes:
- Iterate all tmux pane TTYs instead of only the first — prevents
  false "exited" in multi-pane sessions
- Handle EPERM from process.kill(pid, 0) as "process exists" — EPERM
  means the process is alive but we lack permission to signal it
- Use word-boundary regex for process name matching — prevents false
  positives on similar names like "claude-code" or paths containing
  the substring
- Add tests for EPERM handling, multi-pane detection, and strict
  name matching (134 tests total)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: exclude next-env.d.ts from eslint

Auto-generated Next.js type file triggers triple-slash-reference rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Bugbot review — JSON.parse guard, detectActivity pattern priority

- Guard parseJsonlFile against non-object JSON values (null, arrays,
  primitives) that would cause TypeError in downstream extractors
- Split null output (non-tmux → "active") from empty output (tmux → "idle")
  so non-tmux runtimes don't falsely report idle
- Reorder pattern matching: idle before blocked so stale errors in the
  tmux scrollback don't mask an idle prompt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract shellEscape to @agent-orchestrator/core

Move the duplicated POSIX shell-escaping utility from all three agent
plugins into packages/core/src/utils.ts and re-export from the package
entry point. Plugins now import { shellEscape } from the shared package.

Addresses Bugbot review comment on PR #5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add execFileAsync timeouts, use satisfies for plugin exports

- Add { timeout: 30_000 } to all execFileAsync calls (tmux, ps) per
  CLAUDE.md convention to prevent hanging on stuck processes
- Replace `const plugin: PluginModule<Agent>` with inline
  `export default { ... } satisfies PluginModule<Agent>` to preserve
  narrow literal types on manifest fields

Addresses Bugbot review comments on PR #5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename introspect → getSessionInfo, detectActivity uses JSONL

- Rename Agent.introspect() → getSessionInfo(), AgentIntrospection → AgentSessionInfo
- Claude Code detectActivity: replace tmux screen-scraping with JSONL-based
  detection (file mtime + last entry type)
- Remove terminal pattern constants (ACTIVE_PATTERNS, IDLE_PATTERNS, etc.)
- Update all tests: JSONL-based activity tests, renamed introspect blocks
- waiting_input/blocked states deferred to hooks-based implementation (#16)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add OpenCode agent plugin, integration tests, and CI workflow

- Add agent-opencode plugin with getLaunchCommand, detectActivity,
  isProcessRunning, getSessionInfo (27 unit tests)
- Add integration test suite for all 4 agents (claude-code, codex,
  aider, opencode) using real binaries in tmux sessions
- Add GitHub Actions CI workflow for integration tests
- Add test:integration script to root package.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update core test mocks — introspect → getSessionInfo + isProcessing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Bugbot review — Windows paths, bytesRead, hardcoded paths

- toClaudeProjectPath: handle Windows backslashes and drive letters
- readLastJsonlEntry: use bytesRead to avoid null character corruption
- Remove duplicate next-env.d.ts eslint ignore entry
- Remove hardcoded developer-specific binary paths from integration tests
- Add isProcessing limitation comments with issue references (#17-19)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:28:42 +05:30
prateek 134bc6e177
Merge pull request #3 from ComposioHQ/feat/INT-1327
feat: implement core services (metadata, event-bus, tmux, session-manager, lifecycle-manager)
2026-02-14 07:24:07 +05:30
Prateek f2e0ac1867 fix: filter invalid session IDs in listMetadata to prevent downstream crashes
listMetadata() now validates filenames against VALID_SESSION_ID regex
before returning them, preventing readMetadataRaw() from throwing on
unexpected files in the sessions directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 06:30:13 +05:30
Prateek 660b8bb13d fix: restrict temp file permissions, validate session prefix format
- tmux sendKeys: write temp files with mode 0o600 instead of default umask
- config: validate sessionPrefix matches [a-zA-Z0-9_-]+ (Zod schema)
- config: sanitize derived prefix from project ID to match metadata ID rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 05:17:53 +05:30
Prateek 4028e5b77d fix: suppress immediate notification when send-to-agent reaction handles event
When a send-to-agent reaction was configured (e.g. for ci-failed), the
reactionHandledNotify flag stayed false, causing checkSession() to also
call notifyHuman() immediately — bypassing the retry/escalation workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 04:07:38 +05:30
Prateek 9e7a767730 fix: notify on significant transitions, preserve stuck state on probe failure
Two bugs fixed:

1. Transitions with non-info priority (e.g. merge.completed,
   review.approved, session.errored) now call notifyHuman() directly
   when no reaction config handles notification. Before, these
   transitions updated state silently with no human notification.

2. When agent.detectActivity() throws, sessions in stuck/needs_input
   state now preserve that state instead of being coerced to "working"
   by the fallback at the bottom of determineStatus().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 03:55:19 +05:30
Prateek 95fd47ef2a refactor: remove EventBus, simplify lifecycle manager
The event bus was over-engineered for a system with ~50 sessions. The
dashboard can read metadata files + poll GitHub directly. Remove the
EventBus interface, event-bus.ts module, and all emit/subscribe wiring
from session-manager and lifecycle-manager. The reaction engine (the
valuable part) stays — executeReaction now takes sessionId/projectId
directly instead of going through OrchestratorEvent.

Also fix listMetadata() to filter out directories (not just the
"archive" dir by name) so readMetadataRaw() can't crash on non-file
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 03:38:11 +05:30
Prateek 4c47a42b6e fix: use literal mode for tmux send-keys, prune stale lifecycle trackers
- Add -l flag to tmux send-keys for short messages so key-like payloads
  (e.g. "Enter", "Escape") are sent as text, not interpreted as keypresses
- Prune states and reactionTrackers maps in pollAll() for sessions that
  no longer appear in the session list, preventing unbounded memory growth
  and stale retry counters from applying to reused session IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:09:49 +05:30
Prateek 07b3a553f7 fix: allow notify reactions when auto is false, clean up reserved IDs on spawn failure
- Reactions with action: "notify" bypass the auto: false gate so
  merge-ready notifications fire for approved-and-green config
- All spawn failure paths now delete the reserved session metadata file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:00:58 +05:30
Prateek 65787f08e3 fix: use handle.runtimeName for plugin lookup, atomic session ID reservation
- kill() and send() prefer handle.runtimeName over project defaults
- spawn() uses O_EXCL atomic file creation to prevent concurrent ID collisions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:41:11 +05:30
Prateek a225bc17f5 fix: runtime plugin lookup, post-create guard, restart transition detection
- kill() uses handle.runtimeName to find the correct runtime plugin
- postCreate cleanup skips destroy when workspacePath is project root
- checkSession reads persisted metadata status on first poll after restart

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:16:50 +05:30
Prateek 88ea7d1491 fix: prevent project workspace deletion and agent config workspace leak
- kill() skips workspace.destroy when worktree matches project.path
- spawn() wraps getLaunchCommand/getEnvironment in workspace cleanup guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:55:07 +05:30
Prateek ad0b45006c fix: session recovery, reaction config merge, summary priority
- stuck/needs_input sessions recover to working when agent is active
- Project reaction overrides merge with global defaults (not replace)
- summary.all_complete events get info priority (not action)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:45:33 +05:30
Prateek 24391c874e fix: ignore next-env.d.ts in ESLint config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek f6f5e24e3c fix: add missing cleanup status to VALID_STATUSES set
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek 4a804503b6 fix: execute all-complete reaction and guard workspace postCreate
- summary.all_complete reactions now execute in pollAll() after emission
- workspace.postCreate errors trigger workspace cleanup to prevent leaks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek 2ecb011982 fix: address all review comments, lint/format, bugbot issues
Review fixes:
- metadata: sessionId validation to prevent path traversal
- event-bus: guard appendFileSync, remove unused import
- lifecycle-manager: fix reaction retry counting (track attempts,
  clear on status transition), allow retry on send failure instead
  of immediate escalation, detect killed sessions in polling loop,
  map session.killed to agent-exited reaction
- session-manager: validate status against union, clean up runtime
  and workspace on spawn failure, kill with default plugins when
  project config is missing

Lint/format fixes:
- Remove unused imports across all test files
- Replace `Function` type with typed callback in tmux tests
- Replace require() with dynamic import() in tests
- Replace dynamic delete with object spread in metadata
- Apply prettier formatting to all files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek c49649c2c1 fix: address codex review — path traversal, retry logic, type safety
- metadata: validate sessionId against ^[a-zA-Z0-9_-]+$ to prevent
  path traversal attacks
- lifecycle-manager: fix reaction retry/escalation to count attempts
  (not just successes), escalate after maxRetries attempts; map
  session.killed to agent-exited reaction (was unreachable session.exited)
- session-manager: validate status against known SessionStatus union
  (default to "spawning" for unknown values); clean up workspace on
  runtime creation failure to prevent resource leaks
- event-bus: guard appendFileSync in try/catch so disk errors don't
  crash event emitters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek d6ec95402d fix: address PR review — unsafe casts, tmux race, concurrent polling
- session-manager: replace readMetadata with readMetadataRaw (no unsafe casts),
  add safeJsonParse for JSON.parse guards, escapeRegex for session prefix,
  explicit branch priority, convert from this-based to closure-based methods
- tmux: named buffers (load-buffer -b / paste-buffer -b -d) to prevent
  global paste buffer race on concurrent sendKeys; send Escape before text
  to clear partial input
- lifecycle-manager: concurrent polling with Promise.allSettled instead of
  sequential for-of await, allCompleteEmitted guard to prevent repeated
  summary.all_complete events, polling re-entrancy guard, safe reaction
  config handling with action/auto checks
- tests: update tmux tests for Escape-first and named buffer changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek 2b9a28a031 test: add comprehensive unit tests for all core services
Add vitest test suite with 103 tests across 5 test files:

- metadata.test.ts (19 tests): read/write/update/delete/list, key=value
  parsing, comments, values with equals signs, archiving, dotfile exclusion
- event-bus.test.ts (22 tests): emit/on/off, wildcard listeners, handler
  error resilience, priority inference, JSONL persistence round-trip,
  filtered history with sessionId/projectId/type/priority/since/limit
- tmux.test.ts (23 tests): all tmux wrappers with mocked child_process,
  session listing/parsing, send-keys short vs load-buffer long text,
  capture-pane, kill, getPaneTTY
- session-manager.test.ts (23 tests): spawn pipeline with mocked plugins,
  session numbering, branch derivation, event emission, metadata writing,
  list with dead runtime detection, kill with cleanup, send via runtime
- lifecycle-manager.test.ts (16 tests): state transitions (spawning→working,
  killed, stuck, needs_input, ci_failed, merged, mergeable), reaction
  triggering, auto=false suppression, on/off handlers, getStates isolation

Also fixes inferPriority to handle merge.completed as "action" priority.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek de86054b9d feat: implement core services (metadata, event-bus, tmux, session-manager, lifecycle-manager)
Implement the 5 core service modules in packages/core/src/:

- metadata.ts: Flat-file key=value session metadata read/write matching
  existing bash script format. Supports read, write, update, delete
  (with archive), and list operations.

- event-bus.ts: In-process pub/sub with JSONL append-only persistence.
  Supports typed and wildcard listeners, priority inference from event
  type, filtered history queries, and a createEvent() helper.

- tmux.ts: Async wrappers around tmux commands using child_process.execFile
  (no shell injection). Handles long/multiline messages via load-buffer/
  paste-buffer. Covers list, create, send-keys, capture-pane, kill, and
  pane TTY discovery.

- session-manager.ts: Session CRUD that orchestrates Runtime + Agent +
  Workspace plugins. Full spawn pipeline (workspace → runtime → agent),
  list with live runtime checks, kill with cleanup, batch cleanup checking
  PR state + issue state + runtime liveness, and message delivery.

- lifecycle-manager.ts: State machine per session with configurable polling
  interval. Detects status transitions, emits events, triggers configured
  reactions (send-to-agent, notify, auto-merge) with retry counting and
  time-based escalation. Routes notifications by priority to configured
  notifier plugins.

All services implement interfaces defined in types.ts. Updated index.ts
to export all new modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
prateek 0c535c98d7
fix: CI handles Next.js build separately, fix web tsconfig (#14)
* fix: CI workflow handles Next.js build separately, fix web tsconfig rootDir

- Remove rootDir from web tsconfig (conflicts with Next.js generated .next/types)
- Include .next/types/**/*.ts in web tsconfig
- CI: build non-web packages first, web build allowed to soft-fail
- CI: typecheck excludes web (typechecked by next build)
- CI: test excludes web build dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove || true from web build — bugbot caught it, web errors should fail CI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: override rootDir in web tsconfig to "." for Next.js compat

Next.js generates .next/types/ files that must be under rootDir.
Base tsconfig sets rootDir to "src", web needs "." to include both
src/ and .next/types/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:41:58 +05:30
Prateek c8061ce03f chore: add ESLint, Prettier, CI workflow, and comprehensive CLAUDE.md conventions
- ESLint flat config with typescript-eslint strict rules
- Prettier config (double quotes, semicolons, 2-space indent)
- GitHub Actions CI: lint + typecheck + test on PRs
- Cursor BugBot config (.cursor/BUGBOT.md)
- Comprehensive CLAUDE.md with code conventions, security rules,
  plugin patterns, and common mistakes to avoid
- Fix unused param lint error in plugin-registry.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:01:52 +05:30
Prateek 5058c409d5 feat: scaffold TypeScript monorepo with all plugin interfaces
Phase 0 complete. Establishes:
- pnpm workspace with 18 packages (core, cli, web, 15 plugins)
- Complete type definitions in packages/core/src/types.ts defining
  all 8 plugin slot interfaces (Runtime, Agent, Workspace, Tracker,
  SCM, Notifier, Terminal) + core service interfaces
- YAML config loader with Zod validation and sensible defaults
- Plugin registry with built-in discovery
- CLAUDE.md with conventions for spawned agents

All agents can now branch from main and implement their assigned
packages against the interfaces defined in types.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:02:42 +05:30
Prateek 36b0792eb6 docs: add parallel implementation plan with 7-agent work breakdown
Maps dependency graph, identifies 7 truly independent work streams
after Phase 0 (scaffold + types), with Linear tickets and spawn
commands ready to go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:41:06 +05:30
Prateek 3eecca8460 docs: update architecture with push-first notification model
The human never polls. The system notifies the human. Two-tier event
handling: auto-handle routine issues silently, push notifications only
when human judgment is required. Escalation chains, priority-based
routing, and the dashboard is a drill-down tool, not the primary interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:38:00 +05:30
Prateek 74001b22d0 docs: add competitive research and architecture design artifacts
Comprehensive analysis of 16+ competing agent orchestration tools
(Gas Town, OpenHands, SWE-ReX, Par, claude-flow, etc.) and detailed
architecture design with 8-slot plugin system, session lifecycle state
machine, and human attention optimization patterns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:35:46 +05:30
Prateek 1601ad75c0 feat: add agent-orchestrator (ao) as a self-hosting project
- Create claude-ao-session: worktree-based session manager for ao
  (Linear tickets, GitHub PRs, AO_SESSION env var)
- Add ao|agent-orchestrator case to all shared scripts:
  claude-batch-spawn, claude-spawn, claude-status, claude-open-all,
  claude-review-check, claude-bugbot-fix
- Rewrite CLAUDE.orchestrator.md with full self-hosting instructions:
  commands reference, workflows, architecture, tips
- Session prefix: ao, metadata: ~/.ao-sessions/, worktrees: ~/.worktrees/ao/

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 15:44:17 +05:30
Prateek c16103fb4d rename CLAUDE.local.md to CLAUDE.orchestrator.md
This file contains the shared orchestrator instructions that ship with
the repo. It's loaded explicitly via --append-system-prompt-file when
launching an orchestrator session, rather than auto-discovered as a
CLAUDE.local.md. Users can still have their own CLAUDE.local.md for
personal overrides.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 15:34:24 +05:30
Prateek 0273e8f3d0 feat: initial commit with orchestrator scripts and dev instructions
Import all 18 production orchestrator scripts from ~/. These are direct
copies with hardcoded paths — generalization is the next step. Includes
CLAUDE.local.md with project overview, architecture notes, and roadmap.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 15:25:31 +05:30