fix(plugin): ignore compaction session agent updates

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-03-07 15:39:25 +09:00
parent f5f996983e
commit e193002775
2 changed files with 89 additions and 1 deletions

View File

@@ -0,0 +1,83 @@
declare const require: (name: string) => any
const { afterEach, describe, expect, test } = require("bun:test")
import { _resetForTesting, getSessionAgent, updateSessionAgent } from "../features/claude-code-session-state"
import { createEventHandler } from "./event"
function createMinimalEventHandler() {
return createEventHandler({
ctx: {} as never,
pluginConfig: {} as never,
firstMessageVariantGate: {
markSessionCreated: () => {},
clear: () => {},
},
managers: {
tmuxSessionManager: {
onSessionCreated: async () => {},
onSessionDeleted: async () => {},
},
skillMcpManager: {
disconnectSession: async () => {},
},
} as never,
hooks: {
autoUpdateChecker: { event: async () => {} },
claudeCodeHooks: { event: async () => {} },
backgroundNotificationHook: { event: async () => {} },
sessionNotification: async () => {},
todoContinuationEnforcer: { handler: async () => {} },
unstableAgentBabysitter: { event: async () => {} },
contextWindowMonitor: { event: async () => {} },
directoryAgentsInjector: { event: async () => {} },
directoryReadmeInjector: { event: async () => {} },
rulesInjector: { event: async () => {} },
thinkMode: { event: async () => {} },
anthropicContextWindowLimitRecovery: { event: async () => {} },
runtimeFallback: undefined,
modelFallback: undefined,
agentUsageReminder: { event: async () => {} },
categorySkillReminder: { event: async () => {} },
interactiveBashSession: { event: async () => {} },
ralphLoop: { event: async () => {} },
stopContinuationGuard: { event: async () => {}, isStopped: () => false },
compactionTodoPreserver: { event: async () => {} },
writeExistingFileGuard: { event: async () => {} },
atlasHook: { handler: async () => {} },
} as never,
})
}
describe("createEventHandler compaction agent filtering", () => {
afterEach(() => {
_resetForTesting()
})
test("does not overwrite the stored session agent with compaction", async () => {
// given
const sessionID = "ses_compaction_poisoning"
updateSessionAgent(sessionID, "atlas")
const eventHandler = createMinimalEventHandler()
const input: Parameters<ReturnType<typeof createEventHandler>>[0] = {
event: {
type: "message.updated",
properties: {
info: {
id: "msg-compaction",
sessionID,
role: "user",
agent: "compaction",
time: { created: Date.now() },
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
},
},
},
}
// when
await eventHandler(input)
// then
expect(getSessionAgent(sessionID)).toBe("atlas")
})
})

View File

@@ -97,6 +97,11 @@ function extractProviderModelFromErrorMessage(message: string): { providerID?: s
return {};
}
function isCompactionAgent(agent: string): boolean {
return agent.toLowerCase() === "compaction";
}
type EventInput = Parameters<NonNullable<NonNullable<CreatedHooks["writeExistingFileGuard"]>["event"]>>[0];
export function createEventHandler(args: {
ctx: PluginContext;
@@ -261,7 +266,7 @@ export function createEventHandler(args: {
const agent = info?.agent as string | undefined;
const role = info?.role as string | undefined;
if (sessionID && role === "user") {
if (agent) {
if (agent && !isCompactionAgent(agent)) {
updateSessionAgent(sessionID, agent);
}
const providerID = info?.providerID as string | undefined;