Commit Graph

81 Commits

Author SHA1 Message Date
suraj_markup a37a210977
Merge pull request #593 from suraj-markup/fix/resolve-project-cwd-matching
fix: auto-detect project from cwd in multi-project configs
2026-03-21 18:51:24 +05:30
suraj-markup 6d1055f412 fix: simplify cwd matching — remove redundant tilde expansion
loadConfig() already expands ~ in project paths via expandPaths(),
so manual replacement is unnecessary. Use resolve(proj.path) directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:08:15 +05:30
suraj-markup 36098efd0b fix: auto-detect project from cwd in multi-project configs
When multiple projects are configured and no project argument is given,
`ao start` and `ao stop` now match the current working directory against
project paths before erroring. This lets users run `ao start` from within
a project directory without specifying the project name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:02:42 +05:30
github-actions[bot] c7d6634839 chore: version packages 2026-03-20 15:47:55 +00:00
suraj-markup 4fcdc674d4 Rename npm package from @composio/agent-orchestrator to @composio/ao
Users can now install with:
  npm install -g @composio/ao

Instead of the longer:
  npm install -g @composio/agent-orchestrator

- Rename packages/agent-orchestrator → packages/ao
- Update package.json name and directory field
- Update all references in README, SETUP.md, changeset config, and CLI error messages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 02:17:02 +05:30
suraj-markup 716be0cb74 Fix Bugbot review comments: tilde expansion, duplicate names, cross-platform detect
- Fix autoDetectProject path matching: expand ~ before comparing project
  paths to cwd, so `path: ~/my-repo` matches `/Users/user/my-repo`
- Fix addProjectToConfig: detect duplicate directory names and auto-suffix
  instead of silently overwriting existing project entries
- Fix agent detect() in all 4 plugins: replace `which` (Unix-only) with
  direct `--version` invocation for cross-platform compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:27:01 +05:30
suraj-markup 596913aa34 Fix review issues: busy-wait loops, lock race, import hygiene, typed errors
- Replace CPU-burning spin loops in running-state.ts with async setTimeout
  and jittered backoff; make all exports async
- Fix TOCTOU race in acquireLock by extracting tryAcquire helper with
  retry loop instead of force-remove-and-retry-once
- Hoist dynamic imports in addProjectToConfig/choice-2 to static imports
- Add try/finally around readline in detectAgentRuntime
- Validate session prefix uniqueness in "Start new orchestrator" menu
- Add ConfigNotFoundError class to @composio/ao-core, replace fragile
  string matching in ao start with instanceof check
- Fix setup.sh to recommend `ao start` instead of deprecated `ao init`
- Fix start-all.ts: resolve next binary with fallback, wait for children
  on SIGTERM instead of immediate process.exit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:33:06 +05:30
suraj-markup 511d2b7fb9 Resolve remaining PR review comments
- Remove redundant project-existence check after autoDetectProject
- Wrap SIGTERM in try/catch for restart flow (dead process case)
- Remove unused setCallerContext export from caller-context.ts
- Fix unused vi import lint error in init.test.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:51:02 +05:30
suraj-markup 47a043ff6f Fix CI test hang: add missing mocks for new imports in start.test.ts
- Add vi.mock() for running-state, caller-context, detect-env,
  detect-agent, and project-detection modules imported by start.ts
- Without these mocks, vitest threads never exit (open handles)
- Remove init.test.ts test that triggered dynamic import of start.js
- Relax waitForPortAndOpen assertion (port resolution depends on
  full dashboard startup which is mocked out)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:41:55 +05:30
suraj-markup 09f757dd0e Fix PR review comments: spawn hint, dead code, detect import
- Fix spawn hint in start.ts to use new single-arg syntax
- Remove dead addProjectOnly() export (add-project fully deleted)
- Make detect-agent.ts handle both named and default export shapes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:14:42 +05:30
suraj-markup 030c669c37 Show friendly warning instead of hard error for old two-arg spawn syntax
When users run `ao spawn <project> <issue>`, show a yellow warning
explaining the new syntax (`ao spawn <issue>`) instead of Commander's
raw "too many arguments" error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 14:32:35 +05:30
suraj-markup 44313f925d Remove two-arg support from ao spawn and batch-spawn
- spawn now takes only `[issue]` — project is always auto-detected
- batch-spawn now takes only `<issues...>` — no project prefix
- Commander rejects extra positional args automatically
- Update orchestrator prompt to remove projectId from spawn example
- Rewrite spawn tests to use auto-detected project (single project in config)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 14:29:14 +05:30
suraj-markup 79c2274bfe Fix remaining CI failures: init tests + direct-terminal-ws lint
- Rewrite init.test.ts to match simplified deprecated init command
  (old tests referenced removed --output, --auto, --smart flags)
- Add eslint-disable for consistent-type-imports in direct-terminal-ws.ts
  (node-pty is optional, must use import() type annotations)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 14:14:13 +05:30
suraj-markup a1a9966ed5 Fix CI failures: lint eqeqeq + test manifest displayName
- Change != null to !== undefined && !== null in caller-context.ts and
  session-manager.ts (3 locations) to satisfy eqeqeq lint rule
- Add displayName field to all 4 agent plugin test manifest assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:55:08 +05:30
suraj-markup 0b1bd49940 Address PR review comments: unused import, stop orphan, docs accuracy
- Remove unused getCallerType import from start.ts
- Fix ao stop orphaning dashboard: always run stopDashboard via lsof
  after killing parent PID, since SIGTERM may not propagate to child
- Revert spawn single-project special case back to always matching
  project ID (backward compat)
- Update README: replace ao init --interactive and ao add-project
  references with ao start equivalents, update spawn syntax

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:44:29 +05:30
suraj-markup 630586f66c Fix medium issues: lockfile, SIGTERM wait, config overwrite, spawn ambiguity
- Add advisory lockfile (O_EXCL) to running-state.ts to prevent TOCTOU
  race when multiple ao start processes run concurrently
- Wait up to 5s for old process to exit after SIGTERM in Override menu,
  escalate to SIGKILL if needed — prevents port conflict on restart
- Guard autoCreateConfig against overwriting existing config files —
  returns existing config instead of silently replacing it
- Fix spawn single-arg disambiguation: in single-project configs, always
  treat the arg as an issue ID (user never needs to specify project)
- Clean up running.json by deleting file instead of writing "null"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:57:26 +05:30
suraj-markup 694681f728 Fix review issues: ESM compat, prompt accuracy, port registration
- Replace require() with execFileSync in all 4 agent plugin detect()
  functions — fixes ReferenceError in ESM-only environments
- Remove non-existent -p flag from orchestrator prompt spawn/batch-spawn
  docs — prevents orchestrator agent from generating broken commands
- Return actual port from runStartup() and pass to register() — fixes
  running.json storing wrong port when auto-escalation picks a free port

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:49:53 +05:30
suraj-markup f3f81dad84 Simplify onboarding: absorb init + add-project into ao start
Reduces onboarding to `npm install -g @composio/ao && ao start`.

- Absorb init and add-project logic into `ao start` with auto-config
  creation, environment detection, and project type detection
- Add single-instance tracking via running.json with already-running
  interactive menu (human) and info+exit (agent)
- Add caller context detection (human/orchestrator/agent) via TTY and
  AO_CALLER_TYPE env var
- Add plugin-based agent runtime detection — no hardcoded binary paths,
  each plugin exports detect() and displayName
- Simplify `ao spawn` to just `ao spawn <issue>` with auto-detected
  project (backward compat: `ao spawn <project> <issue>` still works)
- Set AO_CALLER_TYPE, AO_PROJECT_ID, AO_CONFIG_PATH, AO_PORT env vars
  on all spawned sessions (worker, orchestrator, restore)
- Add `ao config-help` subcommand for config schema reference
- Deprecate `ao init` to thin wrapper, fully remove `ao add-project`
- Register/unregister in running.json on start/stop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 01:17:33 +05:30
suraj-markup 14fa726551 Publish ao-web on npm, harden error handling, deduplicate code
- Publish @composio/ao-web dashboard as npm package (removed private flag,
  added files field, production entry point, node-pty made optional)
- CLI auto-detects dev vs production mode for dashboard startup
- Use local next binary instead of npx in production start-all.ts
- findWebDir() throws with install-specific guidance instead of returning
  broken path
- Fix CI-silent failure: setup.sh and ao-update.sh exit 1 on non-interactive
  npm link failure
- Deduplicate detectDefaultBranch into shared cli/lib/git-utils.ts
- Add EACCES permission guidance to README.md and SETUP.md
- Move resolveProjectIdForSessionId to @composio/ao-core
- Update design doc with problems #8-13, changes #9-12, known limitations
  section, and expanded test plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 19:01:49 +05:30
suraj-markup 90fe0e09f8 Add contextual next-step hints with directory guidance
Every command now prints what to run next and from where:
- setup.sh → tells user to cd to project dir and run ao init
- ao init → tells user to run ao start (with directory context)
- ao add-project → tells user to run ao start and ao spawn
- ao start → tells user to run ao spawn <project> <issue>

Eliminates confusion about running commands in the wrong directory
(e.g., ao init inside agent-orchestrator instead of the project repo).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 22:07:58 +05:30
suraj-markup 457e83f7c9 feat: auto-detect init defaults, auto-find free port on start
- `ao init` now auto-detects project with zero prompts by default
- Old 13-prompt wizard moved to `ao init --interactive`
- Removed dead `--smart` placeholder flag
- `ao start` auto-finds next free port if configured port is busy
  instead of crashing with an error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:46:32 +05:30
suraj-markup 672ee0e0c5 feat: reduce onboarding friction with npm link fix and ao add-project command
- Fix npm link EACCES permission error in setup.sh, ao-update.sh, and ao-doctor.sh
  by auto-retrying with sudo when interactive
