Unified Agent + reusable Go agent core.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: gate todo workflow prompt block

Lyric 2cda878b 383256b1

+88 -28
+3
agent/prompt_template_test.go
··· 32 32 if strings.Contains(prompt, "auth_profiles: \n") { 33 33 t.Fatalf("prompt should not emit empty AuthProfiles lines: %s", prompt) 34 34 } 35 + if strings.Contains(prompt, "[[ TODO Workflow ]]") { 36 + t.Fatalf("prompt should not include todo workflow without an injected block: %s", prompt) 37 + } 35 38 }
-28
agent/prompts/system.md
··· 33 33 {{- end}} 34 34 {{- end}} 35 35 36 - ## TODO Workflow 37 - Use this workflow ONLY when you need to remeber something for future work, 38 - or mark an exisiting todo item as completed. 39 - Maintain `TODO.md` and `TODO.DONE.md` under `file_state_dir`. 40 - 41 - TODO.md entry format examples: 42 - ``` 43 - - [ ] [Created](2026-02-11 09:30) | at 2026-02-11 10:00 Remind [John](tg:@johnwick) to submit the report. 44 - - [ ] [Created](2026-02-11 09:30), [ChatID](tg:-1001981343441) | 2026-02-11 10:00 Have a lunch with [John](tg:@johnwick), Miss Louis and [Sarah](tg:29930) at the Italian restaurant. 45 - ``` 46 - 47 - TODO.DONE.md entry format examples: 48 - ``` 49 - - [x] [Created](2026-02-11 09:30), [Done](2026-02-11 10:00) | at 2026-02-11 10:00 Remind [John](tg:@johnwick) to submit the report. 50 - - [x] [Created](2026-02-11 09:30), [Done](2026-02-11 10:00), [ChatID](tg:-1001981343441) | 2026-02-11 10:00 Had a lunch with [John](tg:@johnwick), Miss Louis and [Sarah](tg:29930) at the Italian restaurant. 51 - ``` 52 - 53 - IF a new task is identified THEN 54 - Use `todo_update` tool to add the task to TODO.md. 55 - ELSE IF a task is expired THEN 56 - Notify the mentioned contacts via `contacts_send`: 57 - Send only a concise reminder message; 58 - DD NOT mention TODO files, pending counts, or delivery status. 59 - Use `todo_update` tool to complete tasks. 60 - ELSE IF a task is NOT due THEN 61 - do nothing. 62 - ENDIF 63 - 64 36 ## Reference Format 65 37 66 38 ### People Reference Format
+2
cmd/mistermorph/daemoncmd/serve.go
··· 470 470 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 471 471 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 472 472 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, registry) 473 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, registry) 473 474 engine := agent.New( 474 475 client, 475 476 registry, ··· 488 489 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 489 490 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 490 491 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, registry) 492 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, registry) 491 493 engine := agent.New( 492 494 client, 493 495 registry,
+1
cmd/mistermorph/runcmd/run.go
··· 182 182 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 183 183 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 184 184 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, reg) 185 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, reg) 185 186 186 187 var hook agent.Hook 187 188 if configutil.FlagOrViperBool(cmd, "interactive", "interactive") {
+1
internal/channelruntime/heartbeat/run.go
··· 229 229 promptprofile.ApplyPersonaIdentity(&promptSpec, opts.Logger) 230 230 promptprofile.AppendLocalToolNotesBlock(&promptSpec, opts.Logger) 231 231 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, reg) 232 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, reg) 232 233 if opts.MemoryOrchestrator != nil && opts.MemoryInjectionEnabled { 233 234 snap, memErr := opts.MemoryOrchestrator.PrepareInjection(memoryruntime.PrepareInjectionRequest{ 234 235 SubjectID: heartbeatMemorySubjectID,
+1
internal/channelruntime/lark/runtime_task.go
··· 94 94 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 95 95 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 96 96 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, reg) 97 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, reg) 97 98 promptprofile.AppendLarkRuntimeBlocks(&promptSpec, isLarkGroupChat(job.ChatType)) 98 99 99 100 engine := agent.New(
+1
internal/channelruntime/line/runtime_task.go
··· 80 80 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 81 81 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 82 82 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, reg) 83 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, reg) 83 84 promptprofile.AppendLineRuntimeBlocks(&promptSpec, isLineGroupChat(job.ChatType)) 84 85 85 86 engine := agent.New(
+1
internal/channelruntime/slack/runtime_task.go
··· 94 94 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 95 95 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 96 96 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, reg) 97 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, reg) 97 98 promptprofile.AppendSlackRuntimeBlocks(&promptSpec, isSlackGroupChat(job.ChatType), job.MentionUsers, strings.Join(availableEmojiNames, ",")) 98 99 99 100 memSubjectID := slackMemorySubjectID(job)
+1
internal/channelruntime/telegram/runtime_task.go
··· 110 110 promptprofile.ApplyPersonaIdentity(&promptSpec, logger) 111 111 promptprofile.AppendLocalToolNotesBlock(&promptSpec, logger) 112 112 promptprofile.AppendPlanCreateGuidanceBlock(&promptSpec, reg) 113 + promptprofile.AppendTodoWorkflowBlock(&promptSpec, reg) 113 114 promptprofile.AppendTelegramRuntimeBlocks(&promptSpec, isGroupChat(job.ChatType), job.MentionUsers, strings.Join(telegramtools.StandardReactionEmojis(), ",")) 114 115 115 116 memSubjectID := telegramMemorySubjectID(job)
+16
internal/promptprofile/prompt_blocks.go
··· 17 17 //go:embed prompts/block_plan_create.md 18 18 var planCreateBlockTemplateSource string 19 19 20 + //go:embed prompts/block_todo_workflow.md 21 + var todoWorkflowBlockTemplateSource string 22 + 20 23 //go:embed prompts/block_local_tool_notes.md 21 24 var localToolNotesBlockTemplateSource string 22 25 ··· 126 129 return 127 130 } 128 131 content := strings.TrimSpace(planCreateBlockTemplateSource) 132 + if content == "" { 133 + return 134 + } 135 + spec.Blocks = append(spec.Blocks, agent.PromptBlock{ 136 + Content: content, 137 + }) 138 + } 139 + 140 + func AppendTodoWorkflowBlock(spec *agent.PromptSpec, registry *tools.Registry) { 141 + if _, ok := registry.Get("todo_update"); !ok { 142 + return 143 + } 144 + content := strings.TrimSpace(todoWorkflowBlockTemplateSource) 129 145 if content == "" { 130 146 return 131 147 }
+42
internal/promptprofile/prompt_blocks_test.go
··· 1 1 package promptprofile 2 2 3 3 import ( 4 + "context" 4 5 "strings" 5 6 "testing" 6 7 7 8 "github.com/quailyquaily/mistermorph/agent" 9 + "github.com/quailyquaily/mistermorph/tools" 8 10 ) 9 11 10 12 func TestAppendSlackRuntimeBlocks_Group(t *testing.T) { ··· 141 143 t.Fatalf("private policy text missing: %q", spec.Blocks[0].Content) 142 144 } 143 145 } 146 + 147 + func TestAppendTodoWorkflowBlock_RequiresTodoUpdateTool(t *testing.T) { 148 + spec := agent.PromptSpec{} 149 + reg := tools.NewRegistry() 150 + 151 + AppendTodoWorkflowBlock(&spec, reg) 152 + 153 + if len(spec.Blocks) != 0 { 154 + t.Fatalf("blocks len = %d, want 0", len(spec.Blocks)) 155 + } 156 + } 157 + 158 + func TestAppendTodoWorkflowBlock_IncludesPolicyWhenTodoUpdateToolExists(t *testing.T) { 159 + spec := agent.PromptSpec{} 160 + reg := tools.NewRegistry() 161 + reg.Register(&testTool{name: "todo_update"}) 162 + 163 + AppendTodoWorkflowBlock(&spec, reg) 164 + 165 + if len(spec.Blocks) != 1 { 166 + t.Fatalf("blocks len = %d, want 1", len(spec.Blocks)) 167 + } 168 + if !strings.Contains(spec.Blocks[0].Content, "[[ TODO Workflow ]]") { 169 + t.Fatalf("todo workflow heading missing: %q", spec.Blocks[0].Content) 170 + } 171 + if !strings.Contains(spec.Blocks[0].Content, "`todo_update`") { 172 + t.Fatalf("todo workflow tool guidance missing: %q", spec.Blocks[0].Content) 173 + } 174 + } 175 + 176 + type testTool struct { 177 + name string 178 + } 179 + 180 + func (t *testTool) Name() string { return t.name } 181 + func (t *testTool) Description() string { return "" } 182 + func (t *testTool) ParameterSchema() string { return "{}" } 183 + func (t *testTool) Execute(context.Context, map[string]any) (string, error) { 184 + return "", nil 185 + }
+19
internal/promptprofile/prompts/block_todo_workflow.md
··· 1 + [[ TODO Workflow ]] 2 + Use this workflow only when you need to remember something for future work, or mark an existing todo item as completed. 3 + Maintain `TODO.md` and `TODO.DONE.md` under `file_state_dir`. 4 + 5 + `TODO.md` entry format examples: 6 + ```text 7 + - [ ] [Created](2026-02-11 09:30) | at 2026-02-11 10:00 Remind [John](tg:@johnwick) to submit the report. 8 + - [ ] [Created](2026-02-11 09:30), [ChatID](tg:-1001981343441) | 2026-02-11 10:00 Have lunch with [John](tg:@johnwick), Miss Louis and [Sarah](tg:29930) at the Italian restaurant. 9 + ``` 10 + 11 + `TODO.DONE.md` entry format examples: 12 + ```text 13 + - [x] [Created](2026-02-11 09:30), [Done](2026-02-11 10:00) | at 2026-02-11 10:00 Remind [John](tg:@johnwick) to submit the report. 14 + - [x] [Created](2026-02-11 09:30), [Done](2026-02-11 10:00), [ChatID](tg:-1001981343441) | 2026-02-11 10:00 Had lunch with [John](tg:@johnwick), Miss Louis and [Sarah](tg:29930) at the Italian restaurant. 15 + ``` 16 + 17 + - If a new task is identified, use `todo_update` to add it to `TODO.md`. 18 + - If a task is expired, notify mentioned contacts via `contacts_send` with a concise reminder. Do not mention TODO files, pending counts, or delivery status. Then use `todo_update` to complete the task. 19 + - If a task is not due, do nothing.