Repository navigation
Settled Magic Context child sessions are not deleted on OpenCode 2 (teardown delete is directory-scoped; orphan sweep reads the v1 session table) #558
Description
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:
-
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 returnsSession not found(exit 1); deleting the same id from the correct directory returnsdeleted(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. -
The fallback sweep cannot clean them either.
sweepOrphanedRetrospectiveChildrenreads from the v1sessiontable: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 exactdirectory, 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
- 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.
- No user-facing filter. The host does not read the
magic_context: "hidden-run"metadata marker, so there is no way to hide them. - Storage growth. The rows (and their messages) accumulate indefinitely.
Requests
- 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.
- Point the orphan sweep at
session_v2on OpenCode 2 (or make it store-aware), and match children by their internal marker/title rather than only by exactdirectory. - 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
- The
magic_context: "hidden-run"marker already exists and is reliable — a host that honors it could filter these out with no store changes. - Related: internal child lifecycle / OpenCode 2 readiness (OpenCode V2 readiness: existing-state migration and remaining V2 capability gaps #492).
Activity
magic-alfonso commentedon Sep 28, 2026
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:
teardownChildSessionand thesession-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 themagic_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_unrecognizeddrain 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 lingeringhidden-runsessions 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.magic-alfonso commentedon Sep 29, 2026
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 anddoctorreports 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 runopencode plugin update). If you still see this on 0.44.2, please reopen with a log.fix(railway): run magic-context doctor --fix before OpenChamber starts