- Add `ao add-project <path>` command that auto-detects git remote, default branch,
  project type, and generates agent rules — appends to existing config in one step

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:55:42 +05:30
Harsh Batheja 1d960feb6b
fix: protect orchestrators from session cleanup (#453)
* fix: harden orchestrator cleanup selection

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: suppress orchestrators in cleanup command output

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: remove unreachable cleanup guard branch

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-13 09:39:49 +05:30
Harsh Batheja 3d518aed2f
feat: add doctor and update maintenance tooling (#437)
* feat: add ao doctor maintenance checks

* feat: add ao update refresh script

* feat: add shared CLI script runner

* feat: add doctor and update CLI commands

* docs: document doctor and update commands

* fix: harden maintenance safety checks

* fix: harden ao update behavior

* fix: strip inline comments from doctor config values
2026-03-12 20:59:22 +05:30
Harsh Batheja b04d3e21de
feat: add end-to-end observability across core, web, and terminal (#436)
* feat: add end-to-end observability across core, web, and terminal

Instrument lifecycle/API/websocket flows with correlation-aware metrics and operator health surfaces so the system can self-diagnose and escalate failures.

* fix: realign session restore set and unblock claim PR typecheck

* fix(web): restore project-filtered sessions route after main merge

* fix(core): remove unused orchestrator import to unblock lint

* fix(core): always record lifecycle poll failures and remove dead review branches

* fix(web): harden websocket metrics and reuse SSE observers

* fix(web): preserve primary session API errors when services bootstrap fails

* fix(web): use full orchestrator config type in SSE observer helper

* fix(web): restore project-scoped SSE and guard observability error paths

* fix(web): address remaining Bugbot review gaps

* fix(web): align SSE project attribution and share session project resolver

* fix(web): require request arg for SSE route and align tests

* fix(web): record websocket error disconnects as failures

* fix(web): use path alias for session project resolver import

* fix(web): include active connection count in disconnect metrics
2026-03-12 16:57:40 +05:30
Harsh ac49fb4b81 fix: separate orchestrators from worker status output
Keep orchestrator control sessions visible in CLI status without counting them as worker sessions, and preserve explicit roles in JSON output for multi-project tooling.
2026-03-11 23:33:25 +05:30
Harsh Batheja c1e8c1e839
fix: prevent orchestrator sessions from owning PRs (#432)
* fix: repair orchestrator PR metadata and session branch allocation

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: harden orchestrator prompt delegation rules

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: hide orchestrator PR ownership in status

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: restore session suffix parsing for branch allocation

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: avoid dynamic delete in metadata repair path

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: reduce read-time repair overhead and preserve activity timestamps

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* refactor: share orchestrator read-repair logic

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-11 22:04:38 +05:30
Harsh Batheja 4edf19df32
feat: lifecycle manager, backlog auto-claim, task decomposition, and verification gate (#365)
* feat: wire lifecycle manager, backlog auto-claim, and dashboard overhaul

- Start LifecycleManager in dashboard server (30s polling) so reactions
  actually fire: CI failures, review comments, merge conflicts are now
  auto-forwarded to agents
- Add backlog auto-claim poller (60s interval) that watches for issues
  labeled `agent:backlog` and auto-spawns agent sessions up to max
  concurrent limit (5)
- Add tabbed dashboard UI: Board (kanban), Backlog (issue queue), PRs
- Add issue creation form in dashboard — creates GitHub issues with
  `agent:backlog` label for immediate agent pickup
- Add API routes: /api/backlog, /api/issues, /api/setup-labels
- Pass notifier config through plugin registry (slack webhook fix)

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

* feat: add task decomposition layer (classify → decompose → recurse)

Adds LLM-driven recursive task decomposition upstream of session spawning.
Complex issues are broken into atomic subtasks before agents start working.
Each agent receives lineage context (where it fits in the hierarchy) and
sibling awareness (what parallel agents are doing).

Core changes:
- New decomposer module (core/src/decomposer.ts) — classify, decompose,
  plan tree, lineage formatting, using Claude API
- Extended SessionSpawnConfig with lineage/siblings fields
- Prompt builder Layer 4: decomposition context (hierarchy + siblings)
- ProjectConfig.decomposer config section with Zod validation
- Tracker plugin: added removeLabels support for label management

CLI:
- `ao spawn <project> <issue> --decompose` flag
- `--max-depth <n>` option for decomposition depth
- Spawns multiple sessions with lineage context for composite tasks

Backlog poller:
- Respects project.decomposer.enabled for auto-decomposition
- Posts plan as issue comment when requireApproval=true
- Auto-spawns subtasks with lineage when requireApproval=false

Config example:
  projects:
    my-app:
      decomposer:
        enabled: true
        maxDepth: 3
        requireApproval: true

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

* feat: add verification gate — issues stay open until human confirms fix

PR merge no longer auto-closes GitHub issues. Instead:

1. On PR merge: issue labeled `merged-unverified`, stays open
2. Human checks staging, then runs `ao verify <issue>` to close
3. Or `ao verify <issue> --fail` to flag verification failure

Changes:
- services.ts: labelIssuesForVerification() replaces closeIssuesForMergedSessions()
- New CLI command: `ao verify` (verify/fail/list modes)
- New API route: GET/POST /api/verify
- Dashboard: new Verify tab with one-click verify/fail buttons
- ao status: shows count of issues awaiting verification
- Idle session detection + auto-nudge reaction
- Use TERMINAL_STATUSES in batch-spawn dedup check

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

* fix: rename decomposerConfig to avoid variable shadowing

Addresses Bugbot medium severity issue where inner  variable
shadowed outer  from getServices().

* fix: update pnpm-lock.yaml for new @anthropic-ai/sdk dependency

* fix: resolve remaining merge conflicts and syntax errors

- Remove leftover conflict markers in types.ts
- Remove orphaned code in services.ts
- Fix semicolon to comma in config.ts
- Remove unused import in verify.ts

* fix: address final Bugbot issues

- requireApproval path now exits early with continue to prevent
  fall-through to in-progress label and session spawned comment
- remove packages/core/package-lock.json (pnpm workspace should only
  use root pnpm-lock.yaml)

* fix: idle sessions now transition back to working

When agent resumes activity after being idle, the status correctly
transitions to 'working' instead of remaining stuck in 'idle' state.

* fix(backlog): remove agent:backlog label when claiming issues

When claiming issues from the backlog, the poller now removes the
agent:backlog label in addition to adding agent:in-progress. This
prevents duplicate work if all spawned sessions reach terminal status
and the poller rediscovers the issue.

* fix(test): use Set for TERMINAL_STATUSES mock

The mock for TERMINAL_STATUSES was an array, but the real export is a
ReadonlySet. Changed to use a Set so tests with non-empty sessions won't
crash when calling .has().

* fix(web): resolve backlog/dashboard regressions after branch sync

* fix(web): align dashboard events hook and SSE test mocks

* fix(notifier-openclaw): apply exponential delay from retry index

* fix(integration-tests): align openclaw retry delay expectation

* fix(web): keep dashboard header stats in sync

* fix(openclaw): keep first retry at base delay

---------

Co-authored-by: Agent <agent@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Harsh <harsh@Ubuntu-24-Forrest.lan>
Co-authored-by: Harsh <harsh@example.com>
2026-03-10 12:31:25 +05:30
Harsh Batheja 650d3ad695
fix(core): enforce single-owner PR claim consolidation (#390)
* fix(core): enforce single-owner PR claim consolidation

* refactor: remove dead `takeover` option from claimPR

Since PR consolidation is now automatic (single-owner enforcement),
the `--takeover` flag became dead code. Users passing it got no
error but it had zero effect.

This commit:
- Removes takeover from ClaimPROptions interface
- Removes --takeover flag from spawn and session CLI commands
- Updates orchestrator-prompt.ts examples
- Updates tests to reflect automatic consolidation

Tests: ao-core 403 passing, ao-cli 189 passing (spawn+session)

* fix: remove stale --takeover from Quick Start example

Remove remaining --takeover reference in orchestrator prompt Quick Start section.

* feat(core): document and test asymmetric PR ownership model

- Add JSDoc to claimPR documenting RULE A (exclusive PR->Agent) and
  RULE B (Agent->Many PRs) ownership contract
- Add tests for:
  - Same session claiming multiple PRs sequentially (RULE B)
  - Idempotent re-claim by same owner
  - Stale/dead prior owner handoff
  - Exclusive PR ownership enforcement (RULE A)

139 tests passing

---------

Co-authored-by: Harsh <harsh@Ubuntu-24-Forrest.lan>
2026-03-10 11:54:26 +05:30
Harsh Batheja 003eb78adb
fix: pause workers on model limits and stabilize session visibility (#367)
* fix: pause workers on model limits and stabilize session visibility

Detect model limit exhaustion, pause project worker operations until reset, and expose pause state in dashboard/API. Also harden killed-session cleanup and SSE session reconciliation so sessions do not appear ghost-active or disappear until reload.

* fix: satisfy lint in rate-limit pause probe

* fix: address bugbot feedback for pause handling

* fix(lifecycle): prevent infinite re-pause loop for duration-based rate limits

Duration-based rate limits (e.g., 'usage limit reached for N hours') were
causing infinite re-pause loops because they always calculate reset time as
Date.now() + duration, which extends the pause on every poll cycle if the
message remains in terminal output.

Now checks for existing active pause before setting a new one:
- Skips override if same session already has active pause
- Preserves longer pauses from other sessions

Fixes infinite loop described in PR #367 review comment.

* fix(core): export global pause constants to prevent duplication

Export GLOBAL_PAUSE_*_KEY constants and parsePauseUntil utility from
@composio/ao-core so web package can import them instead of hardcoding.

This prevents silent breakage if key values ever change - now there's
a single source of truth.

Addresses review comment on PR #367.

* fix(web): globalPause as first-class state in SSE event flow

globalPause is now part of the same reducer/event flow as sessions,
not derived from provider-specific output text in the UI.

Changes:
- useSessionEvents: manage globalPause alongside sessions in reducer state
- Dashboard: consume globalPause from hook instead of SSR-only prop
- types: re-export GlobalPauseState from shared lib (provider-agnostic contract)
- Tests: 15 new tests proving banner appears/disappears from state updates
  alone, regardless of agent model/plugin (Claude Code, OpenCode, Codex)

Design requirements satisfied:
- First-class state in same reducer/event flow as sessions
- Key names sourced from shared core contract via export/import
- Provider-neutral: no Anthropic/OpenAI string coupling in control logic
- State-driven: banner visibility from reducer state updates via SSE

Addresses Bugbot finding: Dashboard pause banner never updates after
initial render (eba24a0b-9e4c-47e3-91c9-7d10be01e3cf)

* fix: remove unused import in test file

* fix(cli): consistent purge default for session kill and stop commands

The kill method's default changed from opt-in (=== true) to opt-out (!== false).
Both session kill and stop commands now use the same logic:
  purgeOpenCode = opts.purgeSession === true ? true : opts.keepSession !== true

This ensures consistent behavior across all kill paths.

Adds --keep-session flag to both commands.
Adds regression tests for provider-agnostic behavior verified.

Addresses Bugbot finding: orchestrator start cleanup inconsistent
with new purge default (2bc2535f-b2d1-4f64-96d6-150f97ed8564)
2026-03-10 08:42:17 +05:30
prateek a99d37b1d0
feat(ao): add OpenClaw notifier plugin for AO escalations
Adds AO notifier-openclaw (webhook-first Phase 0), registry wiring, tests, docs, and BugBot fixes.
2026-03-09 18:24:30 +05:30
Harsh Batheja cc2031f0f6
fix: bugbot follow-ups from PR #315 (#357)
* fix: address bugbot follow-ups in send and opencode discovery

* fix(test): update stale integration test expectation for opencode bootstrap

The getLaunchCommand implementation now uses a robust bootstrap pattern
that captures the session ID between 'opencode run' and 'exec opencode --session'.
Updated test to check both parts separately instead of expecting a contiguous
string that no longer exists.

* fix: avoid NaN in sort comparator when both timestamps are missing

The comparator (b.updatedAt ?? -Infinity) - (a.updatedAt ?? -Infinity)
produces NaN when both timestamps are missing because -Infinity - (-Infinity)
is NaN in IEEE 754. A comparator returning NaN violates ECMA-262's
'consistent comparison function' requirement, making sort results
implementation-defined.

Fixed by adding equality check before subtraction:
- If both timestamps are equal (including both -Infinity), return 0
- Otherwise return the difference
2026-03-08 12:50:54 +05:30
Harsh Batheja 4e2144d99e
feat: OpenCode session lifecycle and CLI controls (#315)
* feat: refine OpenCode session reuse strategy and cleanup

* fix: harden OpenCode session selection and lint errors

* refactor: centralize OpenCode reuse resolution flow

* fix: return 404 for missing session in message route

* feat: replace force remap with terminal reload control

* fix: protect project path from session kill cleanup

* fix: preserve fullscreen alignment without reload action

* fix: harden OpenCode session id handling and title reuse selection

* fix: show OpenCode reload control and remap before restart

* fix: preserve title-only OpenCode reuse with fallback mapping persistence

* fix: resolve remaining PR315 Bugbot findings

* fix: keep OpenCode discovery title-based without timestamp sorting

* fix: avoid enrichment race fallout in session listing

* fix: guard OpenCode discovery parse with array check

* fix: stabilize Linear comment integration check

* fix: harden OpenCode discovery and prompt option flow

* ux: clarify OpenCode terminal restart action

* fix: remap OpenCode session fresh on each restart

* fix: validate remap session ids before reuse

* fix: clean archived metadata only after purge

* docs: align OpenCode remap selection with title-based behavior

* fix: harden opencode cleanup and ignore local sisyphus state

* test: add timeout cleanup coverage for session enrichment

* fix: harden Linear integration helper against transient non-JSON errors

* fix: make linear integration assertions resilient to eventual consistency

* fix: remove unused fs import after rebase

* fix: address remaining Bugbot blockers for opencode session handling

* fix: avoid stale metadata overwrite during restore post-launch

* fix: forward subagent in orchestrator flows and defer reuse lookup

* fix: apply configured subagent fallback for session spawn

* fix: scope archived cleanup by project and delay archive restore write

* fix: normalize orchestrator strategy aliases in start display logic

* fix: centralize orchestrator strategy normalization in core

* fix: derive orchestrator reuse display from spawn result

* fix: keep cleanup results consistent across project-id collisions

* fix: namespace cleanup results when session IDs collide

* fix: harden GitHub issue stateReason fallback

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: avoid false failing CI state mapping

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: add tmux command timeouts

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: bound session API enrichment latency

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: repair scm-github merge resolution

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: repair lifecycle-manager test merge

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: delay archive metadata recreation until restore passes

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test: restore claim-pr session mocks

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: address review findings for OpenCode lifecycle PR

- Fix stripControlChars to preserve newlines for reload commands
- Add SessionNotFoundError and use instanceof checks in API routes
- Document orchestratorSessionStrategy in YAML example
- Validate existingSessionId with asValidOpenCodeSessionId()
- Extract inline Node script to buildSessionLookupScript helper
- Create OpenCodeSessionManager interface for remap capability
- Create OpenCodeAgentConfig type for agent-specific config
- Change default orchestratorSessionStrategy from delete to reuse

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

* fix: guard reused session display without metadata

* chore: add agent config files to .gitignore

Agent configuration files (CLAUDE.md, AGENTS.md, IMPROVEMENTS.md, etc.) are personal and project-specific. They should not be committed to the repository.

Changes:
- Remove CLAUDE.md from git tracking
- Add agent config files to .gitignore
- Create .gitignore-template for reference

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

* chore: update gitignore for agent config folder structure

Reorganized agent configuration files:
- CLAUDE.md and AGENTS.md stay in root (agents read them there)
- Tracking files move to .opencode/ (IMPROVEMENTS.md, etc.)
- Optional Claude files in .claude/

Updated .gitignore to ignore folders instead of individual files.

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

* fix: tighten opencode remap and session discovery safeguards

* fix: address Bugbot findings in session manager

* fix: scope opencode discovery to opencode sessions

* fix: restore concurrent listing and strict permission literals

* fix: throw SessionNotFoundError, parallelize list enrichment, fix permissions type

- Session manager now throws SessionNotFoundError instead of plain Error
  for missing sessions, so web API routes correctly return 404 (not 500)
- Parallelize session enrichment in list() — was sequential, causing O(N)
  latency for N sessions with subprocess enrichment
- Fix AgentLaunchConfig.permissions type to accept legacy "skip" value
  (AgentPermissionInput instead of AgentPermissionMode)
- Add happy-path and validation tests for /api/sessions/:id/message route
- Update all test mocks to use SessionNotFoundError

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

* fix: route ao send through session manager

* feat: add purge option to orchestrator stop

* fix: register opencode agent in web services

* test: align web API missing-session coverage

* fix: match notifier config by plugin name

* refactor: dedupe session lookup and tmux buffer send flow

* test: update send lifecycle wait expectation

* fix: harden send routing and cleanup purge controls

---------

Co-authored-by: Harsh <harsh@Ubuntu-24-Forrest.lan>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Prateek <karnalprateek@gmail.com>
2026-03-08 09:55:44 +05:30
Prateek db79ad9423 test: align codex plugin version checks and stabilize init port tests 2026-03-07 21:02:07 +05:30
Jayesh Sharma 8801502871
feat: add PR claim flow for agent sessions (#326)
* feat: add PR claim flow for agent sessions

* feat: support PR claim during spawn

* fix: address PR review feedback

* feat: add lifecycle worker automation

* fix: prevent duplicate messages on unconfirmed delivery and deduplicate review prompt

sendWithConfirmation now treats unconfirmed delivery as a soft success
since the message was already sent, preventing the dispatch hash from
staying stale and causing duplicate sends on the next poll cycle.

review-check command now sources its prompt from the lifecycle reaction
config instead of hardcoding it, keeping both paths aligned.

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

* fix: pass AO_CONFIG_PATH to spawned lifecycle worker and log duplicate-worker early exit

The spawned lifecycle worker now receives AO_CONFIG_PATH in its
environment so it finds the correct config regardless of CWD. Also
added a log message when exiting early due to an existing worker.

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

* fix: prevent lifecycle worker from clearing metadata on SCM fetch failures

SCM methods (getPendingComments, getAutomatedComments) silently returned []
on error, causing maybeDispatchReviewBacklog to treat fetch failures as
"no comments" and clear all tracking metadata every poll cycle. The worker
appeared to do nothing because it kept resetting its own state.

- SCM methods now propagate errors instead of returning []
- maybeDispatchReviewBacklog distinguishes null (fetch failed, skip) from
  [] (confirmed empty, safe to clear)
- pollAll() logs errors instead of silently swallowing them
- Added heartbeat logging and stdout flush before process.exit

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

* fix: add debug breadcrumbs when review comment fetch fails

Logs a console.debug message when getPendingComments or
getAutomatedComments fails, making it clear the lifecycle loop is
preserving metadata due to a fetch failure rather than genuinely
having no comments.

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

* fix: scope lifecycle worker polling to its project and add missing test

pollAll() now passes the worker's projectId to sessionManager.list(),
so each per-project lifecycle worker only polls its own sessions. This
prevents duplicate SCM API calls and race conditions in multi-project
configs.

Also fixed misleading test name and added a test verifying that
--no-dashboard alone still starts the lifecycle worker.

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

* fix: remove dead confirmation check and respect project-level reaction overrides

Removed the unreachable "could not be confirmed" guard from the retry
logic since sendWithConfirmation now returns silently on unconfirmed
delivery.

review-check now resolves the prompt per-session by checking
project-level reaction overrides before falling back to the global
config, matching lifecycle worker behavior.

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

* fix: close TOCTOU race in lifecycle worker spawn and unexport getRegistry

Write the child PID from the parent immediately after spawn, closing
the window where a concurrent ensureLifecycleWorker could pass the
"not running" check and spawn a duplicate worker.

Also removed the unnecessary export on getRegistry since it has no
external consumers.

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

* fix: always include ao base prompt on spawn

* fix: clear heartbeat on shutdown and add initial delay to confirmation loop

Clear the heartbeat interval in the shutdown handler to prevent it
firing during the stream-flush window after lifecycle.stop().

Move the sleep in sendWithConfirmation to before each check (including
the first), so the runtime has time to reflect the message before
the first confirmation attempt.

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

* fix: resolve lint errors in lifecycle and session manager

- Replace dynamic delete with object reconstruction in lifecycle-manager
- Add { cause } to re-thrown errors in session-manager for preserve-caught-error rule

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:54:19 +05:30
prateek a4a1a32e96
fix: auto-detect free port in ao start <url> config generation (#275)
* fix: auto-detect free port in ao start <url> config generation

When generating a new config via `ao start <url>`, auto-detect a free
port instead of hardcoding 3000. Reuses `findFreePort` (extracted to
shared web-dir.ts from init.ts). When port was auto-selected, skip the
strict preflight port check and silently find another free port if
needed (race condition).

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

* fix: add polling to listIssues integration test for Linear API consistency

The listIssues test was failing because Linear's API has eventual
consistency — a just-created issue may not appear in list queries
immediately. Other tests in this file already use pollUntilEqual for
the same reason. Use pollUntil to retry until the issue appears.

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

* fix: restore coupling between MAX_PORT_SCAN and findFreePort

MAX_PORT_SCAN was decoupled from findFreePort after extraction to
web-dir.ts — init.ts kept a local constant only used in messages while
findFreePort had its own default, and start.ts hardcoded 99. Export
MAX_PORT_SCAN from web-dir.ts as the single source of truth, use it as
findFreePort's default parameter, and import it in both callers.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:03:20 +05:30
prateek b5128cf69f
fix: use gh repo clone for private repo auth in ao start <url> (#273)
HTTPS clone fails on private repos without credentials. Now tries
three strategies in order:
1. gh repo clone (handles GitHub auth via gh auth token)
2. git clone with SSH URL (works with SSH keys)
3. git clone with HTTPS URL (public repos fallback)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:42:35 +05:30
prateek e72593d4f8
fix: URL start multi-project resolution and cross-platform browser open (#272)
* fix: URL start handles multi-project configs and cross-platform browser open

When `ao start <url>` loads an existing config with multiple projects,
resolve the correct project by matching the URL's owner/repo against
each project's `repo` field instead of failing with "Multiple projects".

Also fix browser open to use platform-appropriate command (open/xdg-open/start)
instead of hardcoded macOS `open`.

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

* fix: use shallow clone (--depth 1) for faster repo onboarding

Full clones of large repos can take minutes. Shallow clone with
depth 1 fetches only the latest commit, making `ao start <url>`
near-instant for any repo size.

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

* fix: use cmd.exe /c start for Windows browser open

`start` is a cmd.exe builtin, not a standalone executable — spawn
would fail with ENOENT. Run via cmd.exe /c with empty title arg.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:26:41 +05:30
prateek 80a1d59999
feat: add `ao start <url>` one-command project onboarding (#267)
* feat: add `ao start <url>` one-command project onboarding

When `ao start` receives a repo URL instead of a project ID, it now:
1. Parses the URL (supports GitHub, GitLab, Bitbucket — HTTPS and SSH)
2. Clones the repo (or reuses if already present with matching remote)
3. Auto-generates agent-orchestrator.yaml with sensible defaults:
   - SCM/tracker plugins inferred from URL host
   - Default branch detected from local refs
   - Project type detected (language, package manager)
   - Post-create install commands set based on package manager
4. Starts the orchestrator + dashboard as usual

New core module `config-generator.ts` handles URL parsing, SCM detection,
project info detection, and config generation. All logic is in core (not
CLI) for reusability. 36 unit tests cover URL parsing, SCM detection,
default branch detection, project info, config generation, and clone
target resolution.

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

* fix: address bugbot review feedback

1. Non-JS postCreate fix: Only set postCreate install commands for JS
   package managers (pnpm/yarn/bun/npm). Rust/Go/Python projects no
   longer get an incorrect "npm install" command.

2. Deduplicate startup logic: Extract shared `runStartup()` helper used
   by both URL-mode and normal-mode start flows. Eliminates ~100 lines
   of duplicated dashboard+orchestrator startup code.

3. SSH URL matching: Normalize SSH URLs to HTTPS format in
   `isRepoAlreadyCloned()` so repos cloned via SSH
   (git@github.com:owner/repo) are correctly detected as matching.

4. SCM/tracker always explicit: Always set scm and tracker fields in
   generated config so `applyProjectDefaults()` doesn't silently
   override with github defaults for non-GitHub repos.

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

* fix: sanitize repo names for project IDs and session prefixes

Address remaining bugbot comments:
- Add sanitizeProjectId() to handle dots/special chars in repo names
  (e.g. "my.app" → "my-app" instead of failing Zod validation)
- Preserve original case for generateSessionPrefix() so CamelCase
  detection works (e.g. "DevOS" → prefix "dos", not "dev")
- Strip invalid chars before prefix generation to prevent dots
  leaking into sessionPrefix

Also update README and SETUP docs with `ao start <url>` quick
onboarding flow.

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

* feat: auto-open browser to orchestrator page on `ao start`

Opens the orchestrator session page (/sessions/{id}) instead of the
root dashboard, since the Kanban board is empty on first startup
with no worker sessions yet.

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

* fix: replace fixed 3s delay with port polling for browser open

Poll the dashboard port via TCP connection attempts (300ms intervals,
30s timeout) instead of a hardcoded setTimeout. Opens the browser only
once the server is actually accepting connections — deterministic
regardless of how long Next.js takes to compile.

Applied to both `ao start` and `ao dashboard` commands.

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

* test: add CLI tests for start/stop commands

12 new tests covering:
- Project resolution: single project, explicit arg, missing project,
  multi-project ambiguity, empty config
- URL argument: reuse existing clone, git clone flow, existing config
  detection, clone failure error handling
- Port polling: waitForPortAndOpen polls isPortAvailable before opening
  browser (proves deterministic behavior vs fixed delay)
- Stop command: session kill + dashboard stop, graceful missing session

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

* refactor: deduplicate waitForPortAndOpen into shared utility

Move waitForPortAndOpen to lib/web-dir.ts (alongside isPortAvailable)
so both start.ts and dashboard.ts share a single cancellation-aware
implementation. start.ts now uses AbortSignal like dashboard.ts —
polling stops immediately if the dashboard process exits early.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:52:49 +05:30
suraj_markup 7aa883c217
Merge pull request #254 from suraj-markup/feat/try-pr-script
feat(scripts): add try-pr.sh for testing PR worktrees locally
2026-03-03 01:18:06 +05:30
suraj_markup 01a6c56e1b
Merge pull request #246 from suraj-markup/feat/issue-245
fix(setup): improve setup.sh prereq validation and add start/spawn pre-flight checks
2026-03-02 03:24:14 +05:30
suraj-markup 6603685a2f feat(scripts): add try-pr.sh for testing PR worktrees locally
Adds scripts/try-pr.sh — a helper to switch the global 'ao' command to
any session's worktree for manual testing, then restore back to main.

Usage:
  bash scripts/try-pr.sh <session-id>            # CLI/core/plugins only
  bash scripts/try-pr.sh <session-id> --with-web # also builds + starts dashboard
  bash scripts/try-pr.sh --restore               # switch back to main

Also fixes isPortAvailable() to use a connect-based probe instead of
bind-based. The old approach had false positives on macOS when Next.js
listens on :: (IPv6 wildcard) — binding 127.0.0.1 succeeded even though
the port was taken. A TCP connect correctly detects any listener
regardless of which address it's bound to.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 03:19:49 +05:30
suraj-markup 18dea011e7 fix: address review feedback and add preflight tests
- Consistent error formatting: single spawn now uses err.message like
  batch-spawn, avoiding redundant "Error:" prefix
- checkBuilt distinguishes missing node_modules ("pnpm install") from
  missing dist ("pnpm build") so users get the right guidance
- Add 13 preflight unit tests covering port, build, tmux, and gh checks
- Add 5 spawn integration tests covering runtime-aware tmux check,
  tracker-aware gh check, and error message formatting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 03:05:00 +05:30
suraj-markup 62cd42b30f fix: resolve checkBuilt from web node_modules and remove unused imports
Check ao-core/dist/index.js relative to webDir's node_modules instead
of using require.resolve which fails under npm link. Also removes
unused existsSync/resolve/createRequire imports that caused lint errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 02:50:56 +05:30
suraj-markup b43b1556c0 fix: check core dist output instead of .next/BUILD_ID in checkBuilt
.next/BUILD_ID only exists after next build (production) but the
dashboard runs with next dev. Check @composio/ao-core resolve instead,
which is the actual cause of module resolution errors when packages
are not compiled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 02:44:19 +05:30
suraj-markup cb071e122d fix: wrap batch-spawn preflight in try-catch
Preflight errors in batch-spawn were unhandled, causing unformatted
rejection output. Now caught with chalk.red formatting and
process.exit(1), matching the single spawn handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 02:40:12 +05:30
suraj-markup 96c7ae534a fix: run spawn preflight checks once before batch loop
Extract preflight checks from spawnSession into runSpawnPreflight and
call it once upfront in both registerSpawn and registerBatchSpawn. A
missing prerequisite now fails fast with one clear error instead of
repeating N times across the batch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 02:01:15 +05:30
suraj-markup f14465335d fix: address review feedback on preflight checks
- Consistent error handling: all preflight functions now throw instead
  of mixing process.exit and throw, so callers can catch and clean up
- checkGhAuth distinguishes "not installed" (ENOENT) from "not
  authenticated" by checking gh --version first
- Move checkBuilt() after package.json existence check in start.ts so
  missing web package shows "Run: pnpm install" not "Run: pnpm build"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 01:45:39 +05:30
suraj-markup 42442e007e fix(cli): correct misleading comment on isPortAvailable IPv6 coverage
The comment claimed connect-based detection works for IPv6 listeners,
but the probe only connects to 127.0.0.1 (IPv4). Updated the comment
to accurately document this limitation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:53:11 +05:30
suraj-markup dfcc0841ec fix(cli): remove unused createServer import and fix flaky port test
- Remove unused `createServer` import from web-dir.ts (only `Socket`
  is used) — fixes the lint error in CI
- Mock `isPortAvailable` in the "uses port 3000" test so it doesn't
  depend on port 3000 actually being free on the host machine

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:41:08 +05:30