@@ -189,17 +189,17 @@ func processMessage(
189189 Content : llmResp .Content ,
190190 })
191191
192- // Build A2A task with proper history containing both user and agent messages .
192+ // Build A2A task with full history including tool calls/results .
193193 responseBytes , _ := json .Marshal (llmResp )
194194 now := workflow .Now (ctx )
195195
196- userMsg := a2atype . NewMessage ( a2atype . MessageRoleUser , a2atype. TextPart { Text : userText } )
196+ taskHistory := buildA2AHistory ( * history )
197197 agentMsg := a2atype .NewMessage (a2atype .MessageRoleAgent , a2atype.TextPart {Text : llmResp .Content })
198198
199199 task := & a2atype.Task {
200200 ID : a2atype .TaskID (req .SessionID ),
201201 ContextID : req .SessionID ,
202- History : [] * a2atype. Message { userMsg , agentMsg } ,
202+ History : taskHistory ,
203203 Status : a2atype.TaskStatus {
204204 State : a2atype .TaskStateCompleted ,
205205 Message : agentMsg ,
@@ -477,6 +477,75 @@ func taskActivityOptions() workflow.ActivityOptions {
477477 }
478478}
479479
480+ // buildA2AHistory converts the internal conversation history into A2A Messages
481+ // suitable for task persistence. Each entry becomes a properly typed message:
482+ // user text, assistant text, function_call DataParts, and function_response DataParts.
483+ func buildA2AHistory (history []conversationEntry ) []* a2atype.Message {
484+ // Build a mapping from tool call ID to tool name for result entries.
485+ toolNameByID := make (map [string ]string )
486+ for _ , entry := range history {
487+ for _ , tc := range entry .ToolCalls {
488+ toolNameByID [tc .ID ] = tc .Name
489+ }
490+ }
491+
492+ var msgs []* a2atype.Message
493+ for _ , entry := range history {
494+ switch entry .Role {
495+ case "user" :
496+ msgs = append (msgs , a2atype .NewMessage (a2atype .MessageRoleUser ,
497+ a2atype.TextPart {Text : entry .Content }))
498+
499+ case "assistant" :
500+ if len (entry .ToolCalls ) > 0 {
501+ // Emit each tool call as a separate message with function_call metadata.
502+ for _ , tc := range entry .ToolCalls {
503+ var args map [string ]any
504+ if len (tc .Args ) > 0 {
505+ _ = json .Unmarshal (tc .Args , & args )
506+ }
507+ msgs = append (msgs , a2atype .NewMessage (a2atype .MessageRoleAgent ,
508+ a2atype.DataPart {
509+ Data : map [string ]any {
510+ "id" : tc .ID ,
511+ "name" : tc .Name ,
512+ "args" : args ,
513+ },
514+ Metadata : map [string ]any {"adk_type" : "function_call" },
515+ }))
516+ }
517+ }
518+ if entry .Content != "" && len (entry .ToolCalls ) == 0 {
519+ msgs = append (msgs , a2atype .NewMessage (a2atype .MessageRoleAgent ,
520+ a2atype.TextPart {Text : entry .Content }))
521+ }
522+
523+ case "tool" :
524+ var result any
525+ if len (entry .ToolResult ) > 0 {
526+ _ = json .Unmarshal (entry .ToolResult , & result )
527+ }
528+ toolName := toolNameByID [entry .ToolCallID ]
529+ if toolName == "" {
530+ toolName = entry .ToolCallID
531+ }
532+ msgs = append (msgs , a2atype .NewMessage (a2atype .MessageRoleAgent ,
533+ a2atype.DataPart {
534+ Data : map [string ]any {
535+ "id" : entry .ToolCallID ,
536+ "name" : toolName ,
537+ "response" : map [string ]any {
538+ "isError" : false ,
539+ "result" : result ,
540+ },
541+ },
542+ Metadata : map [string ]any {"adk_type" : "function_response" },
543+ }))
544+ }
545+ }
546+ return msgs
547+ }
548+
480549// extractTextFromA2AMessage extracts the text content from a JSON-encoded A2A Message.
481550// Falls back to treating the bytes as plain text if parsing fails.
482551func extractTextFromA2AMessage (msgBytes []byte ) string {
0 commit comments