H3: cancelTask(skipNotification=true) now schedules task removal.
Previously the early return path skipped cleanup, leaking task objects
in this.tasks Map permanently. Extracted scheduleTaskRemoval() helper
called from both skipNotification and normal paths.
H2: Per-task completion cleanup timer decoupled from allComplete check.
Previously cleanup timer only ran when ALL sibling tasks completed. Now
each finished task gets its own removal timer regardless of siblings.
H1+C2: TaskHistory.clearAll() added and wired into shutdown(). Added
clearSession() calls on session error/deletion and prune cycles.
taskHistory was the only data structure missed by shutdown().
Tests: 10 pass (3 cancel + 3 completion + 4 history)
H7: Process 'exit'/'SIGINT' listeners registered per-session were
never removed when all sessions disconnected, accumulating handlers.
- Add unregisterProcessCleanup() called in disconnectAll()
H8: Race condition where disconnectSession() during pending connection
left orphan clients in state.clients.
- Add disconnectedSessions Set to track mid-flight disconnects
- Check disconnect marker after connection resolves, close if stale
- Clear marker on reconnection for same session
Tests: 6 pass (3 disconnect + 3 race)
When queryWindowState returned null during session deletion, the
session mapping was deleted but the real tmux pane stayed alive,
creating zombie panes.
- Add closePending/closeRetryCount fields to TrackedSession
- Mark sessions closePending instead of deleting on close failure
- Add retryPendingCloses() called from onSessionCreated and cleanup
- Force-remove mappings after 3 failed retry attempts
- Extract TrackedSessionState helper for field initialization
Tests: 3 pass, 9 expects
- Check response.error and !response.data after session.get() to fail closed
- Prevents unlimited spawning when SDK returns non-throwing error responses
- Added regression tests for SDK error and missing data scenarios
- parseInteger() now rejects malformed input like '120oops' using /^\d+$/ regex
- New parseActiveValue() validates active flag is exactly '0' or '1'
- Added regression tests for malformed integers, negative values, empty fields, non-binary active flags
Implements question-denied session permission rules when creating child
sessions via background task delegation. This prevents subagents from
asking questions by passing explicit permission configuration during
session creation.
🤖 GENERATED WITH ASSISTANCE OF OhMyOpenCode
The GitHub repository was renamed from oh-my-opencode to oh-my-openagent,
but all documentation, scripts, and source code references still pointed
to the old repository name. This caused confusion for users who saw
'oh-my-opencode' in docs but a different repo name on GitHub.
Updated all references across:
- README files (en, ko, ja, zh-cn, ru)
- CONTRIBUTING.md
- docs/ (installation, overview, configuration, etc.)
- Source code (schema URLs, GitHub API calls, issue links)
- Test snapshots
The npm package name remains 'oh-my-opencode' (unchanged).
Fixes: https://x.com/Dhruv14588676/status/2031216617762468348
When a completed session is no longer returned by session.status(),
allStatuses[sessionID] is undefined. Previously this fell through to
a 'still running' log, leaving the task stuck as running forever.
Match the sync-session-poller pattern: only continue (skip completion
check) when sessionStatus EXISTS and is not idle. When undefined,
fall through to validateSessionHasOutput + checkSessionTodos +
tryCompleteTask, same as idle.
Align runtime defaults, tests, docs, and generated artifacts with the newer GPT-5.4 baseline. Keep think-mode and prompt-routing expectations consistent after the model version bump.
When git-master skill is loaded, all git commands are prefixed with the configured env variable (default: GIT_MASTER=1). This enables custom git hooks to detect git-master skill usage. Set to empty string to disable.
When users have .md agent files in ~/.claude/agents/ with the same names
as builtin agents (e.g. sisyphus.md, hephaestus.md, atlas.md),
loadAgentsFromDir() hardcodes mode: "subagent" for all loaded agents.
Because the config assembly spreads userAgents after builtinAgents:
config.agent = {
...builtinAgents, // sisyphus: mode="primary"
...userAgents, // sisyphus: mode="subagent" ← overrides
}
this causes all primary agents to become subagents. The TUI filters out
subagents, so only non-plugin agents (like "docs") appear in the agent
selector.
Fix:
- Filter out user/project agents that share names with builtin agents
before spreading into config.agent (both sisyphus-enabled and fallback
branches)
- Respect frontmatter `mode` field in .md agent files instead of
hardcoding "subagent"
- Add `mode` to AgentFrontmatter type
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip \r characters from list-panes output to handle Windows-style line
endings. Also relax field count check from 9 to 8 to handle cases where
pane_title is empty or missing, which caused the parser to drop pane
rows and fail to determine the main pane in single-pane sessions.
Fixes#2241