Commit Graph

5183 Commits

Author SHA1 Message Date
YeonGyu-Kim f8a1a11bb7 fix(team-mode): refactor layout to use testable spawn-process helper
The existing layout.test.ts relied on mock.module("bun", ...) registered
at the top level, but test-setup.ts calls mock.restore() + restoreModuleMocks()
in afterEach, so every test except the first one lost its mocks. CI has
been red on this file since e303feef.

Two changes:

1. layout.ts now imports spawn from the existing spawn-process helper
   instead of "bun" directly, matching the pattern established for
   closeTmuxPane and killTmuxSessionIfExists. This does not change
   runtime behavior - spawn-process just re-exports Bun's spawn.

2. layout.test.ts registers module mocks inside beforeEach and uses the
   ?test=UUID cache-busting dynamic-import pattern so the mocks apply
   on every test run, not just the first.

All 4 layout.test.ts cases now pass.
2026-04-18 19:42:19 +09:00
YeonGyu-Kim ea4f3c81f4 fix(tmux): treat pane-already-closed as success in closeTmuxPane
After send-keys C-c the subprocess running inside the pane (for example
"opencode attach") exits on SIGINT, which causes tmux to destroy the
pane automatically. The subsequent kill-pane then returns exit 1 with
stderr "can't find pane: %NN" even though the end state is exactly
what we wanted.

Before this fix closeTmuxPane reported failure for that branch, which
kept TmuxSessionManager's retryPendingCloses loop marking the (now
deleted) pane as still-pending forever and left stale entries behind
in the tracked sessions map. This is the behavior the user observed
as "screen opens, streaming runs, but cleanup doesn't finish" when
running with tmux.isolation="session".

Now we detect the "can't find pane" stderr and return true, treating
the auto-destroy path the same as an explicit successful kill.
2026-04-18 19:34:54 +09:00
YeonGyu-Kim b9d2acdcf9 fix(background-agent): run manager cleanup on uncaughtException and unhandledRejection
Signal handlers covered SIGINT/SIGTERM/SIGBREAK/beforeExit/exit, but a
synchronous throw or a top-level rejected promise terminated the process
without letting TmuxSessionManager (or any other registered manager) run
its shutdown hook. That reliably left orphan tmux panes after an opencode
crash.

Added registration for uncaughtException and unhandledRejection that fan
out through the existing cleanupAll() path, set process.exitCode = 1,
and arm the same 6 second forced-exit guard we use for signals. Test
helpers hold process-level spies so the new tests do not leak listeners
between runs.
2026-04-18 19:32:04 +09:00
YeonGyu-Kim 21554be870 fix(tmux-subagent): tighten serve/attach cleanup paths so panes and sessions are torn down reliably
Three defects observed with tmux.isolation="session" where the omo-agents
session was left with orphan fish panes after subagents finished:

1. cleanup() never ran 'tmux kill-session -t omo-agents'. If any pane
   lingered (for example because opencode attach stayed blocked on SSE),
   the isolated session survived process shutdown. Now we explicitly kill
   the shared session through killTmuxSessionIfExists when isolation is
   "session".

2. session.error events bypassed tmux cleanup entirely. Only session.deleted
   closed panes, so any provider error that did not escalate into a delete
   left the pane behind. Added onSessionError on TmuxSessionManager, wired
   from plugin/event.ts, which funnels through the same onSessionDeleted
   close path for tracked sessions only.

3. retryPendingCloses() only ran when a new session was created. If the
   main process went idle after a failed close, the pending session stayed
   pending forever. TmuxPollingManager now accepts the retry callback and
   fires it on every tick, alongside the existing stability-based close
   sweep.

Manager tests cover isolation=session kill invocation, inline/window
isolation skipping the kill, the onSessionError happy + untracked paths,
and an isolated-session kill failure that must not break cleanup.
2026-04-18 19:31:45 +09:00
YeonGyu-Kim de8a0167e6 feat(tmux): add killTmuxSessionIfExists utility for explicit session teardown
Adds killTmuxSessionIfExists(sessionName), a best-effort no-op when the
named session is absent. Drains both stdio streams so it does not leak
pipe buffers the way closeTmuxPane historically did.

