runtime-process:
- Reserve the per-instance processes-map slot before the platform split
so the Windows ConPTY branch participates in duplicate-create detection
and getMetrics/getAttachInfo bookkeeping. Previously, Windows returned
a handle without storing it, so duplicate session IDs were silently
accepted and getMetrics always reported 0 uptime.
- Add 500ms graceful-exit poll before SIGKILLing the pty-host on destroy
so node-pty can dispose its ConPTY handle. Skipping this orphaned the
conpty_console_list_agent helper and triggered Windows Error Reporting
dialogs (0x800700e8) on real runs, not just tests.
- pty-host: install SIGTERM/SIGINT/SIGHUP/SIGBREAK/beforeExit handlers
that drive the same shutdown sequence (kill pty, drop clients, close
pipe, exit after 50ms grace), and route MSG_KILL_REQ through the same
path. Previously MSG_KILL_REQ only called pty.kill() and left the host
process lingering.
- Add windowsHide:true to the pty-host child spawn so node-pty's helper
console window stays hidden on errors.
workspace-worktree: normalize paths to a comparable POSIX form
(backslash→slash, lowercase drive letter) when matching git worktree
list --porcelain output against project directories. git emits
forward-slash paths on Windows; path.join produces native backslashes
— the comparison failed and list() returned empty.
agent-opencode: guard tmux/ps usage with isWindows() in isProcessRunning
so process-runtime sessions on Windows take the PID-signal path instead
of attempting Unix-only commands.
cli/start: detect Windows local paths in isLocalPath (drive letter
prefix, UNC path, .\, ..\) so spawn arguments like C:\... aren't
mistaken for project names.
integration test: replace cat + /tmp with platform-native echo (findstr
"x*" on Windows, cat on Unix) and os.tmpdir(); the original used
Unix-only tooling and would never run on Windows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
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.
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>
- 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>
- 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>
Resolved conflict in orchestrator-prompt.ts — kept "runtime session"
wording (Windows-aware) and took main's additions for prompt-driven
session naming and freeform --prompt spawning docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- session.ts/status.ts: use session.lastActivityAt on Windows (no tmux)
- session-manager.ts: stabilize ConPTY output before sending post-launch prompt
to prevent prompts being swallowed during agent startup splash screen
- pty-client.ts: split message + Enter into two writes (300ms gap) to match
tmux send-keys behavior; fix isAlive to return true while pipe is connectable
regardless of whether the agent process inside has exited
- pty-host.ts: keep named pipe server alive after agent exits (mirrors tmux
session persistence) so clients can still attach and view scrollback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
* 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>
- Fix CI diff coverage and review feedback
- Fix test reliability for session attach, stop, and Windows attach
- Add coverage for session attach binary protocol and edge cases
- Clean up stdin listener + add resolvePipePath tests
- Address review comments + coverage for pipe relay
- Make 80 failing tests pass on Windows (cross-platform mocks, path assertions)
- Fix production code: execFile shell option for .cmd, isPathInside separator,
openclaw binary detection via `where` on Windows
- Add platform-aware test assertions for shell escaping, PATH handling, hooks
- Add signal forwarding comment for Windows dashboard process
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- session.ts: restore isOrchestratorSession from core (checks both name
and metadata role) instead of name-only isOrchestratorSessionName
- session.ts: remove unused allSessionPrefixes after merge conflict
- start.test.ts: update stop command tests for sm.list() flow
- toClaudeProjectPath: remove speculative space replacement, keep only
verified chars (/ : .) — addresses review comment about Unix breakage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- cli/session: add Windows ao session attach via named pipe relay with
raw stdin mode and Ctrl+\ to detach; skip getTmuxActivity on Windows
- cli/start: fix ao stop looking for "tr-orchestrator" instead of the
actual numbered session (e.g. tr-orchestrator-5) — also fixes Linux
- agent-claude-code: fix toClaudeProjectPath dropping Windows drive colon
(C:\→C- not C) breaking JSONL lookup; ignore stale JSONL entries from
previous sessions in reused worktrees
- pty-client: use \r (carriage return) instead of \n for PTY Enter key
Addresses blockers W13 (partial), W14.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows equivalent of the tmux daemon. Per-session detached pty-host.js
process owns a ConPTY (via node-pty), listens on a named pipe
(\.\pipe\ao-pty-{hash}-{sessionId}), and relays terminal I/O to any
connected client.
- runtime-process: spawn pty-host on Windows, route sendMessage/getOutput/
isAlive/destroy through named pipe protocol
- mux-websocket: named pipe relay for dashboard terminal (skip TerminalManager
on Windows), resolvePipePath via generateConfigHash (instant, no pipe scan)
- direct-terminal-ws: use real mux server on Windows instead of placeholder
- tmux-utils: findTmux returns null on Windows, resolvePipePath added
- orchestrator-prompt: runtime-agnostic language
- opencode: fix isProcessRunning tmux-before-guard bug (W29)
Addresses blockers W01-W12, W23, W24, W28, W29, W33-W35.
Unix behavior completely unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
- 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>
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>
Our Windows-specific files were written before the @composio → @aoagents
rename landed. Update all affected imports across runtime-process,
workspace-clone, workspace-worktree, cli commands, and test files.
T04: resolveNextBin() skips POSIX .bin/next shim on Windows; invokes
next/dist/bin/next via process.execPath instead (ENOENT fix).
T18: shellEscape() branches on isWindows() — PowerShell uses '' doubling,
Unix uses POSIX '\'' escaping. All 4 agent plugins benefit automatically.
T06 secondary: tmux attach hint in `ao spawn` output gated behind
runtimeName === "tmux" (process runtime has no tmux session).
T06/T11: runtime-process destroy() and isAlive() fall back to
handle.data.pid when the in-memory processes Map is empty — fixes
cross-process CLI calls (ao session ls / ao session kill).
Mirrors the fixes already applied to agent-claude-code:
I-1: Guard ps -eo pid,tty,args behind isWindows() in isProcessRunning for
all three agents. ps is Unix-only; a stale tmux handle on Windows
would silently return false via exception catch. The explicit guard
makes intent clear and avoids the unnecessary execFile call.
I-2: Inline systemPromptFile content on Windows instead of $(cat ...)
bash command substitution (aider: --system-prompt; opencode: prompt
value). codex is unaffected — it passes the path via -c flag and
reads the file itself.
Tests: added isWindows mock (default false) to all three test suites to
prevent real isWindows()=true on Windows from triggering the new guard
in existing Unix-path tests. Added Windows-specific tests for each fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>