Commit Graph

168 Commits

Author SHA1 Message Date
Harsh Batheja 9fcc1d9ed5 fix(plugin-kimicode): plumb workspacePath into AgentLaunchConfig
The kimicode plugin's --work-dir was passing projectConfig.path, which
breaks worktree-mode workspaces. In worktree mode, projectConfig.path is
the original repo root while session.workspacePath is the per-session
checkout — they differ. Either kimi would write to the project root
(breaking worktree isolation) or md5(projectConfig.path) would diverge
from md5(session.workspacePath), so getActivityState/getSessionInfo would
never find this session's bucket.

Fix:
- Add optional `workspacePath` field to AgentLaunchConfig.
- Plumb it through all 3 launch call sites in session-manager.ts.
- kimicode getLaunchCommand uses config.workspacePath, falling back to
  config.projectConfig.path when undefined.
- Tests for the divergent-paths case.

Public-interface change: AgentLaunchConfig grows one optional field.

Invariants preserved:
- Agent.getLaunchCommand signature unchanged — still takes one
  AgentLaunchConfig.
- Existing plugins (claude-code, aider, codex, opencode) compile and run
  unchanged; the new field is optional and they ignore it.
- Clone-mode workspaces (where workspacePath === projectConfig.path)
  produce the same launch command as before.
- Fallback to projectConfig.path keeps callers that don't pass the new
  field working — no flag day required.
2026-04-29 03:53:32 +05:30
Harsh Batheja bc6f1cf70d fix(plugin-kimicode): use kimi.json for workspace mapping and add --work-dir
Read ~/.kimi/kimi.json work_dirs[] as the authoritative workspace-to-session
mapping. When last_session_id is populated, prefer it over the directory-mtime
recency heuristic — kimi itself wrote it. Falls back gracefully to the existing
MD5 hash scan when kimi.json is absent or last_session_id is null.

Add --work-dir to getLaunchCommand using projectConfig.path to establish an
explicit cwd contract, preventing shell-rc / tmux-hook drift from causing the
MD5(cwd) hash to diverge from kimi's session bucket.
2026-04-26 20:16:11 +05:30
Harsh Batheja 9795e8b8a9 fix(plugin-kimicode): address inline review from illegalcall
Addresses all 10 inline comments on PR #1390.

Load-bearing fixes:

