Unified Agent + reusable Go agent core.
0
fork

Configure Feed

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

fix: trigger heartbeat for pending todos

Lyric 4fd697e3 1314722f

+369 -34
+14 -15
internal/heartbeatutil/heartbeat.go
··· 23 23 if err := materializeDueRecurringTodos(checklistPath); err != nil { 24 24 return "", true, err 25 25 } 26 - checklist, empty, err := readHeartbeatChecklist(checklistPath) 26 + todoBlock, err := readOpenTodosBlock(checklistPath) 27 27 if err != nil { 28 28 return "", true, err 29 29 } 30 - if empty { 31 - return "", true, nil 32 - } 33 - checklist, err = appendOpenTodos(checklistPath, checklist) 30 + checklist, checklistEmpty, err := readHeartbeatChecklist(checklistPath) 34 31 if err != nil { 35 32 return "", true, err 36 33 } 37 - return checklist, false, nil 34 + var sections []string 35 + if todoBlock != "" { 36 + sections = append(sections, todoBlock) 37 + } 38 + if checklist != "" { 39 + sections = append(sections, checklist) 40 + } 41 + return strings.Join(sections, "\n\n"), checklistEmpty, nil 38 42 } 39 43 40 - func appendOpenTodos(checklistPath string, task string) (string, error) { 41 - task = strings.TrimSpace(task) 44 + func readOpenTodosBlock(checklistPath string) (string, error) { 42 45 todoPath := todoWIPPathForChecklist(checklistPath) 43 46 if todoPath == "" { 44 - return task, nil 47 + return "", nil 45 48 } 46 49 raw, err := os.ReadFile(todoPath) 47 50 if err != nil { 48 51 if os.IsNotExist(err) { 49 - return task, nil 52 + return "", nil 50 53 } 51 54 return "", err 52 55 } ··· 55 58 return "", err 56 59 } 57 60 if len(wip.Entries) == 0 { 58 - return task, nil 61 + return "", nil 59 62 } 60 63 var b strings.Builder 61 - if task != "" { 62 - b.WriteString(task) 63 - b.WriteString("\n\n") 64 - } 65 64 b.WriteString("## Current TODO.md Open Items\n\n") 66 65 for _, item := range wip.Entries { 67 66 line := heartbeatTODOEntryLine(item)
+66
internal/heartbeatutil/heartbeat_recurring_test.go
··· 57 57 t.Fatalf("TODO.RECUR.md did not advance Next:\n%s", string(recurRaw)) 58 58 } 59 59 } 60 + 61 + func TestBuildHeartbeatTaskRunsForOpenTodosWhenChecklistEmpty(t *testing.T) { 62 + root := t.TempDir() 63 + checklistPath := filepath.Join(root, "HEARTBEAT.md") 64 + if err := os.WriteFile(checklistPath, []byte("# Heartbeat\n\n"), 0o600); err != nil { 65 + t.Fatalf("write heartbeat checklist: %v", err) 66 + } 67 + todoText := `--- 68 + created_at: "1970-01-01T00:00:00Z" 69 + updated_at: "1970-01-01T00:00:00Z" 70 + open_count: 1 71 + --- 72 + 73 + # TODO 74 + 75 + - [ ] [Created](2026-05-01 12:41) | Review open invoices. 76 + ` 77 + if err := os.WriteFile(filepath.Join(root, "TODO.md"), []byte(todoText), 0o600); err != nil { 78 + t.Fatalf("write TODO.md: %v", err) 79 + } 80 + 81 + task, checklistEmpty, err := BuildHeartbeatTask(checklistPath) 82 + if err != nil { 83 + t.Fatalf("BuildHeartbeatTask() error = %v", err) 84 + } 85 + if !checklistEmpty { 86 + t.Fatalf("checklistEmpty = false, want true") 87 + } 88 + if !strings.Contains(task, "## Current TODO.md Open Items") || !strings.Contains(task, "Review open invoices.") { 89 + t.Fatalf("heartbeat task missing open TODO snapshot:\n%s", task) 90 + } 91 + } 92 + 93 + func TestBuildHeartbeatTaskMaterializesRecurringTodosWhenChecklistEmpty(t *testing.T) { 94 + root := t.TempDir() 95 + checklistPath := filepath.Join(root, "HEARTBEAT.md") 96 + if err := os.WriteFile(checklistPath, []byte("# Heartbeat\n\n"), 0o600); err != nil { 97 + t.Fatalf("write heartbeat checklist: %v", err) 98 + } 99 + dueAt := time.Now().UTC().Add(-1 * time.Hour).Format("2006-01-02 15:04") 100 + recurPath := filepath.Join(root, "TODO.RECUR.md") 101 + recur := `--- 102 + created_at: "1970-01-01T00:00:00Z" 103 + updated_at: "1970-01-01T00:00:00Z" 104 + recurring_count: 1 105 + --- 106 + 107 + # TODO Recurring 108 + 109 + - [ ] [Next](` + dueAt + `), [Repeat](daily) | Review open invoices. 110 + ` 111 + if err := os.WriteFile(recurPath, []byte(recur), 0o600); err != nil { 112 + t.Fatalf("write recurring todo: %v", err) 113 + } 114 + 115 + task, checklistEmpty, err := BuildHeartbeatTask(checklistPath) 116 + if err != nil { 117 + t.Fatalf("BuildHeartbeatTask() error = %v", err) 118 + } 119 + if !checklistEmpty { 120 + t.Fatalf("checklistEmpty = false, want true") 121 + } 122 + if !strings.Contains(task, "## Current TODO.md Open Items") || !strings.Contains(task, dueAt+" Review open invoices.") { 123 + t.Fatalf("heartbeat task missing materialized TODO snapshot:\n%s", task) 124 + } 125 + }
+8
web/vitepress/docs/.vitepress/i18n.ts
··· 72 72 } 73 73 }, 74 74 { 75 + slug: 'todo-and-heartbeat', 76 + text: { 77 + en: 'TODO and Heartbeat', 78 + zh: '待办事项与 Heartbeat', 79 + ja: 'TODO と Heartbeat' 80 + } 81 + }, 82 + { 75 83 slug: 'skills', 76 84 text: { 77 85 en: 'Skills',
+4 -6
web/vitepress/docs/guide/built-in-tools.md
··· 110 110 111 111 ### `todo_update` 112 112 113 - Maintains `TODO.md` / `TODO.DONE.md` under `file_state_dir`, including add and complete operations. 113 + Maintains todo files under `file_state_dir`, including one-off todos in `TODO.md`, completed one-off todos in `TODO.DONE.md`, and recurring todos in `TODO.RECUR.md`. 114 114 115 115 - Key limits: `add` requires `people`; `complete` uses semantic matching and will error on no-match or ambiguous match. 116 - - Recurring todos are added with `action=add_recurring` and stored in `TODO.RECUR.md` under `file_state_dir`: 117 - ```text 118 - - [ ] [Next](2026-05-02 09:00), [Repeat](daily), [TZ](Asia/Tokyo), [ChatID](tg:-100123) | Remind [John](tg:@john) to submit report. 119 - ``` 120 - Supported repeat values are `daily`, `weekly`, `every N days`, and `every N hours`. `TZ` is optional; when omitted, the runtime local timezone is used. Heartbeat expands due recurring records into ordinary `TODO.md` items, advances `Next`, then includes the current open `TODO.md` items in the heartbeat task. 116 + - Recurring todos are added with `action=add_recurring`. 117 + 118 + For the runtime workflow around TODO files and `HEARTBEAT.md`, see [TODO and Heartbeat](/guide/todo-and-heartbeat). 121 119 122 120 ## Dedicated Tools 123 121
+1
web/vitepress/docs/guide/docs-map.md
··· 14 14 - [Runtime Modes](/guide/runtime-modes) 15 15 - [Prompt Architecture](/guide/prompt-architecture) 16 16 - [Memory](/guide/memory) 17 + - [TODO and Heartbeat](/guide/todo-and-heartbeat) 17 18 - [Skills](/guide/skills) 18 19 - [Built-in Tools](/guide/built-in-tools) 19 20 - [Subagents](/guide/subagents)
+89
web/vitepress/docs/guide/todo-and-heartbeat.md
··· 1 + --- 2 + title: TODO and Heartbeat 3 + description: How TODO files and HEARTBEAT.md let the agent track work outside the current chat. 4 + --- 5 + 6 + # TODO and Heartbeat 7 + 8 + Heartbeat is the runtime trigger for recurring checks. It can run on a timer or be started by an external poke. Each heartbeat run creates a fresh runtime task and does not include chat history. 9 + 10 + ## Heartbeat Flow 11 + 12 + On each heartbeat tick or poke: 13 + 14 + 1. The runtime reads `TODO.RECUR.md`. 15 + 2. Due recurring todos are copied into `TODO.md`. 16 + 3. Their `Next` timestamp is advanced. 17 + 4. The runtime reads `TODO.md`. 18 + 5. If there are open todos, they are added to the heartbeat task. 19 + 6. The runtime reads `HEARTBEAT.md`. 20 + 7. If `HEARTBEAT.md` is not empty, it is added to the heartbeat task. 21 + 8. If the task has any content, the agent handles it with normal tools, including `todo_update`. 22 + 23 + If `TODO.RECUR.md`, `TODO.md`, and `HEARTBEAT.md` produce no task content, no agent task is started. 24 + 25 + ## HEARTBEAT.md 26 + 27 + `HEARTBEAT.md` is the standing instruction for each heartbeat. It should describe what the agent should check, not a one-off user request. 28 + 29 + Good uses: 30 + 31 + - Check open todos. 32 + - Look for due follow-ups. 33 + - Inspect routine files. 34 + - Send reminders when a todo asks for it. 35 + 36 + Avoid putting one-off tasks directly into `HEARTBEAT.md`. Put those in `TODO.md`, or use `TODO.RECUR.md` if they repeat. 37 + 38 + ## TODO Flow 39 + 40 + TODO files hold concrete work. The `todo_update` tool writes and completes TODO records. During heartbeat, current open `TODO.md` items are added to the heartbeat task so the agent can act on them. 41 + 42 + There are three TODO files. 43 + 44 + ### TODO.md 45 + 46 + `TODO.md` contains work that should happen once: 47 + 48 + ```text 49 + - [ ] [Created](2026-05-01 12:41), [ChatID](tg:-100123) | Remind [John](tg:@john) to submit report. 50 + ``` 51 + 52 + Use `TODO.md` for reminders and tasks that should be handled once. 53 + 54 + ### TODO.DONE.md 55 + 56 + `TODO.DONE.md` contains completed one-off todos. When `todo_update` completes a `TODO.md` item, it moves the record here. 57 + 58 + Recurring todos do not move to `TODO.DONE.md`. 59 + 60 + ### TODO.RECUR.md 61 + 62 + `TODO.RECUR.md` contains repeat rules: 63 + 64 + ```text 65 + - [ ] [Next](2026-05-07 15:00), [Repeat](weekly), [TZ](Asia/Tokyo) | Play tennis. 66 + - [ ] [Next](2026-05-02 09:00), [Repeat](every 6 hours) | Check the report queue. 67 + ``` 68 + 69 + Supported repeat values: 70 + 71 + - `daily` 72 + - `weekly` 73 + - `every N days` 74 + - `every N hours` 75 + 76 + `TZ` is optional. If it is omitted, the runtime local timezone is used. 77 + 78 + Recurring records stay in `TODO.RECUR.md`. On heartbeat, due records are copied into `TODO.md`, and only their `Next` timestamp moves forward. 79 + 80 + ## Choosing the File 81 + 82 + | Need | File | 83 + |---|---| 84 + | Tell the agent what to check on each heartbeat | `HEARTBEAT.md` | 85 + | Do this once | `TODO.md` | 86 + | Keep a record of completed one-off todos | `TODO.DONE.md` | 87 + | Do this repeatedly | `TODO.RECUR.md` | 88 + 89 + For the tool that updates todo files, see [`todo_update`](/guide/built-in-tools#todo_update). For state directory placement, see [Filesystem Roots](/guide/filesystem-roots).
+4 -6
web/vitepress/docs/ja/guide/built-in-tools.md
··· 110 110 111 111 ### `todo_update` 112 112 113 - `file_state_dir` 配下の `TODO.md` / `TODO.DONE.md` を更新し、TODO の追加と完了を扱います。 113 + `file_state_dir` 配下の TODO ファイルを更新します。`TODO.md` の一回限りの TODO、`TODO.DONE.md` の完了済み TODO、`TODO.RECUR.md` の繰り返し TODO を扱います。 114 114 115 115 - 主な制約: `add` では `people` が必要です。`complete` は意味的マッチングを使うため、候補がない場合や曖昧な場合はエラーになります。 116 - - 繰り返し TODO は `action=add_recurring` で追加し、`file_state_dir` 配下の `TODO.RECUR.md` に保存されます。 117 - ```text 118 - - [ ] [Next](2026-05-02 09:00), [Repeat](daily), [TZ](Asia/Tokyo), [ChatID](tg:-100123) | Remind [John](tg:@john) to submit report. 119 - ``` 120 - `Repeat` は `daily`、`weekly`、`every N days`、`every N hours` に対応しています。`TZ` は任意で、省略時は runtime のローカルタイムゾーンを使います。heartbeat は期限が来た繰り返しレコードを通常の `TODO.md` 項目へ展開し、`Next` を進めたうえで、現在の open な `TODO.md` 項目を heartbeat task に含めます。 116 + - 繰り返し TODO は `action=add_recurring` で追加します。 117 + 118 + TODO ファイルと `HEARTBEAT.md` の実行時の流れは [TODO と Heartbeat](/ja/guide/todo-and-heartbeat) を参照してください。 121 119 122 120 ## 専用ツール 123 121
+1
web/vitepress/docs/ja/guide/docs-map.md
··· 14 14 - [Runtime モード](/ja/guide/runtime-modes) 15 15 - [Prompt 設計](/ja/guide/prompt-architecture) 16 16 - [Memory](/ja/guide/memory) 17 + - [TODO と Heartbeat](/ja/guide/todo-and-heartbeat) 17 18 - [Skills](/ja/guide/skills) 18 19 - [組み込みツール](/ja/guide/built-in-tools) 19 20 - [Subagents](/ja/guide/subagents)
+89
web/vitepress/docs/ja/guide/todo-and-heartbeat.md
··· 1 + --- 2 + title: TODO と Heartbeat 3 + description: TODO ファイルと HEARTBEAT.md が、現在のチャット外の作業を追跡する仕組み。 4 + --- 5 + 6 + # TODO と Heartbeat 7 + 8 + Heartbeat は、繰り返し確認を行うための runtime trigger です。タイマーで実行することも、外部 poke で開始することもできます。各 heartbeat run は新しい runtime task を作り、チャット履歴は含みません。 9 + 10 + ## Heartbeat の流れ 11 + 12 + heartbeat tick または poke ごとに、次の順で処理されます。 13 + 14 + 1. Runtime は `TODO.RECUR.md` を読みます。 15 + 2. 期限が来た繰り返し TODO を `TODO.md` にコピーします。 16 + 3. 対応する `Next` 時刻を進めます。 17 + 4. Runtime は `TODO.md` を読みます。 18 + 5. 未完了 TODO があれば、heartbeat task に追加します。 19 + 6. Runtime は `HEARTBEAT.md` を読みます。 20 + 7. `HEARTBEAT.md` が空でなければ、heartbeat task に追加します。 21 + 8. task に内容があれば、agent は通常のツールで処理します。`todo_update` も使えます。 22 + 23 + `TODO.RECUR.md`、`TODO.md`、`HEARTBEAT.md` のどれからも task 内容が作られなければ、agent task は開始されません。 24 + 25 + ## HEARTBEAT.md 26 + 27 + `HEARTBEAT.md` は heartbeat ごとの固定指示です。具体的な一回限りのユーザー依頼ではなく、agent が何を確認するかを書きます。 28 + 29 + 向いている内容: 30 + 31 + - 未完了 TODO を確認する。 32 + - 期限が来た後続対応を探す。 33 + - 定期的に確認するファイルを見る。 34 + - TODO がリマインドを求めているときに通知する。 35 + 36 + 一回限りのタスクを直接 `HEARTBEAT.md` に書くのは避けます。一回限りのタスクは `TODO.md` に、繰り返すタスクは `TODO.RECUR.md` に書きます。 37 + 38 + ## TODO の流れ 39 + 40 + TODO ファイルは具体的な作業を保存します。`todo_update` ツールは TODO 記録の追加と完了を扱います。Heartbeat 実行時には、現在の `TODO.md` の未完了 TODO が heartbeat task に追加され、agent が処理できるようになります。 41 + 42 + TODO ファイルは 3 つあります。 43 + 44 + ### TODO.md 45 + 46 + `TODO.md` は一回だけ実行すればよい作業を保存します。 47 + 48 + ```text 49 + - [ ] [Created](2026-05-01 12:41), [ChatID](tg:-100123) | Remind [John](tg:@john) to submit report. 50 + ``` 51 + 52 + 一回限りのリマインダーやタスクには `TODO.md` を使います。 53 + 54 + ### TODO.DONE.md 55 + 56 + `TODO.DONE.md` は完了した一回限りの TODO を保存します。`todo_update` が `TODO.md` の項目を完了すると、その記録はここへ移動します。 57 + 58 + 繰り返し TODO は `TODO.DONE.md` へ移動しません。 59 + 60 + ### TODO.RECUR.md 61 + 62 + `TODO.RECUR.md` は繰り返しルールを保存します。 63 + 64 + ```text 65 + - [ ] [Next](2026-05-07 15:00), [Repeat](weekly), [TZ](Asia/Tokyo) | Play tennis. 66 + - [ ] [Next](2026-05-02 09:00), [Repeat](every 6 hours) | Check the report queue. 67 + ``` 68 + 69 + 対応している `Repeat` 値: 70 + 71 + - `daily` 72 + - `weekly` 73 + - `every N days` 74 + - `every N hours` 75 + 76 + `TZ` は任意です。省略すると runtime のローカルタイムゾーンが使われます。 77 + 78 + 繰り返し記録は `TODO.RECUR.md` に残ります。Heartbeat 実行時に、期限が来た記録は `TODO.md` にコピーされ、`Next` 時刻だけが進みます。 79 + 80 + ## どのファイルを使うか 81 + 82 + | 必要なこと | ファイル | 83 + |---|---| 84 + | heartbeat ごとに agent が確認することを書く | `HEARTBEAT.md` | 85 + | 一回だけ実行する | `TODO.md` | 86 + | 完了した一回限りの TODO を残す | `TODO.DONE.md` | 87 + | 繰り返し実行する | `TODO.RECUR.md` | 88 + 89 + TODO ファイルを更新するツールは [`todo_update`](/ja/guide/built-in-tools#todo_update) を参照してください。状態ディレクトリの場所は [ファイルシステムのルート](/ja/guide/filesystem-roots) を参照してください。
+3 -7
web/vitepress/docs/zh/guide/built-in-tools.md
··· 112 112 113 113 ### `todo_update` 114 114 115 - 维护 `file_state_dir` 下的 `TODO.md` / `TODO.DONE.md`,支持新增待办和完成待办。 115 + 维护 `file_state_dir` 下的 TODO 文件,包括 `TODO.md` 里的一次性待办、`TODO.DONE.md` 里的已完成一次性待办,以及 `TODO.RECUR.md` 里的循环待办。 116 116 117 117 关键限制:`add` 需要 `people`;`complete` 依赖语义匹配,找不到或匹配过多都会报错。 118 118 119 - 循环待办通过 `action=add_recurring` 新增,写入 `file_state_dir` 下的 `TODO.RECUR.md`: 120 - 121 - ```text 122 - - [ ] [Next](2026-05-02 09:00), [Repeat](daily), [TZ](Asia/Tokyo), [ChatID](tg:-100123) | Remind [John](tg:@john) to submit report. 123 - ``` 119 + 循环待办通过 `action=add_recurring` 新增。 124 120 125 - 当前支持的 `Repeat` 值是 `daily`、`weekly`、`every N days`、`every N hours`。`TZ` 可选;省略时使用运行进程的本地时区。heartbeat 会把到期的循环记录展开成普通 `TODO.md` 待办,推进 `Next`,然后把当前 `TODO.md` 的 open items 一起放进 heartbeat task。 121 + TODO 文件与 `HEARTBEAT.md` 的运行流程见 [待办事项与 Heartbeat](/zh/guide/todo-and-heartbeat)。 126 122 127 123 ## 专属工具 128 124
+1
web/vitepress/docs/zh/guide/docs-map.md
··· 14 14 - [Runtime 模式](/zh/guide/runtime-modes) 15 15 - [Prompt 组织](/zh/guide/prompt-architecture) 16 16 - [Memory](/zh/guide/memory) 17 + - [待办事项与 Heartbeat](/zh/guide/todo-and-heartbeat) 17 18 - [Skills](/zh/guide/skills) 18 19 - [内置工具](/zh/guide/built-in-tools) 19 20 - [Subagents](/zh/guide/subagents)
+89
web/vitepress/docs/zh/guide/todo-and-heartbeat.md
··· 1 + --- 2 + title: 待办事项与 Heartbeat 3 + description: TODO 文件和 HEARTBEAT.md 如何让 agent 在当前对话之外继续跟踪工作。 4 + --- 5 + 6 + # 待办事项与 Heartbeat 7 + 8 + Heartbeat 是用于重复检查的 runtime 触发。它可以按间隔运行,也可以由外部 poke 启动。每次 heartbeat 都会创建一条新的 runtime task,不包含聊天历史。 9 + 10 + ## Heartbeat 流程 11 + 12 + 每次 heartbeat tick 或 poke 时: 13 + 14 + 1. Runtime 读取 `TODO.RECUR.md`。 15 + 2. 到期的循环待办会被复制到 `TODO.md`。 16 + 3. 对应记录的 `Next` 时间会向后推进。 17 + 4. Runtime 读取 `TODO.md`。 18 + 5. 如果有未完成待办,就加入 heartbeat task。 19 + 6. Runtime 读取 `HEARTBEAT.md`。 20 + 7. 如果 `HEARTBEAT.md` 不是空的,就加入 heartbeat task。 21 + 8. 如果 heartbeat task 有内容,agent 用普通工具处理这次任务,包括 `todo_update`。 22 + 23 + 如果 `TODO.RECUR.md`、`TODO.md` 和 `HEARTBEAT.md` 都没有产生任务内容,就不会启动 agent task。 24 + 25 + ## HEARTBEAT.md 26 + 27 + `HEARTBEAT.md` 是每次 heartbeat 的固定说明。它应该描述 agent 要检查什么,而不是保存某个一次性用户请求。 28 + 29 + 适合放在这里的内容: 30 + 31 + - 检查打开的待办事项。 32 + - 查找到期的后续跟进。 33 + - 检查例行文件。 34 + - 当待办事项要求提醒时,发送提醒。 35 + 36 + 不要把一次性任务直接写进 `HEARTBEAT.md`。一次性任务放进 `TODO.md`;重复任务放进 `TODO.RECUR.md`。 37 + 38 + ## TODO 流程 39 + 40 + TODO 文件保存具体待办。`todo_update` 工具负责写入和完成 TODO 记录。Heartbeat 运行时,当前 `TODO.md` 里的未完成待办会被加入 heartbeat task,让 agent 有机会处理它们。 41 + 42 + TODO 文件有三个。 43 + 44 + ### TODO.md 45 + 46 + `TODO.md` 保存只需要执行一次的工作: 47 + 48 + ```text 49 + - [ ] [Created](2026-05-01 12:41), [ChatID](tg:-100123) | Remind [John](tg:@john) to submit report. 50 + ``` 51 + 52 + 一次性提醒和一次性任务放在 `TODO.md`。 53 + 54 + ### TODO.DONE.md 55 + 56 + `TODO.DONE.md` 保存已经完成的一次性待办。`todo_update` 完成 `TODO.md` 里的项目时,会把记录移动到这里。 57 + 58 + 循环待办不会移动到 `TODO.DONE.md`。 59 + 60 + ### TODO.RECUR.md 61 + 62 + `TODO.RECUR.md` 保存重复规则: 63 + 64 + ```text 65 + - [ ] [Next](2026-05-07 15:00), [Repeat](weekly), [TZ](Asia/Tokyo) | Play tennis. 66 + - [ ] [Next](2026-05-02 09:00), [Repeat](every 6 hours) | Check the report queue. 67 + ``` 68 + 69 + 当前支持的 `Repeat` 值: 70 + 71 + - `daily` 72 + - `weekly` 73 + - `every N days` 74 + - `every N hours` 75 + 76 + `TZ` 可选。省略时使用 runtime 的本地时区。 77 + 78 + 循环记录会留在 `TODO.RECUR.md`。Heartbeat 运行时,到期记录会被复制到 `TODO.md`,只有 `Next` 时间向后推进。 79 + 80 + ## 该写到哪里 81 + 82 + | 需求 | 文件 | 83 + |---|---| 84 + | 告诉 agent 每次 heartbeat 要检查什么 | `HEARTBEAT.md` | 85 + | 只做一次 | `TODO.md` | 86 + | 保存已完成的一次性待办 | `TODO.DONE.md` | 87 + | 重复执行 | `TODO.RECUR.md` | 88 + 89 + 更新 TODO 文件的工具见 [`todo_update`](/zh/guide/built-in-tools#todo_update)。状态目录的位置见 [文件系统根目录](/zh/guide/filesystem-roots)。