Also exports ISOLATED_SESSION_NAME ("omo-agents") from session-spawn so
callers can tear down the shared isolated session without hard-coding
the name in multiple places.
2026-04-18 19:31:22 +09:00
YeonGyu-Kim 2a99a524ea fix(tmux): drain kill-pane stdout to prevent pipe backpressure hang
closeTmuxPane spawned kill-pane with stdout: "pipe" but never drained the
stream, which could leave the subprocess hanging indefinitely when tmux
wrote anything to stdout (for example under --force-close race conditions).

- send-keys now uses stdout: "ignore" so there is no pipe to drain
- kill-pane keeps the pipe but drains stdout/stderr alongside proc.exited
- switch imports to the new spawn-process helper so the behavior is
  covered by hermetic tests that mock the spawn boundary
2026-04-18 19:31:04 +09:00
YeonGyu-Kim 7a7926f222 chore(tmux-subagent): remove dead event-handler modules
Drop cleanup.ts, session-created-handler.ts, and session-deleted-handler.ts
which were never wired up; the lifecycle logic they contained lives inline
in TmuxSessionManager. Barrels trimmed to match.
2026-04-18 19:30:44 +09:00
YeonGyu-Kim e303feefd2 feat(team-mode): add team-layout-tmux for focus+grid pane visualization
Introduce a dedicated module that builds the two-window tmux layout team-mode relies on:
- "focus" window uses main-vertical for the lead-centric view
- "grid" window uses tiled so every member pane is visible at once

createTeamLayout spawns omo-team-<teamRunId>, registers pane titles with
color-coded labels, and returns the focus/grid window IDs plus a
pane-by-member map. removeTeamLayout tears the session down idempotently.
canVisualize short-circuits when TMUX is unset so callers degrade
gracefully outside a tmux context.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 18:33:58 +09:00
YeonGyu-Kim bf7fd7ce67 Merge pull request #3501 from code-yeongyu/fix/perf-omo-in-tree
perf(plugin-init): de-slop in-tree launch — cold init 1.2s → 29ms (43× faster)
2026-04-18 17:35:09 +09:00
Sisyphus 0a808de6a2 fix(auto-update-checker): trigger deferred startup on session.created with parentID guard 2026-04-18 17:23:50 +09:00
YeonGyu-Kim 91848057ea fix(background-agent): fire promptAsync before tmux callback
Awaiting the tmux callback blocked the prompt for up to 10s (waitForSessionReady downstream). During that window the spawned pane ran 'opencode attach' against an empty session and rendered a blank TUI. Users saw 'pane created but attach not working'.

Start promptWithModelSuggestionRetry immediately after session.create, then invoke the tmux callback as fire-and-forget. The session becomes active before the attach client connects.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 17:22:11 +09:00
YeonGyu-Kim c9c1c58c2c fix(tmux-subagent): track pane without blocking on session readiness
waitForSessionReady polled session.status for up to 10s before the pane was registered, but session.status only becomes visible after promptAsync starts. Blocking pane tracking on that signal caused the attach client to see an empty session and render a blank TUI.

Track the pane immediately after spawn, and run the readiness probe in the background purely for observability.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 17:22:01 +09:00
Sisyphus a6a5a08b56 fix(tools/skill): invalidate skill cache at session boundary 2026-04-18 17:21:09 +09:00
Sisyphus a8504be70c test(auto-update-checker): cover session.created trigger with parentID guard 2026-04-18 17:19:18 +09:00
Sisyphus 1d187097f3 test(tools/skill): cover per-session skill cache invalidation 2026-04-18 17:17:23 +09:00
YeonGyu-Kim bd1529825c fix(test): isolate skill factory discovery in ci
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 15:24:19 +09:00
Sisyphus e766354e22 test(auto-update-checker): align zauc-mocks-hook with deferred idle check
The zauc-mocks-hook variant previously asserted that session.created synchronously
ran the startup checks. After auto-update-checker was refactored to defer work to
the first session.idle via scheduleDeferredIdleCheck (5s timer), those assertions
never fired.

