feat: Added todo__clear function to the todo system and updated REPL commands to have a .clear todo as well for significant changes in agent direction
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled

This commit is contained in:
2026-04-02 13:13:44 -06:00
parent 1536cf384c
commit 6c17462040
8 changed files with 99 additions and 9 deletions
+2 -1
View File
@@ -467,11 +467,12 @@ inject_todo_instructions: true # Include the default todo instructions into pr
### How It Works ### How It Works
1. When `inject_todo_instructions` is enabled, agents receive instructions on using four built-in tools: 1. When `inject_todo_instructions` is enabled, agents receive instructions on using five built-in tools:
- `todo__init`: Initialize a todo list with a goal - `todo__init`: Initialize a todo list with a goal
- `todo__add`: Add a task to the list - `todo__add`: Add a task to the list
- `todo__done`: Mark a task complete - `todo__done`: Mark a task complete
- `todo__list`: View current todo state - `todo__list`: View current todo state
- `todo__clear`: Clear the entire todo list and reset the goal
These instructions are a reasonable default that detail how to use Loki's To-Do System. If you wish, These instructions are a reasonable default that detail how to use Loki's To-Do System. If you wish,
you can disable the injection of the default instructions and specify your own instructions for how you can disable the injection of the default instructions and specify your own instructions for how
+8 -7
View File
@@ -120,13 +120,14 @@ For more information on sessions and how to use them in Loki, refer to the [sess
Loki lets you build OpenAI GPT-style agents. The following commands let you interact with and manage your agents in Loki lets you build OpenAI GPT-style agents. The following commands let you interact with and manage your agents in
Loki: Loki:
| Command | Description | | Command | Description |
|----------------------|------------------------------------------------------------| |----------------------|-----------------------------------------------------------------------------------------------|
| `.agent` | Use an agent | | `.agent` | Use an agent |
| `.starter` | Display and use conversation starters for the active agent | | `.starter` | Display and use conversation starters for the active agent |
| `.edit agent-config` | Open the agent configuration in your preferred text editor | | `.clear todo` | Clear the todo list and stop auto-continuation (requires `auto_continue: true` on the agent) |
| `.info agent` | Display information about the active agent | | `.edit agent-config` | Open the agent configuration in your preferred text editor |
| `.exit agent` | Leave the active agent | | `.info agent` | Display information about the active agent |
| `.exit agent` | Leave the active agent |
![agent](./images/agents/sql.gif) ![agent](./images/agents/sql.gif)
+16
View File
@@ -117,6 +117,22 @@ Display the current todo list with status of each item.
**Returns:** The full todo list with goal, progress, and item statuses **Returns:** The full todo list with goal, progress, and item statuses
### `todo__clear`
Clear the entire todo list and reset the goal. Use when the current task has been canceled or invalidated.
**Parameters:** None
**Returns:** Confirmation that the todo list was cleared
### REPL Command: `.clear todo`
You can also clear the todo list manually from the REPL by typing `.clear todo`. This is useful when:
- You gave a custom response that changes or cancels the current task
- The agent is stuck in auto-continuation with stale todos
- You want to start fresh without leaving and re-entering the agent
**Note:** This command is only available when an agent with `auto_continue: true` is active. If the todo
system isn't enabled for the current agent, the command will display an error message.
## Auto-Continuation ## Auto-Continuation
When `auto_continue` is enabled, Loki automatically sends a continuation prompt if: When `auto_continue` is enabled, Loki automatically sends a continuation prompt if:
+5
View File
@@ -476,6 +476,11 @@ impl Agent {
self.todo_list.mark_done(id) self.todo_list.mark_done(id)
} }
pub fn clear_todo_list(&mut self) {
self.todo_list.clear();
self.reset_continuation();
}
pub fn continuation_prompt(&self) -> String { pub fn continuation_prompt(&self) -> String {
self.config.continuation_prompt.clone().unwrap_or_else(|| { self.config.continuation_prompt.clone().unwrap_or_else(|| {
formatdoc! {" formatdoc! {"
+2
View File
@@ -7,10 +7,12 @@ pub(in crate::config) const DEFAULT_TODO_INSTRUCTIONS: &str = indoc! {"
- `todo__add`: Add individual tasks. Add all planned steps before starting work. - `todo__add`: Add individual tasks. Add all planned steps before starting work.
- `todo__done`: Mark a task done by id. Call this immediately after completing each step. - `todo__done`: Mark a task done by id. Call this immediately after completing each step.
- `todo__list`: Show the current todo list. - `todo__list`: Show the current todo list.
- `todo__clear`: Clear the entire todo list and reset the goal. Use when the user cancels or changes direction.
RULES: RULES:
- Always create a todo list before starting work. - Always create a todo list before starting work.
- Mark each task done as soon as you finish it; do not batch. - Mark each task done as soon as you finish it; do not batch.
- If the user cancels the current task or changes direction, call `todo__clear` immediately.
- If you stop with incomplete tasks, the system will automatically prompt you to continue." - If you stop with incomplete tasks, the system will automatically prompt you to continue."
}; };
+20
View File
@@ -67,6 +67,11 @@ impl TodoList {
self.todos.is_empty() self.todos.is_empty()
} }
pub fn clear(&mut self) {
self.goal.clear();
self.todos.clear();
}
pub fn render_for_model(&self) -> String { pub fn render_for_model(&self) -> String {
let mut lines = Vec::new(); let mut lines = Vec::new();
if !self.goal.is_empty() { if !self.goal.is_empty() {
@@ -149,6 +154,21 @@ mod tests {
assert!(rendered.contains("○ 2. Map")); assert!(rendered.contains("○ 2. Map"));
} }
#[test]
fn test_clear() {
let mut list = TodoList::new("Some goal");
list.add("Task 1");
list.add("Task 2");
list.mark_done(1);
assert!(!list.is_empty());
list.clear();
assert!(list.is_empty());
assert!(list.goal.is_empty());
assert_eq!(list.todos.len(), 0);
assert!(!list.has_incomplete());
}
#[test] #[test]
fn test_serialization_roundtrip() { fn test_serialization_roundtrip() {
let mut list = TodoList::new("Roundtrip"); let mut list = TodoList::new("Roundtrip");
+21
View File
@@ -76,6 +76,16 @@ pub fn todo_function_declarations() -> Vec<FunctionDeclaration> {
}, },
agent: false, agent: false,
}, },
FunctionDeclaration {
name: format!("{TODO_FUNCTION_PREFIX}clear"),
description: "Clear the entire todo list and reset the goal. Use when the current task has been canceled or invalidated.".to_string(),
parameters: JsonSchema {
type_value: Some("object".to_string()),
properties: Some(IndexMap::new()),
..Default::default()
},
agent: false,
},
] ]
} }
@@ -156,6 +166,17 @@ pub fn handle_todo_tool(config: &GlobalConfig, cmd_name: &str, args: &Value) ->
None => bail!("No active agent"), None => bail!("No active agent"),
} }
} }
"clear" => {
let mut cfg = config.write();
let agent = cfg.agent.as_mut();
match agent {
Some(agent) => {
agent.clear_todo_list();
Ok(json!({"status": "ok", "message": "Todo list cleared"}))
}
None => bail!("No active agent"),
}
}
_ => bail!("Unknown todo action: {action}"), _ => bail!("Unknown todo action: {action}"),
} }
} }
+25 -1
View File
@@ -33,7 +33,7 @@ use std::{env, mem, process};
const MENU_NAME: &str = "completion_menu"; const MENU_NAME: &str = "completion_menu";
static REPL_COMMANDS: LazyLock<[ReplCommand; 38]> = LazyLock::new(|| { static REPL_COMMANDS: LazyLock<[ReplCommand; 39]> = LazyLock::new(|| {
[ [
ReplCommand::new(".help", "Show this help guide", AssertState::pass()), ReplCommand::new(".help", "Show this help guide", AssertState::pass()),
ReplCommand::new(".info", "Show system info", AssertState::pass()), ReplCommand::new(".info", "Show system info", AssertState::pass()),
@@ -137,6 +137,11 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 38]> = LazyLock::new(|| {
"Leave agent", "Leave agent",
AssertState::True(StateFlags::AGENT), AssertState::True(StateFlags::AGENT),
), ),
ReplCommand::new(
".clear todo",
"Clear the todo list and stop auto-continuation",
AssertState::True(StateFlags::AGENT),
),
ReplCommand::new( ReplCommand::new(
".rag", ".rag",
"Initialize or access RAG", "Initialize or access RAG",
@@ -804,6 +809,25 @@ pub async fn run_repl_command(
Some("messages") => { Some("messages") => {
bail!("Use '.empty session' instead"); bail!("Use '.empty session' instead");
} }
Some("todo") => {
let mut cfg = config.write();
match cfg.agent.as_mut() {
Some(agent) => {
if !agent.auto_continue_enabled() {
bail!(
"The todo system is not enabled for this agent. Set 'auto_continue: true' in the agent's config.yaml to enable it."
);
}
if agent.todo_list().is_empty() {
println!("Todo list is already empty.");
} else {
agent.clear_todo_list();
println!("Todo list cleared.");
}
}
None => bail!("No active agent"),
}
}
_ => unknown_command()?, _ => unknown_command()?,
}, },
".vault" => match split_first_arg(args) { ".vault" => match split_first_arg(args) {