From ac929f9f44cf7adab9527f07c5454dcf4d7330cb Mon Sep 17 00:00:00 2001 From: Acbox Date: Mon, 23 Feb 2026 00:06:15 +0800 Subject: [PATCH] feat: add message id in user header --- internal/channel/inbound/channel.go | 2 ++ internal/conversation/flow/resolver.go | 15 ++++++++++++--- packages/agent/src/agent.ts | 5 ++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/internal/channel/inbound/channel.go b/internal/channel/inbound/channel.go index 1e93ac00..cc85ef64 100644 --- a/internal/channel/inbound/channel.go +++ b/internal/channel/inbound/channel.go @@ -664,6 +664,7 @@ func (p *ChannelInboundProcessor) persistInboundUser( } } headerifiedQuery := flow.FormatUserHeader( + strings.TrimSpace(msg.Message.ID), strings.TrimSpace(identity.ChannelIdentityID), strings.TrimSpace(identity.DisplayName), msg.Channel.String(), @@ -737,6 +738,7 @@ func (p *ChannelInboundProcessor) createInboxItem( } meta := flow.BuildUserMessageMeta( + strings.TrimSpace(msg.Message.ID), strings.TrimSpace(ident.ChannelIdentityID), displayName, msg.Channel.String(), diff --git a/internal/conversation/flow/resolver.go b/internal/conversation/flow/resolver.go index a80fa76c..7d5e4b35 100644 --- a/internal/conversation/flow/resolver.go +++ b/internal/conversation/flow/resolver.go @@ -383,6 +383,7 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r displayName := r.resolveDisplayName(ctx, req) headerifiedQuery := FormatUserHeader( + strings.TrimSpace(req.ExternalMessageID), strings.TrimSpace(req.SourceChannelIdentityID), displayName, req.CurrentChannel, @@ -1801,6 +1802,7 @@ func parseResolverUUID(id string) (pgtype.UUID, error) { // message. It is the single source of truth shared by the YAML header // (sent to the LLM) and the inbox content JSONB. type UserMessageMeta struct { + MessageID string `json:"message-id,omitempty"` ChannelIdentityID string `json:"channel-identity-id"` DisplayName string `json:"display-name"` Channel string `json:"channel"` @@ -1812,11 +1814,12 @@ type UserMessageMeta struct { // BuildUserMessageMeta constructs a UserMessageMeta from the inbound // parameters. Both FormatUserHeader and inbox content use this. -func BuildUserMessageMeta(channelIdentityID, displayName, channel, conversationType, conversationName string, attachmentPaths []string) UserMessageMeta { +func BuildUserMessageMeta(messageID, channelIdentityID, displayName, channel, conversationType, conversationName string, attachmentPaths []string) UserMessageMeta { if attachmentPaths == nil { attachmentPaths = []string{} } return UserMessageMeta{ + MessageID: messageID, ChannelIdentityID: channelIdentityID, DisplayName: displayName, Channel: channel, @@ -1838,6 +1841,9 @@ func (m UserMessageMeta) ToMap() map[string]any { "time": m.Time, "attachments": m.AttachmentPaths, } + if m.MessageID != "" { + result["message-id"] = m.MessageID + } if m.ConversationName != "" { result["conversation-name"] = m.ConversationName } @@ -1848,8 +1854,8 @@ func (m UserMessageMeta) ToMap() map[string]any { // the LLM sees structured context (sender, channel, time, attachments) // alongside the raw message. This must be the single source of truth for // user-message formatting — the agent gateway must NOT add its own header. -func FormatUserHeader(channelIdentityID, displayName, channel, conversationType, conversationName string, attachmentPaths []string, query string) string { - meta := BuildUserMessageMeta(channelIdentityID, displayName, channel, conversationType, conversationName, attachmentPaths) +func FormatUserHeader(messageID, channelIdentityID, displayName, channel, conversationType, conversationName string, attachmentPaths []string, query string) string { + meta := BuildUserMessageMeta(messageID, channelIdentityID, displayName, channel, conversationType, conversationName, attachmentPaths) return FormatUserHeaderFromMeta(meta, query) } @@ -1858,6 +1864,9 @@ func FormatUserHeader(channelIdentityID, displayName, channel, conversationType, func FormatUserHeaderFromMeta(meta UserMessageMeta, query string) string { var sb strings.Builder sb.WriteString("---\n") + if meta.MessageID != "" { + writeYAMLString(&sb, "message-id", meta.MessageID) + } writeYAMLString(&sb, "channel-identity-id", meta.ChannelIdentityID) writeYAMLString(&sb, "display-name", meta.DisplayName) writeYAMLString(&sb, "channel", meta.Channel) diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index 69396e8f..6c1958da 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -96,7 +96,10 @@ export const createAgent = ( fs.readText('/data/IDENTITY.md'), fs.readText('/data/SOUL.md'), fs.readText('/data/TOOLS.md'), - ]) + ]).catch((error) => { + console.error(error) + return ['', '', ''] + }) return { identityContent, soulContent, toolsContent } }