Skip to content

Settled Magic Context child sessions are not deleted on OpenCode 2 (teardown delete is directory-scoped; orphan sweep reads the v1 session table) #558

Closed

Description

@morpheus9393

Summary

On Windows + OpenCode 2 (OpenChamber), settled Magic Context internal child sessions (dreamer*, historian, dreamer-classifier, dreamer-memory-mapper) are not deleted after success. They linger in the session store for many hours (observed 0.1h–27h) and are shown as normal, clickable sessions in the host UI.

keep_subagents is at its default false, i.e. the intended behavior is "delete every settled Magic Context child session after success". The teardown path attempts that delete but it does not take effect, and the failure is swallowed by a .catch().

Environment

  • OS: Windows 11
  • Harness: OpenCode 2.x via OpenChamber 2.0.0
  • Magic Context: @cortexkit/opencode-magic-context@0.43.2
  • Stores: ~/.local/share/opencode/opencode.db, ~/.local/share/cortexkit/magic-context/context.db

Evidence

All lingering rows carry the internal marker and a terminal succeeded outcome:

metadata: {"magic_context":"hidden-run","role":"dreamer"|"dreamer-curate"|"historian"}
outcome:  succeeded (most), failed (some)
age:      0.1h … 27.4h
count:    22 lingering at one point in time (after a full day of scheduled tasks)

keep_subagents in config: unset → default false (description: "Debug: keep every settled Magic Context child session instead of deleting it after success").

Root cause (from source)

teardownChildSession (dist) does:

if (promptSettled && !shouldKeepSubagents()) {
  await client.session.delete(request).catch((error) => {
    log(`${context}: session cleanup failed: ${String(error)}`);
  });
  return;
}
if (!promptSettled) {
  // archive + "left to the age-gated sweep"
}

Two problems combine:

  1. The delete is directory-scoped. client.session.delete({ path: { id }, query: { directory: sessionDirectory } }) only resolves the session when the caller's directory matches the session's directory. Reproduced directly: deleting a child session from the wrong cwd returns Session not found (exit 1); deleting the same id from the correct directory returns deleted (exit 0). If the spawn/teardown directory and the session directory ever diverge (worktrees, per-chat directories, transient cwd changes), the delete silently no-ops.

  2. The fallback sweep cannot clean them either. sweepOrphanedRetrospectiveChildren reads from the v1 session table:

    SELECT id, time_created FROM session
    WHERE directory = ? AND (…age predicates…)

    On an OpenCode 2 store the live rows are in session_v2, so the sweep never sees them. The stale rows also only match by exact directory, which misses the divergent-directory case above.

Additionally, the host log shows the child sessions failing to drain at boot:

Failed to drain Session
cause="HiddenCompletionRefusal: hidden_prompt_unrecognized:
       Refusing an unregistered prompt on a Magic Context hidden-run session"

So on OpenCode 2 the hidden-run lifecycle is not completing end-to-end, and nothing reclaims the leftovers.

Impact

  1. Session-list pollution. Every scheduled Dreamer task in every tracked project leaves a permanent, clickable session in the host UI. Over a day this is dozens; the list becomes unusable.
  2. No user-facing filter. The host does not read the magic_context: "hidden-run" metadata marker, so there is no way to hide them.
  3. Storage growth. The rows (and their messages) accumulate indefinitely.

Requests

  1. Make the teardown delete robust on OpenCode 2 — resolve the child session's own directory (it is known at spawn time) instead of relying on the caller's cwd, and surface delete failures instead of swallowing them.
  2. Point the orphan sweep at session_v2 on OpenCode 2 (or make it store-aware), and match children by their internal marker/title rather than only by exact directory.
  3. Consider a user-level keep_subagents-adjacent visibility option so operators can suppress internal child sessions in hosts that do not read the metadata marker.

Notes

Activity

  1. magic-alfonso commented on Sep 28, 2026

    @magic-alfonso

    Thanks for the detailed report. Leftover internal sessions filling the session list is a real problem, and we're investigating it on OpenCode 2 now.

    One note on the analysis: teardownChildSession and the session-table orphan sweep are the OpenCode 1 cleanup path. On OpenCode 2, Magic Context runs hidden work through a separate executor. It creates each child with the magic_context: "hidden-run" marker, retires it, then deletes it through a removal queue, and a boot pass clears children retired earlier. We're checking that path on a real OpenCode 2.0.18 host to see whether retired children are actually removed, whether removal can fail silently (it currently swallows errors), and whether it always targets the child's own directory. We'll also look at cleaning up children left behind by older versions, matched strictly by the hidden-run marker so a user's own sessions are never touched.

    The hidden_prompt_unrecognized drain failures in your log were fixed in 0.44.0 (#548), so part of what you see on 0.43.2 may already be different on 0.44.x. If you move to 0.44.1 at some point, a count of lingering hidden-run sessions after a day of scheduled tasks would help us compare. Because of #554, that's only safe on Windows once the fix for that issue ships, so there's no need to upgrade just for this.

  2. magic-alfonso commented on Sep 29, 2026

    @magic-alfonso

    Fixed in 0.44.2, now on npm. Finished historian and dreamer sessions are deleted in their own directory, and a failed delete is kept for a later attempt instead of being counted as done.

    Under plain opencode serve (OpenChamber, for example) OpenCode gives plugins no way to delete sessions, so Magic Context logs how many are waiting and doctor reports the count. doctor --fix, run with OpenCode closed, backs up the OpenCode store and removes only sessions Magic Context marked and recorded as its own.

    To update: OpenCode 1 and Pi pick it up on restart; on OpenCode 2 open /plugins, select Magic Context and press ctrl+u (or run opencode plugin update). If you still see this on 0.44.2, please reopen with a log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions