Skip to content

Compaction memory injection resets session's active agent to 'general-purpose' #236

Description

@GianniGiglio

What happened

After auto-compaction, opencode-mem's session.compacted handler injects the restored memory context via:

// dist/index.js, session.compacted handler
await ctx.client.session.prompt({
    path: { id: sessionID },
    body: {
        parts: [{ id: `prt-compaction-${Date.now()}`, type: "text", text: memoryContext }],
        noReply: true,
    },
});

This call never sets agent in body, even though SessionPromptData.body (from @opencode-ai/sdk) has an optional agent?: string field. When it's omitted, OpenCode's server defaults the resulting message to a stock fallback agent (observed as general-purpose) instead of inheriting the session's actual current agent.

Impact

Any orchestrated session running a custom primary agent (e.g. a superpowers-style orchestrator, or any custom agent with mode: "primary") permanently loses its agent identity the moment auto-compaction fires — the session silently and irreversibly downgrades to general-purpose for the rest of its life. This happened consistently (checked 4/4 affected sessions in our history) immediately after every session.compacted event that had memories to restore.

This is easy to miss because nothing errors — the session just quietly starts behaving as a different, less-specialized agent (wrong system prompt, wrong tool permissions, no delegation discipline if the original agent was an orchestrator) with no visible warning.

Suggested fix

Before injecting the compaction memory, resolve the session's current agent (e.g. via ctx.client.session.messages({ path: { id: sessionID } }), taking the most recent non-compaction assistant message's agent/mode field) and pass it through explicitly:

const priorMessages = await ctx.client.session.messages({ path: { id: sessionID } });
const lastAssistant = [...priorMessages].reverse().find(
  (m) => m?.info?.role === "assistant" && m.info.mode !== "compaction"
);
const currentAgent = lastAssistant?.info?.agent ?? lastAssistant?.info?.mode;

await ctx.client.session.prompt({
    path: { id: sessionID },
    body: {
        parts: [{ id: `prt-compaction-${Date.now()}`, type: "text", text: memoryContext }],
        noReply: true,
        ...(currentAgent ? { agent: currentAgent } : {}),
    },
});

I've applied this exact patch locally (against the cached dist/index.js) and it resolves the issue for our setup — happy to open a PR with it if useful, though I wasn't 100% sure of the precise shape of the message-list response in the currently published SDK version, so please double-check the info.agent/info.mode field access against the real payload.

Environment

  • opencode-mem 2.23.0

Activity

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions