Commit Graph

355 Commits

Author SHA1 Message Date
Dan Kochetov 9b187e2128 Merge remote-tracking branch 'origin/dev' into fix/background-notification-hook-gate
# Conflicts:
#	src/features/background-agent/manager.ts
2026-02-16 13:56:33 +02:00
YeonGyu-Kim 8e0d1341b6 refactor: consolidate duplicated Promise.all dual reads into resolveMessageContext utility 2026-02-16 18:20:27 +09:00
YeonGyu-Kim 1a6810535c refactor: create normalizeSDKResponse helper and replace scattered patterns across 37 files 2026-02-16 18:20:19 +09:00
YeonGyu-Kim 5a6a9e9800 fix: defensive SDK response handling & parts-reader normalization
- Replace all response.data ?? [] with (response.data ?? response)
  pattern across 14 files to handle SDK array-shaped responses
- Normalize SDK parts in parts-reader.ts by injecting sessionID/
  messageID before validation (P1: SDK parts lack these fields)
- Treat unknown part types as having content in
  recover-empty-content-message-sdk.ts to prevent false placeholder
  injection on image/file parts
- Replace local isRecord with shared import in parts-reader.ts
2026-02-16 16:13:40 +09:00
YeonGyu-Kim 8edf6ed96f fix: address 5 SDK compatibility issues from Cubic round 8
- P1: Use compacted timestamp check instead of nonexistent truncated
  field in target-token-truncation.ts
- P1: Use defensive (response.data ?? response) pattern in
  hook-message-injector/injector.ts to match codebase convention
- P2: Filter by tool type in countTruncatedResultsFromSDK to avoid
  counting non-tool compacted parts
- P2: Treat thinking/meta-only messages as empty in both
  empty-content-recovery-sdk.ts and message-builder.ts to align
  SDK path with file-based logic
2026-02-16 16:13:40 +09:00
YeonGyu-Kim cfb8164d9a docs: regenerate all 13 AGENTS.md files from deep codebase exploration 2026-02-16 16:13:40 +09:00
YeonGyu-Kim 8d82025b70 fix: address Cubic round-4 P2 issues
- isTodo: allow optional id to match Todo interface, preventing
  todos without ids from being silently dropped
- messageHasContentFromSDK: treat unknown part types as empty
  (continue) instead of content (return true) for parity with
  existing storage logic
- readMessagesFromSDK in recover-empty-content-message-sdk: wrap
  SDK call in try/catch to prevent recovery from throwing
2026-02-16 16:13:40 +09:00
YeonGyu-Kim 3fe0e0c7ae docs: clarify injectHookMessage degradation log on SQLite backend 2026-02-16 16:13:40 +09:00
YeonGyu-Kim 4b2410d0a2 fix: address remaining Cubic review comments (P2 issues)
- Add content-based fallback matching for todos without ids

- Add TODO comment for exported but unused SDK functions

- Add resetStorageClient() for test isolation

- Fixes todo duplication risk on beta (SQLite backend)
2026-02-16 16:13:40 +09:00
YeonGyu-Kim b0944b7fd1 feat(session-manager): add version-gated SDK read path for OpenCode beta
- Add SDK client injection via setStorageClient()

- Version-gate getMainSessions(), getAllSessions(), readSessionMessages(), readSessionTodos()

- Add comprehensive tests for SDK path (beta mode)

- Maintain backward compatibility with JSON fallback
2026-02-16 16:13:40 +09:00
YeonGyu-Kim 5eebef953b refactor(shared): unify MESSAGE_STORAGE/PART_STORAGE constants into single source
- Add src/shared/opencode-storage-paths.ts with consolidated constants

- Update imports in hook-message-injector and session-manager

- Add src/shared/opencode-storage-detection.ts with isSqliteBackend()

- Add OPENCODE_SQLITE_VERSION constant

- Export all from shared/index.ts
2026-02-16 16:13:40 +09:00
YeonGyu-Kim c9c02e0525 refactor(shared): consolidate 13+ getMessageDir copies into single shared function 2026-02-16 16:13:39 +09:00
YeonGyu-Kim e90734d6d9 fix(todo): make Todo id field optional for OpenCode beta compatibility
- Make id field optional in all Todo interfaces (TodoInfo, Todo, TodoItem)
- Fix null-unsafe comparisons in todo-sync.ts to handle missing ids
- Add test case for todos without id field preservation
- All tests pass and typecheck clean
2026-02-16 16:13:39 +09:00
YeonGyu-Kim d3574a392f fix: cancel completion timer on resume and prevent silent notification drop 2026-02-16 16:06:36 +09:00
YeonGyu-Kim 33d290b346 fix: add toast cleanup to all BackgroundManager task removal paths
TaskToastManager entries were never removed when tasks completed via
error, session deletion, stale pruning, or cancelled with
skipNotification. Ghost entries accumulated indefinitely, causing the
'Queued (N)' count in toast messages to grow without bound.