Mirror the mock+capture pattern from hook.test.ts so the test drives the deferred
callback synchronously, preserving the original invariants (hasChecked guard,
localDev toast, sisyphus wording) without waiting on real timers.
2026-04-18 15:05:30 +09:00
Sisyphus 8e3f4cc63c fix(tools/skill): harden description pipeline against empty skill list after lazy factory 2026-04-18 14:57:44 +09:00
Sisyphus 0e1a946c1d test(skill-tool): isolate skill discovery spies from other suites 2026-04-18 14:57:30 +09:00
Sisyphus 10b1905f60 test(skill-loader): reset shared skill cache in async resolver tests 2026-04-18 14:57:23 +09:00
Sisyphus 14868430bc test(auto-update-checker): align test triggers with deferred idle check 2026-04-18 14:55:51 +09:00
Sisyphus 61675adbf1 test(slashcommand): isolate command-loader cache between tests 2026-04-18 14:53:14 +09:00
Sisyphus 0ca571e74d Merge branch 'fix/perf-d03' into fix/perf-omo-in-tree 2026-04-18 14:43:55 +09:00
Sisyphus 1fd678f5ad Merge branch 'fix/perf-d02' into fix/perf-omo-in-tree 2026-04-18 14:43:48 +09:00
Sisyphus 85e4f5ecab Merge branch 'fix/perf-q11' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus e47a6aa7f6 Merge branch 'fix/perf-q10' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus b60cad0eb0 Merge branch 'fix/perf-q09' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus d9cea93101 Merge branch 'fix/perf-q08' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 9f15a09595 Merge branch 'fix/perf-q06' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus c5268af087 Merge branch 'fix/perf-q05' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 4857666a89 Merge branch 'fix/perf-q04' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 5faba8d572 Merge branch 'fix/perf-q03' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 38990ec76f Merge branch 'fix/perf-q02' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 56d2d2543b Merge branch 'fix/perf-d10' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 5a20e4060f Merge branch 'fix/perf-d09' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 36ebafda8c Merge branch 'fix/perf-d08' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 82ddc9c6f7 Merge branch 'fix/perf-d07' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus 1db481eb9c Merge branch 'fix/perf-d06' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus a227821d27 Merge branch 'fix/perf-d05' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus dc0d64d5f1 Merge branch 'fix/perf-d04' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
Sisyphus b6b898d061 Merge branch 'fix/perf-d01' into fix/perf-omo-in-tree 2026-04-18 14:43:37 +09:00
YeonGyu-Kim edcc9a1e64 fix(todo-continuation-enforcer): defer prune interval to first idle event
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 14:16:40 +09:00
YeonGyu-Kim 1be1cd6e53 fix(tools/skill): make factory pure and stop defeating skill-loader cache 2026-04-18 14:15:14 +09:00
YeonGyu-Kim a068915dc4 test(perf): add plugin init regression budget
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 14:14:55 +09:00
YeonGyu-Kim 40bd3e02d2 test(tools/skill): cover factory laziness and skill-cache invariants 2026-04-18 14:14:00 +09:00
Sisyphus 52512f226a fix(rules-injector): cache directory scan results per session 2026-04-18 14:13:51 +09:00
Sisyphus ed6ac7ea96 fix(session-notification): defer platform detection and background checks 2026-04-18 14:13:12 +09:00
YeonGyu-Kim 89d394ed3e fix(claude-code-command-loader): skip EXCLUDED_DIRS and memoize per directory
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 14:13:12 +09:00
Sisyphus 439957c4cd test(session-notification): cover lazy platform detect and scheduler startup 2026-04-18 14:13:07 +09:00
YeonGyu-Kim 6dc2234d89 fix(shared): memoize detectPluginConfigFile per process
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-18 14:12:55 +09:00