[#6 line 327] detectActivity ordering was wrong
  The old code checked the idle prompt (`^kimi>\s*$`) before approval/error
  patterns. Real kimi UI re-renders `kimi>` on the last line when asking for
  a confirmation, so \`(Y)es/(N)o\\nkimi>\` was misclassified as idle and the
  session would sit forever looking quiet while actually blocked on input.
  Reordered to: waiting_input → blocked → idle → active. Matches codex/aider.

[#2,#4,#8 lines 128,154,493] No stable AO↔Kimi session binding
  Discovery was pure (path-hash + recency). If the user ran kimi manually in
  the same repo, or two AO sessions shared a workspace hash, AO would attach
  to the wrong UUID — summary / activity / --resume target all corrupted.
  Now:
   - \`session.metadata.kimiSessionId\` pins a specific UUID when set; no
     fallback to recency when the pin misses (fails closed, no silent drift).
   - Unpinned lookups filter UUIDs by \`liveMtime >= session.createdAt - 60s\`
     so stray dirs from prior AO sessions don't attach.
   - findKimiSessionMatch now takes the whole Session (not just workspacePath)
     so createdAt + metadata are available.

[#3 line 141] Any recent subdir was treated as a real session
  Stray temp dirs and crash leftovers would match on mtime, producing
  \`kimi --resume <garbage>\` and bogus active states. Now require
  context.jsonl OR wire.jsonl to exist before trusting a dir. The race
  fallback (empty UUID dir → dir mtime) is removed — the JSONL activity
  fallback in getActivityState covers the startup window instead.

[#5 line 191] Symlink follow outside ~/.kimi/sessions/
  \`stat()\` / \`createReadStream()\` followed symlinks without rebinding, so
  a bucket entry that's a symlink to \`/dev/zero\` or \`/etc/passwd\` would
  hang forever or leak data. Added \`isInsideKimiSessions(path)\` that realpaths
  the candidate and rejects anything outside the sessions root. Every
  bucket entry is checked before use.

Smaller cleanups:

[#1 line 89] Cache: 30s negative TTL + unbounded growth
  Negative results now cached 2s so a session appearing mid-poll is picked
  up on the next cycle. Expired entries evicted on read. Cache capped at
  256 entries with oldest-expiry pruning. Key changed to (workspacePath,
  pinnedUuid) so two AO sessions in the same bucket can't poison each
  other's cache entry.

[#7 line 440] Duplicate argv0Re regex — use the const.

[#9 line 532] maxBuffer: 4096 → 65536. Future \`kimi info\` releases that add
  plugin listings or telemetry banners won't silently break detect() with
  swallowed ENOBUFS.

[#10 test line 650] macOS test breakage: /var/folders is a symlink to
  /private/var/folders, so fakeHome under tmpdir() is a symlink path, while
  the plugin realpaths before hashing. Wrap the mkdtempSync in realpathSync
  so tests agree with the plugin on the canonical path. Linux CI masked this.

Tests: 80 → 86. New coverage:
  - detectActivity classifies confirmation-then-prompt-rerender as waiting_input
  - detectActivity classifies error-then-prompt-rerender as blocked
  - createdAt floor filter (ignores UUIDs from before the AO session)
  - Pinned kimiSessionId wins over recency
  - Pinned UUID missing returns null (no silent fallback)
  - Negative cache TTL ~2s (session appearing mid-poll picked up next cycle)
  - Empty UUID dir without live files is rejected (no stray-dir attach)

Verified end-to-end against real kimi-cli 1.38.0: detect() true,
getSessionInfo extracts correct summary + UUID, getRestoreCommand matches
kimi's own resume hint.
2026-04-23 03:26:43 +05:30
Harsh Batheja 85821646fa fix(plugin-kimicode): address follow-up review issues
Follow-up to the issues filed as a review comment on the PR.

[MED] detect() too loose (\bkimi\b matches unrelated binaries)
  The old regex accepted plain "kimi" alone because the (?:cli|code)?
  suffix was optional — any binary whose output contains "kimi" passed.
  Real kimi-cli's --version prints just "kimi, version X.Y.Z" (no suffix),
  so --version alone can't distinguish it from, say, a hypothetical
  keyboard-input-manager named kimi. Switch to `kimi info` exclusively;
  real kimi-cli prints "kimi-cli version: ..." which is a distinct vendor
  string. Regex now requires "kimi-cli" / "kimi-code" / "moonshot"
  literally. Added maxBuffer cap (4 KB) so a hostile binary can't flood
  detect() with MB-scale output.

[MED] --work-dir not passed — investigated, not actionable in this PR
  AgentLaunchConfig doesn't expose session.workspacePath — only
  projectConfig.path (the project root), which would actively break
  discovery if passed. Runtime cwd handling is load-bearing. Left a
  comment explaining the constraint and pointing at the core-types
  change needed to fix it properly.

[LOW] Empty-bucket race returned transient null
  During session creation kimi mkdirs the UUID directory before writing
  context.jsonl / wire.jsonl. getKimiLiveSignalMtime returned null in
  that window and findKimiSessionMatch returned null, flickering the
  dashboard to "no signal". Fall back to the UUID directory's own mtime
  when live files are absent.

[LOW] isProcessRunning matched "kimi" anywhere in ps args
  Old regex /(?:^|\/)\.?kimi(?:\s|$)|(?:\s|^)kimi(?:\s|$)/ matched
  `cat kimi.log`, `vim ~/.kimi/config.toml`, etc. Anchor to argv[0]
  instead — only the executable itself, or a python/uv/node runner
  followed by `kimi` as the first positional argument, counts.

[NIT] Symlink normalization
  kimi's process reads cwd via os.getcwd(), which returns the realpath on
  Linux. If AO hands us a symlinked workspacePath, our MD5(symlink) won't
  match kimi's MD5(realpath). realpath-resolve with a best-effort fallback
  to the raw string (preserves behavior when the path doesn't exist yet).

Tests: 75 → 80. New coverage:
- detect() vendor-string matrix: kimi-cli / kimi-code / moonshot accepted,
  unrelated "kimi keyboard input manager" rejected
- isProcessRunning rejects `cat kimi.log` / `vim ~/.kimi/config.toml`
- isProcessRunning accepts `python -m kimi`
- Native signal falls back to UUID-dir mtime during the empty-bucket race
- Symlinked workspace path matches the realpath-hashed bucket

Verified end-to-end against real kimi-cli 1.38.0:
- detect() → true (via `kimi info` vendor match)
- getSessionInfo → correct summary + UUID
- getRestoreCommand → matches kimi's own resume hint
2026-04-22 22:56:47 +05:30
Harsh Batheja bc8b50d2db fix(plugin-kimicode): correct session layout discovered via smoke test
Installing kimi-cli 1.38.0 locally (\`uv tool install kimi-cli\`) and running
it once revealed the plugin's session-discovery logic was built on wrong
assumptions about the on-disk layout.

Observed layout (kimi-cli 1.38.0):

  ~/.kimi/sessions/<md5(cwd)>/<session-uuid>/
    context.jsonl  — conversation history
    wire.jsonl     — turn events (TurnBegin/TurnEnd with user_input payload)

Differences from my original assumptions:

- Sessions are nested under \`sessions/\` (not direct subdirectories of
  \`~/.kimi/\`).
- The workspace is identified by an MD5 hash of the absolute path, not by
  a \`cwd\` field stored in a state file.
- There is no \`state.json\`. No \`title\`, \`model\`, or \`cost\` is persisted.
- The session ID is the UUID directory name and is accepted as-is by
  \`kimi --resume <uuid>\`.
- The old \`--continue\` fallback is unnecessary — if we found the directory,
  we always know its UUID.

Fixes:

- \`findKimiSessionMatch\` now computes \`md5(workspacePath)\` with node:crypto
  and lists \`~/.kimi/sessions/<hash>/\` directly. No more full-tree scan of
  \`~/.kimi/\`, no more \`readFile\` of a fictional \`state.json\`.
- \`getKimiLiveSignalMtime\` keeps the parallel \`Promise.all\` stat of
  context.jsonl + wire.jsonl (the only files that exist).
- \`getSessionInfo\` streams the first \`TurnBegin\` out of wire.jsonl as a
  best-effort summary, with a 1 MB byte ceiling. agentSessionId is the UUID.
- \`getRestoreCommand\` drops the \`--continue\` fallback branch — a found dir
  always has a usable UUID.

Verified end-to-end against the real kimi-cli 1.38 binary on this machine:
- \`detect()\` → true
- \`getLaunchCommand\` output parses cleanly when run with \`--help\`
- \`getSessionInfo\` extracts the actual first user prompt ("say hello")
- \`getRestoreCommand\` produces the same UUID kimi itself prints as the
  resume hint: \`kimi -r 6ec34626-aedf-4659-a061-c5fbfa4cf166\`

Tests remain at 75 green. Coverage is now against real on-disk layouts
using temp directories with MD5-hashed bucket names — no mock-structure
drift from reality.
2026-04-22 22:17:08 +05:30
Harsh Batheja 2093126a48 refactor(plugin-kimicode): clean up after second-round review
All changes are non-behavioral perf/style cleanups flagged during my second
review pass — no user-visible changes.

- Consolidate double JSON.parse in findKimiSessionMatchUncached: the previous
  pass parsed each candidate state.json once to extract cwd and a second time
  to extract session_id/model/title. Replaced both helpers with a single
  `parseKimiState(raw)` that returns all four fields in one traversal.
- Carry state.json's mtime through KimiSessionMatch so getKimiLiveSignalMtime
  (renamed from getKimiSessionMtime) doesn't re-stat state.json — the winner's
  mtime was already captured during the scan. Live-signal probe is now limited
  to context.jsonl + wire.jsonl (the per-turn files) and runs them in parallel
  via Promise.all instead of sequential awaits.
- Fold state.json mtime and the live-signal mtime into a single "freshest"
  timestamp in getActivityState so a recently-written context.jsonl wins even
  when state.json is stale.
- Tighten appendApprovalFlags signature: `string | undefined` → proper
  `AgentPermissionInput | undefined` so typos at call sites fail at compile
  time.
- Stricter detect(): don't trust every binary named `kimi` — verify the
  --version output mentions kimi/kimi-cli/kimi-code, and fall back to
  `kimi info` for builds that print a bare version number. Rejects unrelated
  tools that happen to install a `kimi` binary.

Tests: 71 → 75. New coverage:
- detect() accepts kimi-cli vendor strings
- detect() falls back to `kimi info` when --version is ambiguous
- detect() rejects an unrelated `kimi` binary
- Native signal picks the fresher of state.json vs context.jsonl mtimes
2026-04-22 21:52:26 +05:30
Harsh Batheja a76e5019c2 fix(plugin-kimicode): address review feedback
Critical (from @harshitsinghbhandari, verified against kimi-cli source):
- Remove `promptDelivery: "post-launch"` — `-p`/`--prompt` is just a prompt
  string alias (also `--command`/`-c`), NOT a mode switch. The non-interactive
  flag is `--print`, which we never set. Inline delivery via `--prompt` is
  reliable and avoids the post-launch sendMessage() delay.
- Drop unchecked `as string` casts in getRestoreCommand in favor of typeof
  guards + `?? undefined` so null model values don't silently leak.

Medium (performance):
- Add 30s per-workspace cache to findKimiSessionMatch (mirrors codex's
  SESSION_FILE_CACHE_TTL_MS) so the ~/.kimi/ scan doesn't run 12×/min per
  active session. Cache keyed by workspacePath; cleared via the new
  `_resetSessionMatchCache` test-only export between test cases.

Minor (correctness):
- Collapse findKimiSessionDir + readKimiSessionState into one
  findKimiSessionMatch that returns {dir, state} from a single state.json
  read. Previously the file was parsed twice per getSessionInfo /
  getRestoreCommand call.
- Wire config.subagent → `kimi --agent <name>` (default / okabe / custom).
- Tighten detectActivity patterns so "I approve of this approach" and
  "Earlier I failed to connect" no longer falsely trigger waiting_input /
  blocked. Regexes are now line-anchored with `^`/`$` + `\b` word boundaries.

Tests: 58 → 71 (all green). New cases cover:
- Native-signal ready/idle decay (previously only active was tested)
- Cascade ordering: JSONL waiting_input wins over a matching native signal
- Malformed state.json in both getSessionInfo and getRestoreCommand
- `work_dir` alias accepted in addition to `cwd`
- project.agentConfig.model preferred over state.json's recorded model
- False-positive narration guards for both regex tightenings
2026-04-21 15:08:46 +05:30
Harsh Batheja 97f914f73c feat(plugin): add kimicode agent plugin
Add @aoagents/ao-plugin-agent-kimicode implementing the Agent interface
for MoonshotAI's Kimi Code CLI. Follows the AO activity JSONL + PATH
wrapper pattern established by agent-aider/opencode, with a native-ish
signal sourced from ~/.kimi/<session>/ mtimes when present.

- Full Agent interface: getLaunchCommand (--yolo, --model, --agent-file),
  getEnvironment (AO_SESSION_ID + ~/.ao/bin PATH + GH_PATH), detectActivity,
  getActivityState (5-step cascade with mandatory JSONL entry fallback),
  isProcessRunning (tmux TTY + PID signal-0, matches `.kimi`/`uv run kimi`),
  getSessionInfo (state.json parsing), getRestoreCommand (--resume <id>
  with --continue fallback), setupWorkspaceHooks, postLaunchSetup,
  recordActivity, detect().
- Post-launch prompt delivery — kimi's `-p` implicitly enables --print and
  exits, which would break interactive supervised sessions.
- 58 unit tests covering all 7 mandatory getActivityState cases plus
  manifest, launch, env, prompt classification, process detection,
  session info extraction, restore command, and detect().
- Register in cli/src/lib/plugins.ts, detect-agent.ts, plugin-registry.json,
  cli package deps, and update user-facing docs / yaml examples.

Closes #1384
2026-04-21 04:19:31 +05:30
harshitsinghbhandari 350c5c08e8 fix: restore permissionless codex workers with bypass 2026-04-19 19:59:16 +05:30
harshitsinghbhandari bb5dcf0af7 fix: address #1306 review follow-ups 2026-04-19 19:59:16 +05:30
harshitsinghbhandari e1bb51f42a chore: replace changelog edits with changeset 2026-04-19 19:59:16 +05:30
harshitsinghbhandari 3ba526d282 fix: relax codex restore approval mode 2026-04-19 19:59:15 +05:30
harshitsinghbhandari 3449e44d84 test: align codex gh wrapper assertion with dynamic PR parsing
The shared gh wrapper now extracts PR URLs with a regex instead of embedding a literal github URL, so the old codex assertion was stale and broke CI on PR #1300.
2026-04-18 18:02:31 +05:30
harshitsinghbhandari b0d0994efd fix: improve agent plugin cost accounting and restore safety 2026-04-18 13:56:59 +05:30
harshitsinghbhandari db340fad88 fix: finish PR #1300 major follow-up fixes 2026-04-18 12:35:15 +05:30
harshitsinghbhandari e7ad928812 feat: report non-terminal PR workflow events (#131) 2026-04-17 21:46:57 +05:30
harshitsinghbhandari dd83a11503 fix: model missing activity evidence explicitly (#122)
Represent missing activity probes as first-class signal states so lifecycle inference only treats valid idle evidence as proof. This prevents false stuck transitions, keeps API/UI lifecycle truth aligned, and makes root monorepo verification deterministic by serializing recursive build and typecheck.
2026-04-17 19:28:56 +05:30
Harshit Singh Bhandari 42e9be13a5
Merge pull request #114 from harshitsinghbhandari/plan/stage1-lifecycle-foundation
Plan/stage1 lifecycle foundation
2026-04-16 22:09:41 +05:30
Dhruv Sharma 87fb598d87
Merge pull request #1126 from gautamtayal1/fix/spawn-ao-for-opencode
fix (opencode): use local AGENTS.md for OpenCode orchestrator instructions
2026-04-16 18:21:12 +05:30
Adil Shaikh ba77929c93
Merge pull request #1158 from ComposioHQ/feat/issue-1154
fix(cli): stop writing broken yaml when `ao start` runs in a new folder
2026-04-15 15:10:10 +05:30
harshitsinghbhandari d466118450 feat(core): add canonical lifecycle persistence foundation 2026-04-15 12:41:06 +05:30
Dhruv Sharma 9691e6e6c5
Merge pull request #1215 from illegalcall/fix/codex-session-tracking-1099
Fix Codex session activity parsing for payload JSONL
2026-04-15 01:50:22 +05:30
Dhruv Sharma 49954403ef docs(codex): clarify token-accounting precedence in session parser
Token sources in streamCodexSessionData are precedence-ordered via `continue`.
`total_token_usage` is a cumulative snapshot (overwrite) while the others are
per-turn deltas (accumulate) — document this so a future reader doesn't "fix"
the asymmetry and break cumulative totals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 01:45:16 +05:30
adil 2cf9128101 refactor: clean up redundant requireRepo calls and nullish coalescing
- Use ?? instead of || for ownerRepo fallback (semantically correct for
  null-to-undefined conversion)
- Extract requireRepo() result into a local variable in tracker-gitlab's
  updateIssue and issueUrl to avoid redundant validation calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:13:02 +05:30
adil 17ea240f68 fix(cli): stop writing broken yaml when `ao start` runs in a new folder
- Make `repo` field optional in ProjectConfig and Zod schema so projects
  without a detected GitHub remote can still load and run
- Remove placeholder `repo: "owner/repo"` from autoCreateConfig() and
  addProjectToConfig() — omit the field entirely when no remote is found
- Always use actual workingDir for `path` instead of unreliable `~/<projectId>`
  fallback for non-git directories
- Add null guards for `project.repo` across SCM plugins, tracker plugins,
  lifecycle manager, webhooks, and prompt builders to prevent crashes when
  repo is not configured

Closes #1154

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:13:01 +05:30
Dhruv Sharma ab1e4fb069
Merge pull request #1180 from yyovil/add/type-resolution
Add node type resolution for non-web packages
2026-04-13 19:56:36 +05:30
Dhruv Sharma 64b6a2ec72 fix(codex): address cursor bugbot comments on readJsonlPrefixLines
Use a single StringDecoder across reads so multi-byte UTF-8 sequences
that straddle the 8KB chunk boundary buffer correctly instead of
producing U+FFFD replacement characters that break JSON.parse.

Also fix the test mock: makeFakeFileHandle now advances an internal
cursor and returns bytesRead: 0 at EOF. The prior mock copied from
offset 0 every call, which would infinite-loop readJsonlPrefixLines
for any line larger than the 8192-byte chunk size.

Add a regression test using 3,000 CJK characters (9,000 bytes of
payload) to exercise the chunk boundary path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 19:23:29 +05:30
Dhruv Sharma df9f3c8b5d fix(codex): classify activity by payload.type for wrapped event_msg
Real Codex sessions emit records like
`{"type":"event_msg","payload":{"type":"error",...}}` and
`{"type":"event_msg","payload":{"type":"approval_request",...}}`.
readLastJsonlEntry only exposed the top-level `type`, so the codex
plugin's activity switch matched `event_msg` and decayed to ready/idle,
never surfacing `blocked` or `waiting_input`. The approval_request/error
branches were dead code for payload-wrapped sessions, which is the exact
format this PR series is migrating to.

- readLastJsonlEntry now returns payloadType alongside lastType.
- Codex getActivityState prefers payloadType when present and classifies
  task_started/agent_reasoning as active, task_complete as ready, and
  the approval/error variants as waiting_input/blocked.
- New tests cover the payload-wrapped approval_request, exec_approval_request,
  error, task_started, and task_complete cases end-to-end.
- Core utils gains coverage for payloadType extraction and null fallbacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 18:53:50 +05:30
yyovil 9bed49d453 fix: scope node types to node packages 2026-04-13 18:25:21 +05:30
Dhruv Sharma 4ab844e47f Fix Codex session activity parsing 2026-04-13 16:45:57 +05:30
i-trytoohard 36a64c98b7
chore: bump all package versions to 0.2.5 (#1190)
* chore: release 0.2.5

Realign main with npm registry after off-branch publish of 0.2.3/0.2.4.
Bump all 21 linked packages to 0.2.5 and cherry-pick the startup-grace-period
fix for #989 (was in 5e4244a8 but never merged to main).

Also sync non-linked plugin versions (notifier-discord, notifier-openclaw,
scm-gitlab, tracker-gitlab) to their current npm versions.

* Revert "chore: release 0.2.5"

This reverts commit eb17f32834.

* chore: bump all package versions to 0.2.5, remove release workflow

- Bump all 25 packages to 0.2.5 to realign with npm registry
- Update package-version test to expect 0.2.5
- Remove stale .changeset/linear-spawn-branch-name.md
- Delete .github/workflows/release.yml (changesets-based NPM publish)

---------

Co-authored-by: Prateek <karnalprateek@gmail.com>
Co-authored-by: AO Bot <ao-bot@composio.dev>
2026-04-13 05:47:08 +05:30
Gautam Tayal 372c1be631 refactor(opencode): simplify AGENTS.md handling and remove unused code 2026-04-11 18:35:18 +05:30
Gautam Tayal 054e601167 refactor(opencode): replace OpenCode config with agents markdown file 2026-04-11 18:20:33 +05:30
Gautam Tayal fdf46653fc chore: add tests 2026-04-11 15:54:16 +05:30
Gautam Tayal f83f45eacb feat (opencode): add config file instead of injecting system prompt manually 2026-04-11 15:54:07 +05:30
Dhruv Sharma d908500791
Merge pull request #882 from DNX/feat/linear-spawn-branch-name
Honor Linear branchName as single source of truth for spawn worktree branches
2026-04-11 14:45:57 +05:30
harshitsinghbhandari 8f43006472 fix(agent-cursor): use printf %s pattern to prevent shell injection
Fix shell injection vulnerability when combining systemPromptFile with
prompt. The prompt could contain shell metacharacters ($(), backticks)
that would be executed inside the double-quoted string.

Now uses the exact same pattern as OpenCode:
  "$(cat 'file'; printf '\n\n'; printf %s 'prompt')"

The shellEscape wraps prompt in single quotes (no shell expansion),
and printf %s outputs it literally without interpretation.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 12:34:07 +05:30
harshitsinghbhandari 843ae2b897 fix(agent-cursor): use shell substitution for systemPromptFile
Use $(cat file) shell substitution instead of inlining file content
to avoid tmux truncation for large system prompt files (2000+ chars).
This matches the pattern used by Claude Code, Aider, and OpenCode.

- Replace readFileSync with $(cat) in getLaunchCommand
- Remove unused readFileSync import
- Update tests to verify shell substitution behavior

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 12:23:47 +05:30
harshitsinghbhandari e47b03807c fix(agent-cursor): address Cursor Bugbot security findings
1. Add symlink check for .cursor directory in extractCursorSummary
   to match getCursorSessionMtime behavior (prevents path traversal)

2. Add vitest alias for @aoagents/ao-plugin-agent-cursor in CLI tests
   (fixes missing module resolution in tests)

3. Add lstatSync check before readFileSync in getLaunchCommand
   to reject symlinked systemPromptFile paths (security hardening)

4. Add test coverage for symlink rejection behavior

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 12:16:37 +05:30
harshitsinghbhandari f154f87add fix: merge upstream main and resolve conflicts for cursor agent
- Resolve @composio → @aoagents package renaming conflicts
- Add cursor agent to BUILTIN_PLUGINS in plugin-registry.ts
- Add cursor agent to AGENT_PLUGINS in detect-agent.ts
- Add cursor agent import and registration in plugins.ts
- Add cursor agent dependency and import in web services.ts
- Update cursor plugin package naming to @aoagents/ao-plugin-agent-cursor
- Add cursor agent to changeset linked group
- Fix test imports to use new @aoagents package naming

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 11:48:27 +05:30
harshitsinghbhandari 63faae784c fix(agent-cursor): address 7 critical PR review findings
Fixes all issues identified in PR review from illegalcall:

1. 🔴 detect() false positives - Now checks for multiple Cursor-specific
   markers: "Cursor Agent" text OR (--approve-mcps AND --sandbox flags).
   Provides redundancy if Cursor changes one indicator.

2. 🔴 systemPromptFile/systemPrompt ignored - Properly reads file content
   synchronously using readFileSync and prepends to prompt. Clean approach
   without shell command substitution. Gracefully handles missing files.

3. 🟡 Process regex too generic - Fixed regex from /\\.?/ to /\.?/ for
   optional dot prefix. Now correctly matches "agent" or ".agent" binaries.

4. 🟡 Idle check before waiting_input - Reordered detectActivity checks so
   waiting_input patterns (permission prompts) are tested BEFORE idle prompt
   detection. Fixes false negatives when prompts end with input cursor.

5. 🟡 Symlink/path traversal protection - Added lstat() checks in
   extractCursorSummary and getCursorSessionMtime to reject symlinks and
   verify paths stay under workspacePath.

6. 🟡 hasRecentCommits false actives - Added comment acknowledging the
   limitation (same pattern as Aider plugin). Better than missing activity.

7. 🟡 Missing test coverage - Added 11 new tests:
   - 6 tests for detect() covering text match, flag fallback, edge cases
   - 5 tests for systemPromptFile/systemPrompt handling including errors
   Total: 62/62 tests passing

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 11:02:33 +05:30
harshitsinghbhandari d97177bced fix(cursor): address PR review comments from Copilot AI and Cursor bot
Activity Detection (2 related issues):
- Fix getCursorSessionMtime to stat .cursor/chat.md file instead of directory
- Directory mtime only updates on entry changes, not file modifications
- Now checks chat.md file first (tracks actual writes), falls back to directory
- Prevents directory mtime from blocking JSONL fallback in getActivityState
- Allows tier 4 (getActivityFallbackState) to run when needed

Prompt Safety:
- Add -- separator before positional prompt argument in getLaunchCommand
- Prevents prompts starting with - from being misinterpreted as flags
- Matches pattern used in Codex agent plugin
- Update test expectations to include -- separator

Process Detection:
- Update comment to accurately describe "agent" binary matching
- Removed misleading reference to "cursor and .cursor" process names

Plugin Detection:
- Improve detect() to check --version output for Cursor-specific text
- Reduces false positive risk from generic "agent" command name
- Validates output contains "cursor" or "agent" keywords

All tests passing (51/51).

Fixes issues identified in PR #637 review.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 22:17:13 +05:30
harshitsinghbhandari e210feb850 fix(agent-cursor): use --sandbox disabled instead of --trust to skip workspace prompts
The --trust flag only works in headless mode (with --print), so it doesn't
prevent the workspace trust prompt in interactive mode. Changed to --sandbox
disabled which skips workspace trust prompts entirely.

This fixes the issue where Cursor agent would block on startup waiting for
user to approve the workspace trust prompt.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 21:46:22 +05:30
Prateek 27712442f8 fix: restore GitHub repo URLs to ComposioHQ/agent-orchestrator — only npm scope changed 2026-04-09 16:00:32 +00:00
Prateek 2c862f3f29 fix: restore @composio/core references — third-party SDK, not our scope 2026-04-09 16:00:31 +00:00
Prateek 4f2616674f fix: address bugbot comments — missed renames in preflight, tests, and shell scripts 2026-04-09 16:00:31 +00:00
Prateek 967e864f5a chore: rename @composio scope to @aoagents across all packages
Renames all npm package scopes from @composio/* to @aoagents/* and
updates GitHub repo references from ComposioHQ/agent-orchestrator
to aoagents/ao throughout the codebase.

- All package.json names and dependencies
- README badges, links, and install instructions
- Documentation references
- Changeset config
- Source code imports and test files
2026-04-09 15:59:33 +00:00
harshitsinghbhandari f8e535f605 fix(cursor): correct binary name from 'cursor' to 'agent'
CRITICAL: The Cursor CLI binary is actually called 'agent', not 'cursor'.
This was a fundamental assumption error that made the plugin completely non-functional.

## Changes

### Binary and Process Names
- **manifest.description**: "Cursor CLI" → "Cursor Agent CLI"
- **processName**: "cursor" → "agent" (line 119)
- **detect()**: Check for "agent" binary instead of "cursor" (line 335)
- **isProcessRunning**: Look for "agent" process, not "cursor" (line 263)
  - Updated regex to `/(?:^|\/)\bagent\b(?:\s|$)/` with word boundaries
  - Prevents false matches on "agent-orchestrator" etc.

### Command Generation
- **Base command**: "cursor" → "agent" (line 122)
- **Permission flags**: Changed from assumed --auto-approve to actual Cursor flags:
  - --force (Run Everything / YOLO mode)
  - --trust (trust workspace without prompting)
  - --approve-mcps (auto-approve MCP servers)
- **Prompt handling**: Prompt is passed as positional argument, not --prompt flag
- **Removed**: --system flag support (Cursor agent doesn't have this)

### Activity Detection
- Updated prompt patterns: "cursor>" → "agent>", "[cursor]" → "[agent]"
- All terminal output detection patterns updated

### Test Updates (51/51 passing)
- processName expectation: "cursor" → "agent"
- Command assertions: "cursor" → "agent", --auto-approve → --force
- Combined options test: updated to reflect actual flag names
- Process detection tests: "cursor" → "agent", ".cursor" → "/path/to/agent"
- Activity detection tests: all "cursor" references → "agent"
- Manifest description test: updated to match new description

## Verification

**Before (broken):**
```bash
$ detectAvailableAgents()
Available agents: claude-code, aider, codex, opencode  #  cursor missing
```

**After (working):**
```bash
$ detectAvailableAgents()
Available agents: claude-code, aider, codex, cursor, opencode  #  cursor detected

$ agent.getLaunchCommand({permissions: 'permissionless', model: 'claude-sonnet-4', prompt: 'Fix bug'})
"agent --force --trust --approve-mcps --model 'claude-sonnet-4' 'Fix bug'"  #  correct

$ agent.processName
"agent"  #  correct
```

## Impact

This fix makes the plugin actually functional. The previous implementation would:
-  Never detect Cursor CLI (detect() checked wrong binary)
-  Never find running processes (isProcessRunning looked for wrong name)
-  Generate invalid commands (wrong binary name and flags)

Now it:
-  Detects Cursor CLI when `agent` binary is installed
-  Correctly identifies running agent processes
-  Generates valid commands with correct flags

## Testing
-  51/51 tests passing
-  TypeScript typecheck clean
-  Agent detection working (verified with actual agent binary)
-  Command generation produces valid agent commands

Addresses: ComposioHQ/agent-orchestrator#1076

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 21:11:07 +05:30
harshitsinghbhandari f3224e9193 fix(cursor): correct getRestoreCommand signature and update documentation
Address critical PR review findings from #1076.

## Changes

### Critical Fix
- **cursor/index.ts:308**: Add required parameters to getRestoreCommand signature
  - Was: `async getRestoreCommand(): Promise<string | null>`
  - Now: `async getRestoreCommand(_session: Session, _project: ProjectConfig): Promise<string | null>`
  - Fixes interface contract violation that would cause silent runtime failures
  - Parameters prefixed with _ since method always returns null (Cursor doesn't support resume)

### Documentation Improvements
- **cursor/index.ts:125-127**: Add comment explaining --auto-approve flag
  - Clarifies that --auto-approve is equivalent to Aider's --yes flag
  - Maps to same permission semantics across agents
- **cli/config-instruction.ts:24,144**: Add cursor to available agents list
  - Update defaults example to include cursor
  - Update "Available plugins" reference to include cursor

### Type Imports
- **cursor/index.ts:19**: Import ProjectConfig type for getRestoreCommand signature

## Testing
-  cursor plugin tests: 51/51 passing
-  cursor plugin typecheck: clean
-  No regressions introduced

## Why These Changes Are Needed

**Before:**
- getRestoreCommand signature mismatch would cause silent failures when core system calls it with parameters
- Config documentation didn't mention cursor as available agent option
- No explanation why --auto-approve differs from Aider's --yes naming

**After:**
- Interface contract properly satisfied
- Documentation complete and helpful
- Clear code comments explaining design decisions

Addresses: ComposioHQ/agent-orchestrator#1076

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 19:29:26 +05:30
harshitsinghbhandari 04f3a7c27f fix(cursor): register plugin in all discovery/resolution layers
Address PR review feedback from #1076 to make Cursor agent fully functional.

## Changes

### Plugin Registration (High Severity Fixes)
- **core/plugin-registry.ts**: Add cursor to BUILTIN_PLUGINS array for config-based resolution
- **cli/plugins.ts**: Import and register cursor in agentPlugins map for getAgent/getAgentByName
- **cli/detect-agent.ts**: Add cursor to AGENT_PLUGINS array for detectAvailableAgents()
- **web/services.ts**: Import and register cursor plugin for dashboard SessionManager/LifecycleManager
- **web/package.json**: Add @composio/ao-plugin-agent-cursor dependency

### Activity Detection Fix (Medium Severity)
- **cursor/index.ts**: Remove overly broad blocked detection patterns (error:/failed:)
  - Compiler errors, test failures, and linter output are normal tool output
  - Terminal-based detection can't distinguish between actionable agent errors and normal output
  - Follow Aider/OpenCode pattern: only Claude Code detects blocked (has native JSONL)
  - Added comment explaining why blocked detection is removed
- **cursor/index.test.ts**: Remove blocked detection test cases

## Why These Changes Are Needed

Before these fixes:
- `defaults.agent: cursor` in config would throw "Unknown agent plugin: cursor"
- `ao spawn --agent cursor` would fail
- Dashboard couldn't resolve cursor agent (SessionManager/LifecycleManager failures)
- `detectAvailableAgents()` never discovered Cursor
- False "blocked" states when agent encountered normal compiler errors

After these fixes:
- Cursor agent fully discoverable and usable via config and CLI
- Dashboard can spawn and manage Cursor sessions
- Activity detection matches other terminal-based agents (Aider, OpenCode)
- No false positives from normal tool output

## Testing
-  cursor plugin tests: 51/51 passing (reduced from 52 due to removed blocked tests)
-  core typecheck: clean
-  web typecheck: clean
-  CLI can import cursor plugin without errors

Addresses: ComposioHQ/agent-orchestrator#1076

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 19:17:41 +05:30