1- import { ulid } from "ulid "
1+ import { createHash } from "node:crypto "
22import { isMessageCompacted } from "../shared-utils"
33import type { SessionState , WithParts } from "../state"
44import 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
813const 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 )
0 commit comments