Added toastManager.removeTask() calls to all 4 missing cleanup paths:
- session.error handler
- session.deleted handler
- cancelTask with skipNotification
- pruneStaleTasksAndNotifications

Closes #1866
2026-02-16 13:50:57 +09:00
Dan Kochetov 5298ff2879 fix(background-agent): allow disabling parent session reminders 2026-02-16 00:58:33 +02:00
YeonGyu-Kim 8a5f61724d fix(background-agent): handle message.part.delta for heartbeat (OpenCode >=1.2.0)
OpenCode 1.2.0+ changed reasoning-delta and text-delta to emit
'message.part.delta' instead of 'message.part.updated'. Without
handling this event, lastUpdate was only refreshed at reasoning-start
and reasoning-end, leaving a gap where extended thinking (>3min)
could trigger stale timeout.

Accept both event types as heartbeat sources for forward compatibility.
2026-02-15 14:26:25 +09:00
YeonGyu-Kim 3f557e593c fix(background-agent): use correct OpenCode session status for stale guard
OpenCode uses 'busy'/'retry'/'idle' session statuses, not 'running'.
The stale timeout guard checked for type === 'running' which never
matched, leaving all background tasks vulnerable to stale-kill even
when their sessions were actively processing.

Change sessionIsRunning to check type !== 'idle' instead, protecting
busy and retrying sessions from premature termination.
2026-02-15 14:24:45 +09:00
YeonGyu-Kim a0c9381672 fix: prevent stale timeout from killing actively running background tasks
The stale detection was checking lastUpdate timestamps BEFORE
consulting session.status(), causing tasks to be unfairly killed
after 3 minutes even when the session was actively running
(e.g., during long tool executions or extended thinking).

Changes:
- Reorder pollRunningTasks to fetch session.status() before stale check
- Skip stale-kill entirely when session status is 'running'
- Port no-lastUpdate handling from task-poller.ts into manager.ts
  (previously manager silently skipped tasks without lastUpdate)
