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:
@@ -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: {} },
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user