44 "context"
55 "encoding/json"
66 "errors"
7+ "fmt"
78 "strings"
89 "sync"
910 "testing"
@@ -14,6 +15,7 @@ import (
1415 "github.com/voocel/codebot/internal/config"
1516 "github.com/voocel/codebot/internal/skill"
1617 "github.com/voocel/codebot/internal/storage"
18+ localtools "github.com/voocel/codebot/internal/tools"
1719)
1820
1921type stubChatModel struct {}
@@ -93,7 +95,7 @@ func (m *scriptedReminderModel) Generate(
9395 if msg .Role == agentcore .RoleUser && strings .Contains (msg .TextContent (), "<system-reminder>" ) {
9496 sawInjectedReminder = true
9597 }
96- if msg .Role == agentcore .RoleUser && strings .Contains (msg .TextContent (), "重复调用同一个工具 " ) {
98+ if msg .Role == agentcore .RoleUser && strings .Contains (msg .TextContent (), "repeatedly calling the same tool " ) {
9799 m .secondCallSawReminder = true
98100 }
99101 }
@@ -597,16 +599,16 @@ func TestBuildUserMessagePrependsRuntimeRemindersBeforeStaticReminders(t *testin
597599 Agent : ag ,
598600 Settings : config.Resolved {MaxTurns : 30 },
599601 Cwd : t .TempDir (),
600- Reminders : []string {"<system-reminder>\n 静态提醒 \n </system-reminder>" },
602+ Reminders : []string {"<system-reminder>\n static reminder \n </system-reminder>" },
601603 })
602604 t .Cleanup (s .Close )
603605
604- s .queueRuntimeReminder ("loop" , ReminderRepeatToolCall , "<system-reminder>\n 动态提醒 \n </system-reminder>" )
605- msg := s .buildUserMessage (agentcore .TextBlock ("用户输入 " ))
606+ s .queueRuntimeReminder ("loop" , ReminderRepeatToolCall , "<system-reminder>\n runtime reminder \n </system-reminder>" )
607+ msg := s .buildUserMessage (agentcore .TextBlock ("user input " ))
606608 if len (msg .Content ) != 3 {
607609 t .Fatalf ("expected 3 content blocks, got %d" , len (msg .Content ))
608610 }
609- if ! strings .Contains (msg .Content [0 ].Text , "动态提醒 " ) || ! strings .Contains (msg .Content [1 ].Text , "静态提醒 " ) {
611+ if ! strings .Contains (msg .Content [0 ].Text , "runtime reminder " ) || ! strings .Contains (msg .Content [1 ].Text , "static reminder " ) {
610612 t .Fatalf ("unexpected content ordering: %#v" , msg .Content )
611613 }
612614}
@@ -651,8 +653,8 @@ func TestRepeatedToolCallQueuesRuntimeReminder(t *testing.T) {
651653 s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecEnd , ToolID : toolID , Tool : "read" })
652654 }
653655
654- msg := s .buildUserMessage (agentcore .TextBlock ("继续 " ))
655- if len (msg .Content ) == 0 || ! strings .Contains (msg .Content [0 ].Text , "重复调用同一个工具 " ) {
656+ msg := s .buildUserMessage (agentcore .TextBlock ("continue " ))
657+ if len (msg .Content ) == 0 || ! strings .Contains (msg .Content [0 ].Text , "repeatedly calling the same tool " ) {
656658 t .Fatalf ("expected repeated-call reminder, got %#v" , msg .Content )
657659 }
658660}
@@ -671,7 +673,7 @@ func TestDeliverRuntimeReminderSteersCurrentRun(t *testing.T) {
671673 })
672674 t .Cleanup (s .Close )
673675
674- if err := s .Prompt ("开始 " ); err != nil {
676+ if err := s .Prompt ("start " ); err != nil {
675677 t .Fatalf ("prompt: %v" , err )
676678 }
677679 waitFor (t , time .Second , func () bool {
@@ -685,8 +687,8 @@ func TestContinueWithRuntimeReminderAutoContinuesWhenIdle(t *testing.T) {
685687 model := & scriptedReminderModel {}
686688 ag := agentcore .NewAgent (agentcore .WithModel (model ), agentcore .WithMaxTurns (10 ))
687689 if err := ag .SetMessages ([]agentcore.AgentMessage {
688- textMessage (agentcore .RoleUser , "初始任务 " ),
689- textMessage (agentcore .RoleAssistant , "任务已完成。 " ),
690+ textMessage (agentcore .RoleUser , "initial task " ),
691+ textMessage (agentcore .RoleAssistant , "task completed. " ),
690692 }); err != nil {
691693 t .Fatalf ("set messages: %v" , err )
692694 }
@@ -697,12 +699,129 @@ func TestContinueWithRuntimeReminderAutoContinuesWhenIdle(t *testing.T) {
697699 })
698700 t .Cleanup (s .Close )
699701
700- s .continueWithRuntimeReminder ("test_reminder:1:0" , ReminderRepeatToolCall , "<system-reminder>\n 测试运行时提醒。 \n </system-reminder>" )
702+ s .continueWithRuntimeReminder ("test_reminder:1:0" , ReminderRepeatToolCall , "<system-reminder>\n test runtime reminder. \n </system-reminder>" )
701703 waitFor (t , time .Second , func () bool {
702704 return s .LastAssistantText () == "steered"
703705 })
704706}
705707
708+ func TestComplexPromptQueuesTaskManagementReminder (t * testing.T ) {
709+ t .Parallel ()
710+
711+ ag := agentcore .NewAgent (agentcore .WithModel (& stubChatModel {}))
712+ s := NewSession (SessionConfig {
713+ Agent : ag ,
714+ Settings : config.Resolved {MaxTurns : 30 },
715+ Cwd : t .TempDir (),
716+ TaskStore : localtools .NewTaskStore (),
717+ })
718+ t .Cleanup (s .Close )
719+
720+ s .beginTurn ()
721+ s .runtime .beforeUserPrompt ([]agentcore.ContentBlock {
722+ agentcore .TextBlock ("Build a complete project: a Go CLI app that lets AI agents autonomously write novels." ),
723+ })
724+
725+ msg := s .buildUserMessage (agentcore .TextBlock ("start" ))
726+ if len (msg .Content ) != 2 {
727+ t .Fatalf ("expected one injected reminder plus user block, got %#v" , msg .Content )
728+ }
729+ if ! strings .Contains (msg .Content [0 ].Text , "<system-reminder>" ) {
730+ t .Fatalf ("expected injected system reminder, got %#v" , msg .Content )
731+ }
732+ if msg .Content [1 ].Text != "start" {
733+ t .Fatalf ("expected task management reminder, got %#v" , msg .Content )
734+ }
735+ }
736+
737+ func TestSimplePromptDoesNotQueueTaskManagementReminder (t * testing.T ) {
738+ t .Parallel ()
739+
740+ ag := agentcore .NewAgent (agentcore .WithModel (& stubChatModel {}))
741+ s := NewSession (SessionConfig {
742+ Agent : ag ,
743+ Settings : config.Resolved {MaxTurns : 30 },
744+ Cwd : t .TempDir (),
745+ TaskStore : localtools .NewTaskStore (),
746+ })
747+ t .Cleanup (s .Close )
748+
749+ s .beginTurn ()
750+ s .runtime .beforeUserPrompt ([]agentcore.ContentBlock {
751+ agentcore .TextBlock ("How do I print hello world in Go?" ),
752+ })
753+
754+ msg := s .buildUserMessage (agentcore .TextBlock ("start" ))
755+ if len (msg .Content ) != 1 {
756+ t .Fatalf ("expected no task reminder blocks, got %#v" , msg .Content )
757+ }
758+ if strings .Contains (msg .Content [0 ].Text , "task_create" ) {
759+ t .Fatalf ("unexpected task reminder in %#v" , msg .Content )
760+ }
761+ }
762+
763+ func TestTaskManagementReminderQueuedForUntrackedOrBroadWork (t * testing.T ) {
764+ t .Parallel ()
765+
766+ cases := []struct {
767+ name string
768+ store * localtools.TaskStore
769+ run func (s * Session )
770+ }{
771+ {
772+ name : "missing task list" ,
773+ store : localtools .NewTaskStore (),
774+ run : func (s * Session ) {
775+ args := json .RawMessage (`{"path":"main.go"}` )
776+ for i := 0 ; i < 3 ; i ++ {
777+ toolID := fmt .Sprintf ("read-%d" , i )
778+ s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecStart , ToolID : toolID , Tool : "read" , Args : args })
779+ s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecEnd , ToolID : toolID , Tool : "read" })
780+ }
781+ },
782+ },
783+ {
784+ name : "single broad task" ,
785+ store : func () * localtools.TaskStore {
786+ store := localtools .NewTaskStore ()
787+ store .Create ("Implement the entire project" , "An overly broad task" , "Implementing the entire project" , nil )
788+ return store
789+ }(),
790+ run : func (s * Session ) {
791+ s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecStart , ToolID : "task-1" , Tool : "task_create" })
792+ s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecEnd , ToolID : "task-1" , Tool : "task_create" })
793+ editArgs := json .RawMessage (`{"file":"main.go"}` )
794+ s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecStart , ToolID : "edit-1" , Tool : "edit" , Args : editArgs })
795+ s .handleAgentEvent (agentcore.Event {Type : agentcore .EventToolExecEnd , ToolID : "edit-1" , Tool : "edit" })
796+ },
797+ },
798+ }
799+
800+ for _ , tc := range cases {
801+ t .Run (tc .name , func (t * testing.T ) {
802+ ag := agentcore .NewAgent (agentcore .WithModel (& stubChatModel {}))
803+ s := NewSession (SessionConfig {
804+ Agent : ag ,
805+ Settings : config.Resolved {MaxTurns : 30 },
806+ Cwd : t .TempDir (),
807+ TaskStore : tc .store ,
808+ })
809+ t .Cleanup (s .Close )
810+
811+ s .beginTurn ()
812+ tc .run (s )
813+
814+ msg := s .buildUserMessage (agentcore .TextBlock ("continue" ))
815+ if len (msg .Content ) != 2 {
816+ t .Fatalf ("expected one injected reminder plus user block, got %#v" , msg .Content )
817+ }
818+ if ! strings .Contains (msg .Content [0 ].Text , "<system-reminder>" ) {
819+ t .Fatalf ("expected injected system reminder, got %#v" , msg .Content )
820+ }
821+ })
822+ }
823+ }
824+
706825func TestRuntimeMetricsTrackCompactionSavings (t * testing.T ) {
707826 t .Parallel ()
708827
0 commit comments