- Add sessionStatuses parameter to checkAndInterruptStaleTasks
- Add 7 new test cases covering session-status-aware stale detection
2026-02-14 17:59:01 +09:00
YeonGyu-Kim 65a06aa2b7 Merge pull request #1833 from code-yeongyu/fix/inherit-parent-session-tools
fix: inherit parent session tool restrictions in background task notifications
2026-02-14 15:01:37 +09:00
YeonGyu-Kim affefee12f Merge pull request #1835 from code-yeongyu/fix/issue-1781-tmux-pane-width
fix(tmux): thread agent_pane_min_width config through pane management
2026-02-14 15:01:21 +09:00
YeonGyu-Kim 90463bafd2 Merge pull request #1834 from code-yeongyu/fix/issue-1818-agents-skills-path
fix(skill-loader): discover skills from .agents/skills/ directory
2026-02-14 15:01:18 +09:00
YeonGyu-Kim 3de05f6442 fix: apply parentTools in all parent session notification paths
Both parent-session-notifier.ts and notify-parent-session.ts now include
parentTools in the promptAsync body, ensuring tool restrictions are
consistently applied across all notification code paths.
2026-02-14 14:58:25 +09:00
YeonGyu-Kim 8514906c3d fix: inherit parent session tool restrictions in background task notifications
Pass parentTools from session-tools-store through the background task
lifecycle (launch → task → notify) so that when notifyParentSession
sends promptAsync, the original tool restrictions (e.g., question: false)
are preserved. This prevents the Question tool from re-enabling after
call_omo_agent background tasks complete.
2026-02-14 14:58:25 +09:00
YeonGyu-Kim f20e1aa0d0 feat: store tool restrictions in session-tools-store at prompt-send sites
Call setSessionTools(sessionID, tools) before every prompt dispatch so
the tools object is captured and available for later retrieval when
background tasks complete.
2026-02-14 14:58:25 +09:00
YeonGyu-Kim 936b51de79 feat: add parentTools field to BackgroundTask, LaunchInput, ResumeInput
Allows background tasks to carry the parent session's tool restriction
map so it can be applied when notifying the parent session on completion.
2026-02-14 14:58:25 +09:00
YeonGyu-Kim 7186c368b9 fix(skill-loader): discover skills from .agents/skills/ directory
Add discoverProjectAgentsSkills() for project-level .agents/skills/ and
discoverGlobalAgentsSkills() for ~/.agents/skills/ — matching OpenCode's
native skill discovery paths (https://opencode.ai/docs/skills/).

Updated discoverAllSkills(), discoverSkills(), and createSkillContext()
to include these new sources with correct priority ordering.

Co-authored-by: dtateks <dtateks@users.noreply.github.com>
Closes #1818
2026-02-14 14:58:09 +09:00
YeonGyu-Kim 121a3c45c5 fix(tmux): thread agent_pane_min_width config through pane management
The agent_pane_min_width config value was accepted in the schema and
passed as CapacityConfig.agentPaneWidth but never actually used — the
underscore-prefixed _config parameter in decideSpawnActions was unused,
and all split/capacity calculations used the hardcoded MIN_PANE_WIDTH.

Now decideSpawnActions, canSplitPane, isSplittableAtCount,
findMinimalEvictions, and calculateCapacity all accept and use the
configured minimum pane width, falling back to the default (52) when
not provided.

Closes #1781
2026-02-14 14:58:07 +09:00
YeonGyu-Kim f3ff32fd18 fix(background-agent): detect stale tasks that never received progress updates
Tasks with no progress.lastUpdate were silently skipped in
checkAndInterruptStaleTasks, causing them to hang forever when the model
hangs before its first tool call. Now falls back to checking startedAt
against a configurable messageStalenessTimeoutMs (default: 10 minutes).

Closes #1769
2026-02-14 14:56:51 +09:00
YeonGyu-Kim 4ab93c0cf7 fix: refresh lastUpdate on all message.part.updated events, not just tool events
Reasoning/thinking models (Oracle, Claude Opus) were being killed by the
stale timeout because lastUpdate was only refreshed on tool-type events.
During extended thinking, no tool events fire, so after 3 minutes the
task was incorrectly marked as stale and aborted.

Move progress initialization and lastUpdate refresh before the tool-type
conditional so any message.part.updated event (text, thinking, tool)
keeps the task alive.
2026-02-14 13:33:01 +09:00
YeonGyu-Kim c8cd6370e2 Merge pull request #1817 from code-yeongyu/fix/todo-continuation-always-fire
fix(todo-continuation-enforcer): fire continuation for all sessions with incomplete todos
2026-02-14 11:43:10 +09:00
YeonGyu-Kim 9fe48d252c Merge pull request #1787 from popododo0720/fix/memory-leak-session-messages-caching
fix: reduce session.messages() calls with event-based caching to prevent memory leaks
2026-02-13 18:44:00 +09:00
YeonGyu-Kim e3924437ce feat(compaction): wire TaskHistory into BackgroundManager and compaction pipeline
Records task history at 6 status transitions (pending, running×2, error,
cancelled, completed). Exports TaskHistory from background-agent barrel.
Passes backgroundManager and sessionID through compaction hook chain.
2026-02-13 17:40:44 +09:00
YeonGyu-Kim a413e57676 feat(background-agent): add TaskHistory class for persistent task tracking
In-memory tracker that survives BackgroundManager's cleanup cycles.
Records agent delegations with defensive copies, MAX 100 cap per parent,
undefined-safe upsert, and newline-sanitized formatForCompaction output.
2026-02-13 17:40:12 +09:00
Willy f9ea9a4ee9 fix(project): use directory param instead of process.cwd() for agents, commands, and slash commands
Extends the process.cwd() fix to cover all project-level loaders. In the desktop app, process.cwd() points to the app installation directory instead of the project directory, causing project-level agents, commands, and slash commands to not be discovered. Each function now accepts an optional directory parameter (defaulting to process.cwd() for backward compatibility) and callers pass ctx.directory from the plugin context.
2026-02-13 11:09:35 +08:00
Willy 6914f2fd04 fix(skills): use directory param instead of process.cwd() for project skill discovery
Project-level skills (.opencode/skills/ and .claude/skills/) were not
discovered in desktop app environments because the discover functions
hardcoded process.cwd() to resolve project paths. In desktop apps,
process.cwd() points to the app installation directory rather than the
user's project directory.

Add optional directory parameter to all project-level skill discovery
functions and thread ctx.directory from the plugin context through the
entire skill loading pipeline. Falls back to process.cwd() when
directory is not provided, preserving CLI compatibility.
2026-02-13 10:49:15 +08:00
YeonGyu-Kim 5a83c61d77 fix(skills): normalize windows separators for source globs 2026-02-13 11:17:18 +09:00
YeonGyu-Kim 0001bc87c2 feat(skills): load config sources in runtime discovery 2026-02-13 11:08:46 +09:00
YeonGyu-Kim 3a019792e9 test(background-agent): use createMockTask in session.error tests 2026-02-12 18:26:47 +09:00
YeonGyu-Kim 1ceaaa4311 fix(background-agent): handle session.error and prevent zombie queue starts
Marks background tasks as error on session.error to release concurrency immediately, and skips/removes error tasks from queues to avoid zombie starts.
2026-02-12 18:26:03 +09:00
popododo0720 eb56701996 fix: reduce session.messages() calls with event-based caching to prevent memory leaks
- Replace session.messages() fetch in context-window-monitor with message.updated event cache
- Replace session.messages() fetch in preemptive-compaction with message.updated event cache
- Add per-session transcript cache (5min TTL) to avoid full rebuild per tool call
- Remove session.messages() from background-agent polling (use event-based progress)
- Add TTL pruning to todo-continuation-enforcer session state Map
- Add setInterval.unref() to tool-input-cache cleanup timer

Fixes #1222
2026-02-12 11:38:11 +09:00
YeonGyu-Kim b0c570e054 fix(subagent): remove permission.question=deny override that caused zombie sessions
Child session creation was injecting permission: { question: 'deny' } which
conflicted with OpenCode's child session permission handling, causing subagent
sessions to hang with 0 messages after creation (zombie state).

Remove the permission override from all session creators (BackgroundManager,
sync-session-creator, call-omo-agent) and rely on prompt-level tool restrictions
(tools.question=false) to maintain the intended policy.

Closes #1711
2026-02-11 13:52:20 +09:00
YeonGyu-Kim d60697bb13 fix: guard session_ids with optional chaining to prevent crash
boulderState?.session_ids.includes() only guards boulderState, not
session_ids. If boulder.json is corrupted or missing the field,
session_ids is undefined and .includes() crashes silently, losing
subagent results.

Changes:
- readBoulderState: validate parsed JSON is object, default session_ids to []
- atlas hook line 427: boulderState?.session_ids?.includes
- atlas hook line 655: boulderState?.session_ids?.includes
- prometheus-md-only line 93: boulderState?.session_ids?.includes
- appendSessionId: guard with ?. and initialize to [] if missing

Fixes #1672
2026-02-11 13:27:18 +09:00
YeonGyu-Kim 95a4e971a0 test: add validation tests for readBoulderState session_ids handling
Add tests for corrupted/incomplete boulder.json:
- null JSON value returns null
- primitive JSON value returns null
- missing session_ids defaults to []
- non-array session_ids defaults to []
- empty object defaults session_ids to []
- appendSessionId with missing session_ids does not crash

Refs #1672
2026-02-11 13:25:39 +09:00
YeonGyu-Kim f727aab892 fix(skill-mcp): redact sensitive query params from URLs in error messages
API keys passed as query parameters (exaApiKey, tokens, secrets) were
exposed in thrown error messages. Now replaces them with ***REDACTED***.
2026-02-11 00:45:51 +09:00
YeonGyu-Kim 1199e2b839 fix(background): Wave 2 - fix interrupt status checks, display text, error recovery grace, LSP JSONC
- fix(background): include "interrupt" status in all terminal status checks (3 files)
- fix(background): display "INTERRUPTED" instead of "CANCELLED" for interrupted tasks
- fix(cli): add error recovery grace period in poll-for-completion
- fix(lsp): use JSONC parser for config loading to support comments

All changes verified with tests and typecheck.
2026-02-10 22:00:54 +09:00
YeonGyu-Kim 7fe1a653c8 fix(tests): stabilize toast manager and continuation tests 2026-02-10 22:00:54 +09:00
YeonGyu-Kim fe1faa6d0f docs(tasks): add TODO sync documentation to AGENTS.md
- Add comprehensive TODO SYNC section documenting automatic
  bidirectional sync between tasks and OpenCode todo system
- Improve sync-continuation.test.ts with proper mock modules
  for pollSyncSession and fetchSyncResult dependencies
2026-02-10 22:00:54 +09:00
YeonGyu-Kim 83f1304e01 docs(agents): regenerate all AGENTS.md with deep codebase analysis 2026-02-10 14:53:39 +09:00
YeonGyu-Kim 2fd847d88d refactor: fix import path and update test fixtures
- Fix import path in opencode-skill-loader/loader.ts
- Update executor.test.ts fixtures
2026-02-10 11:41:45 +09:00