Skip to content

Commit 12dc48c

Browse files
committed
refactor: make synthetic injection IDs deterministic
1 parent 3c9ce68 commit 12dc48c

File tree

4 files changed

+29
-20
lines changed

4 files changed

+29
-20
lines changed

lib/messages/prune.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,14 @@ const filterCompressedRanges = (
187187
if (userMessage) {
188188
const userInfo = userMessage.info as UserMessage
189189
const summaryContent = summary.summary
190+
const summarySeed = `${summary.blockId}:${summary.anchorMessageId}`
190191
result.push(
191-
createSyntheticUserMessage(userMessage, summaryContent, userInfo.variant),
192+
createSyntheticUserMessage(
193+
userMessage,
194+
summaryContent,
195+
userInfo.variant,
196+
summarySeed,
197+
),
192198
)
193199

194200
logger.info("Injected compress summary", {

lib/messages/utils.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import { ulid } from "ulid"
1+
import { createHash } from "node:crypto"
22
import { isMessageCompacted } from "../shared-utils"
33
import type { SessionState, WithParts } from "../state"
44
import type { UserMessage } from "@opencode-ai/sdk/v2"
55

6-
const generateUniqueId = (prefix: string): string => `${prefix}_${ulid()}`
6+
const SUMMARY_ID_HASH_LENGTH = 16
7+
8+
const generateStableId = (prefix: string, seed: string): string => {
9+
const hash = createHash("sha256").update(seed).digest("hex").slice(0, SUMMARY_ID_HASH_LENGTH)
10+
return `${prefix}_${hash}`
11+
}
712

813
const isGeminiModel = (modelID: string): boolean => {
914
const lowerModelID = modelID.toLowerCase()
@@ -14,11 +19,13 @@ export const createSyntheticUserMessage = (
1419
baseMessage: WithParts,
1520
content: string,
1621
variant?: string,
22+
stableSeed?: string,
1723
): WithParts => {
1824
const userInfo = baseMessage.info as UserMessage
1925
const now = Date.now()
20-
const messageId = generateUniqueId("msg")
21-
const partId = generateUniqueId("prt")
26+
const deterministicSeed = stableSeed?.trim() || userInfo.id
27+
const messageId = generateStableId("msg_dcp_summary", deterministicSeed)
28+
const partId = generateStableId("prt_dcp_summary", deterministicSeed)
2229

2330
return {
2431
info: {
@@ -42,9 +49,14 @@ export const createSyntheticUserMessage = (
4249
}
4350
}
4451

45-
export const createSyntheticTextPart = (baseMessage: WithParts, content: string) => {
52+
export const createSyntheticTextPart = (
53+
baseMessage: WithParts,
54+
content: string,
55+
stableSeed?: string,
56+
) => {
4657
const userInfo = baseMessage.info as UserMessage
47-
const partId = generateUniqueId("prt")
58+
const deterministicSeed = stableSeed?.trim() || userInfo.id
59+
const partId = generateStableId("prt_dcp_text", deterministicSeed)
4860

4961
return {
5062
id: partId,
@@ -59,12 +71,14 @@ export const createSyntheticToolPart = (
5971
baseMessage: WithParts,
6072
content: string,
6173
modelID: string,
74+
stableSeed?: string,
6275
) => {
6376
const userInfo = baseMessage.info as UserMessage
6477
const now = Date.now()
6578

66-
const partId = generateUniqueId("prt")
67-
const callId = generateUniqueId("call")
79+
const deterministicSeed = stableSeed?.trim() || userInfo.id
80+
const partId = generateStableId("prt_dcp_tool", deterministicSeed)
81+
const callId = generateStableId("call_dcp_tool", deterministicSeed)
6882

6983
// Gemini requires thoughtSignature bypass to accept synthetic tool parts
7084
const toolPartMetadata = isGeminiModel(modelID)

package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"@opencode-ai/sdk": "^1.1.48",
4747
"fuzzball": "^2.2.3",
4848
"jsonc-parser": "^3.3.1",
49-
"ulid": "^3.0.2",
5049
"zod": "^4.3.6"
5150
},
5251
"devDependencies": {

0 commit comments

Comments
 (0)