From 1a9e7eb305409a42b54028b9d12549cbe45897d8 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 2 Mar 2026 14:48:39 +0900 Subject: [PATCH] fix(hook-message-injector): add process-unique prefix to message/part IDs to prevent storage collisions IDs now include a random 8-hex-char prefix per process (e.g. msg_a1b2c3d4_000001) preventing collisions when counters reset across process restarts. --- src/features/hook-message-injector/injector.test.ts | 10 ++++++---- src/features/hook-message-injector/injector.ts | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/features/hook-message-injector/injector.test.ts b/src/features/hook-message-injector/injector.test.ts index c66d18d95..6481e8851 100644 --- a/src/features/hook-message-injector/injector.test.ts +++ b/src/features/hook-message-injector/injector.test.ts @@ -197,7 +197,7 @@ describe("findFirstMessageWithAgentFromSDK", () => { describe("generateMessageId", () => { it("returns deterministic sequential IDs with fixed format", () => { // given - const format = /^msg_\d{12}$/ + const format = /^msg_[0-9a-f]{8}_\d{6}$/ // when const firstId = generateMessageId() @@ -206,14 +206,15 @@ describe("generateMessageId", () => { // then expect(firstId).toMatch(format) expect(secondId).toMatch(format) - expect(Number(secondId.slice(4))).toBe(Number(firstId.slice(4)) + 1) + expect(secondId.split("_")[1]).toBe(firstId.split("_")[1]) + expect(Number(secondId.split("_")[2])).toBe(Number(firstId.split("_")[2]) + 1) }) }) describe("generatePartId", () => { it("returns deterministic sequential IDs with fixed format", () => { // given - const format = /^prt_\d{12}$/ + const format = /^prt_[0-9a-f]{8}_\d{6}$/ // when const firstId = generatePartId() @@ -222,7 +223,8 @@ describe("generatePartId", () => { // then expect(firstId).toMatch(format) expect(secondId).toMatch(format) - expect(Number(secondId.slice(4))).toBe(Number(firstId.slice(4)) + 1) + expect(secondId.split("_")[1]).toBe(firstId.split("_")[1]) + expect(Number(secondId.split("_")[2])).toBe(Number(firstId.split("_")[2]) + 1) }) }) diff --git a/src/features/hook-message-injector/injector.ts b/src/features/hook-message-injector/injector.ts index 68520007e..4d43f025b 100644 --- a/src/features/hook-message-injector/injector.ts +++ b/src/features/hook-message-injector/injector.ts @@ -1,4 +1,5 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs" +import { randomBytes } from "node:crypto" import { join } from "node:path" import type { PluginInput } from "@opencode-ai/plugin" import { MESSAGE_STORAGE, PART_STORAGE } from "./constants" @@ -29,6 +30,7 @@ interface SDKMessage { } } +const processPrefix = randomBytes(4).toString("hex") let messageCounter = 0 let partCounter = 0 @@ -208,11 +210,11 @@ export function findFirstMessageWithAgent(messageDir: string): string | null { } export function generateMessageId(): string { - return `msg_${String(++messageCounter).padStart(12, "0")}` + return `msg_${processPrefix}_${String(++messageCounter).padStart(6, "0")}` } export function generatePartId(): string { - return `prt_${String(++partCounter).padStart(12, "0")}` + return `prt_${processPrefix}_${String(++partCounter).padStart(6, "0")}` } function getOrCreateMessageDir(sessionID: string): string {