fix(plugin): verify event hook compatibility with v1.4.0

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-04-08 13:08:03 +09:00
parent e52dd340c6
commit 78e6d780eb
15 changed files with 383 additions and 28 deletions
@@ -11,6 +11,7 @@ import {
generatePartId,
injectHookMessage,
} from "./injector"
import { PART_STORAGE } from "../../shared"
import { isSqliteBackend, resetSqliteBackendCache } from "../../shared/opencode-storage-detection"
//#region Mocks
@@ -53,6 +54,7 @@ function createMockClient(messages: Array<{
tools?: Record<string, boolean>
time?: { created?: number }
}
parts?: Array<{ type?: string }>
}>): {
session: {
messages: (opts: { path: { id: string } }) => Promise<{ data: typeof messages }>
@@ -176,6 +178,24 @@ describe("findNearestMessageWithFieldsFromSDK", () => {
expect(result?.agent).toBe("newest-by-time")
})
it("skips compaction marker user messages when resolving nearest message", async () => {
const mockClient = createMockClient([
{
id: "msg_compaction",
info: { agent: "atlas", model: { providerID: "openai", modelID: "gpt-5" }, time: { created: 200 } },
parts: [{ type: "compaction" }],
},
{
id: "msg_real",
info: { agent: "sisyphus", model: { providerID: "anthropic", modelID: "claude-opus-4" }, time: { created: 100 } },
},
])
const result = await findNearestMessageWithFieldsFromSDK(mockClient as any, "ses_123")
expect(result?.agent).toBe("sisyphus")
})
})
describe("findNearestMessageWithFields JSON backend ordering", () => {
@@ -197,6 +217,34 @@ describe("findNearestMessageWithFields JSON backend ordering", () => {
expect(result?.agent).toBe("newest-by-time")
})
it("skips JSON messages whose parts contain a compaction marker", () => {
mockIsSqliteBackend.mockReturnValue(false)
const messageDir = createMessageDir()
const compactionMessageID = "msg_test_injector_compaction_marker"
const partDir = join(PART_STORAGE, compactionMessageID)
tempDirs.push(partDir)
writeFileSync(join(messageDir, "msg_0001.json"), JSON.stringify({
id: compactionMessageID,
agent: "atlas",
model: { providerID: "openai", modelID: "gpt-5" },
time: { created: 200 },
}))
mkdirSync(partDir, { recursive: true })
writeFileSync(join(partDir, "prt_0001.json"), JSON.stringify({ type: "compaction" }))
writeFileSync(join(messageDir, "msg_0002.json"), JSON.stringify({
id: "msg_0002",
agent: "sisyphus",
model: { providerID: "anthropic", modelID: "claude-opus-4" },
time: { created: 100 },
}))
const result = findNearestMessageWithFields(messageDir)
expect(result?.agent).toBe("sisyphus")
})
})
describe("findFirstMessageWithAgentFromSDK", () => {
@@ -222,6 +270,17 @@ describe("findFirstMessageWithAgentFromSDK", () => {
expect(result).toBe("earliest-agent")
})
it("skips compaction marker user messages when resolving first agent", async () => {
const mockClient = createMockClient([
{ id: "msg_compaction", info: { agent: "atlas", time: { created: 10 } }, parts: [{ type: "compaction" }] },
{ id: "msg_real", info: { agent: "sisyphus", time: { created: 20 } } },
])
const result = await findFirstMessageWithAgentFromSDK(mockClient as any, "ses_123")
expect(result).toBe("sisyphus")
})
it("skips messages without agent field", async () => {
const mockClient = createMockClient([
{ info: {} },
+36 -2
View File
@@ -7,6 +7,7 @@ import type { MessageMeta, OriginalMessageContext, TextPart, ToolPermission } fr
import { log } from "../../shared/logger"
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
import { createInternalAgentTextPart, normalizeSDKResponse } from "../../shared"
import { hasCompactionPartInStorage, isCompactionMessage } from "../../shared/compaction-marker"
export interface StoredMessage {
agent?: string
@@ -32,6 +33,7 @@ interface SDKMessage {
created?: number
}
}
parts?: Array<{ type?: string }>
}
const processPrefix = randomBytes(4).toString("hex")
@@ -39,6 +41,10 @@ let messageCounter = 0
let partCounter = 0
function convertSDKMessageToStoredMessage(msg: SDKMessage): StoredMessage | null {
if (isCompactionMessage(msg)) {
return null
}
const info = msg.info
if (!info) return null
@@ -164,22 +170,38 @@ export function findNearestMessageWithFields(messageDir: string): StoredMessage
return {
fileName,
msg,
hasCompactionMarker: hasCompactionPartInStorage(
typeof (msg as { id?: unknown }).id === "string" ? (msg as { id?: string }).id : undefined,
),
createdAt: typeof msg.time?.created === "number" ? msg.time.created : Number.NEGATIVE_INFINITY,
}
} catch {
return null
}
})
.filter((entry): entry is { fileName: string; msg: StoredMessage & { time?: { created?: number } }; createdAt: number } => entry !== null)
.filter((entry): entry is {
fileName: string
msg: StoredMessage & { time?: { created?: number } }
hasCompactionMarker: boolean
createdAt: number
} => entry !== null)
.sort((left, right) => right.createdAt - left.createdAt || right.fileName.localeCompare(left.fileName))
for (const entry of messages) {
if (entry.hasCompactionMarker || isCompactionMessage({ agent: entry.msg.agent })) {
continue
}
if (entry.msg.agent && entry.msg.model?.providerID && entry.msg.model?.modelID) {
return entry.msg
}
}
for (const entry of messages) {
if (entry.hasCompactionMarker || isCompactionMessage({ agent: entry.msg.agent })) {
continue
}
if (entry.msg.agent || (entry.msg.model?.providerID && entry.msg.model?.modelID)) {
return entry.msg
}
@@ -216,16 +238,28 @@ export function findFirstMessageWithAgent(messageDir: string): string | null {
return {
fileName,
msg,
hasCompactionMarker: hasCompactionPartInStorage(
typeof (msg as { id?: unknown }).id === "string" ? (msg as { id?: string }).id : undefined,
),
createdAt: typeof msg.time?.created === "number" ? msg.time.created : Number.POSITIVE_INFINITY,
}
} catch {
return null
}
})
.filter((entry): entry is { fileName: string; msg: StoredMessage & { time?: { created?: number } }; createdAt: number } => entry !== null)
.filter((entry): entry is {
fileName: string
msg: StoredMessage & { time?: { created?: number } }
hasCompactionMarker: boolean
createdAt: number
} => entry !== null)
.sort((left, right) => left.createdAt - right.createdAt || left.fileName.localeCompare(right.fileName))
for (const entry of messages) {
if (entry.hasCompactionMarker || isCompactionMessage({ agent: entry.msg.agent })) {
continue
}
if (entry.msg.agent) {
return entry.msg.agent
}