9 Commits

Author SHA1 Message Date
6c17462040 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
2026-04-02 13:13:44 -06:00
1536cf384c fix: Clarified user text input interaction
CI / All (ubuntu-latest) (push) Failing after 23s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-03-30 16:27:22 -06:00
d6842d7e29 fix: recursion bug with similarly named Bash search functions in the explore agent
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-03-30 13:32:13 -06:00
fbc0acda2a feat: Added available tools to prompts for sisyphus and code-reviewer agent families 2026-03-30 13:13:30 -06:00
0327d041b6 feat: Added available tools to coder prompt 2026-03-30 11:11:43 -06:00
6a01fd4fbd Merge branch 'main' of github.com:Dark-Alex-17/loki 2026-03-30 10:15:51 -06:00
d822180205 fix: updated the error for unauthenticated oauth to include the REPL .authenticated command 2026-03-28 11:57:01 -06:00
89d0fdce26 feat: Improved token efficiency when delegating from sisyphus -> coder 2026-03-18 15:07:29 -06:00
b3ecdce979 build: Removed deprecated agent functions from the .shared/utils.sh script 2026-03-18 15:04:14 -06:00
19 changed files with 187 additions and 156 deletions
+3 -134
View File
@@ -2,68 +2,6 @@
# Shared Agent Utilities - Minimal, focused helper functions
set -euo pipefail
#############################
## CONTEXT FILE MANAGEMENT ##
#############################
get_context_file() {
local project_dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
echo "${project_dir}/.loki-context"
}
# Initialize context file for a new task
# Usage: init_context "Task description"
init_context() {
local task="$1"
local project_dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
local context_file
context_file=$(get_context_file)
cat > "${context_file}" <<EOF
## Project: ${project_dir}
## Task: ${task}
## Started: $(date -Iseconds)
### Prior Findings
EOF
}
# Append findings to the context file
# Usage: append_context "agent_name" "finding summary
append_context() {
local agent="$1"
local finding="$2"
local context_file
context_file=$(get_context_file)
if [[ -f "${context_file}" ]]; then
{
echo ""
echo "[${agent}]:"
echo "${finding}"
} >> "${context_file}"
fi
}
# Read the current context (returns empty string if no context)
# Usage: context=$(read_context)
read_context() {
local context_file
context_file=$(get_context_file)
if [[ -f "${context_file}" ]]; then
cat "${context_file}"
fi
}
# Clear the context file
clear_context() {
local context_file
context_file=$(get_context_file)
rm -f "${context_file}"
}
#######################
## PROJECT DETECTION ##
#######################
@@ -279,9 +217,9 @@ _detect_with_llm() {
evidence=$(_gather_project_evidence "${dir}")
local prompt
prompt=$(cat <<-EOF
Analyze this project directory and determine the project type, primary language, and the correct shell commands to build, test, and check (lint/typecheck) it.
EOF
)
prompt+=$'\n'"${evidence}"$'\n'
@@ -348,80 +286,11 @@ detect_project() {
echo '{"type":"unknown","build":"","test":"","check":""}'
}
######################
## AGENT INVOCATION ##
######################
# Invoke a subagent with optional context injection
# Usage: invoke_agent <agent_name> <prompt> [extra_args...]
invoke_agent() {
local agent="$1"
local prompt="$2"
shift 2
local context
context=$(read_context)
local full_prompt
if [[ -n "${context}" ]]; then
full_prompt="## Orchestrator Context
The orchestrator (sisyphus) has gathered this context from prior work:
<context>
${context}
</context>
## Your Task
${prompt}"
else
full_prompt="${prompt}"
fi
env AUTO_CONFIRM=true loki --agent "${agent}" "$@" "${full_prompt}" 2>&1
}
# Invoke a subagent and capture a summary of its findings
# Usage: result=$(invoke_agent_with_summary "explore" "find auth patterns")
invoke_agent_with_summary() {
local agent="$1"
local prompt="$2"
shift 2
local output
output=$(invoke_agent "${agent}" "${prompt}" "$@")
local summary=""
if echo "${output}" | grep -q "FINDINGS:"; then
summary=$(echo "${output}" | sed -n '/FINDINGS:/,/^[A-Z_]*COMPLETE/p' | grep "^- " | sed 's/^- / - /')
elif echo "${output}" | grep -q "CODER_COMPLETE:"; then
summary=$(echo "${output}" | grep "CODER_COMPLETE:" | sed 's/CODER_COMPLETE: *//')
elif echo "${output}" | grep -q "ORACLE_COMPLETE"; then
summary=$(echo "${output}" | sed -n '/^## Recommendation/,/^## /{/^## Recommendation/d;/^## /d;p}' | sed '/^$/d' | head -10)
fi
if [[ -z "${summary}" ]]; then
summary=$(echo "${output}" | grep -v "^$" | grep -v "^#" | grep -v "^\-\-\-" | tail -10 | head -5)
fi
if [[ -n "${summary}" ]]; then
append_context "${agent}" "${summary}"
fi
if [[ -z "${output}" ]] || [[ -z "$(echo "${output}" | tr -d '[:space:]')" ]]; then
echo "[${agent} agent completed but produced no text output. The agent may have performed work via tool calls (file writes, builds, etc.) that did not generate visible text. Check the project directory for changes.]"
else
echo "${output}"
fi
}
###########################
## FILE SEARCH UTILITIES ##
###########################
search_files() {
_search_files() {
local pattern="$1"
local dir="${2:-.}"
+3
View File
@@ -122,3 +122,6 @@ instructions: |
- Project: {{project_dir}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
## Available Tools:
{{__tools__}}
+25 -3
View File
@@ -29,12 +29,31 @@ instructions: |
## Your Mission
Given an implementation task:
1. Understand what to build (from context provided)
2. Study existing patterns (read 1-2 similar files)
1. Check for orchestrator context first (see below)
2. Fill gaps only. Read files NOT already covered in context
3. Write the code (using tools, NOT chat output)
4. Verify it compiles/builds
5. Signal completion with a summary
## Using Orchestrator Context (IMPORTANT)
When spawned by sisyphus, your prompt will often contain a `<context>` block
with prior findings: file paths, code patterns, and conventions discovered by
explore agents.
**If context is provided:**
1. Use it as your primary reference. Don't re-read files already summarized
2. Follow the code patterns shown. Snippets in context ARE the style guide
3. Read the referenced files ONLY IF you need more detail (e.g. full function
signature, import list, or adjacent code not included in the snippet)
4. If context includes a "Conventions" section, follow it exactly
**If context is NOT provided or is too vague to act on:**
Fall back to self-exploration: grep for similar files, read 1-2 examples,
match their style.
**Never ignore provided context.** It represents work already done upstream.
## Todo System
For multi-file changes:
@@ -104,4 +123,7 @@ instructions: |
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
- Shell: {{__shell__}}
## Available tools:
{{__tools__}}
+3
View File
@@ -68,6 +68,9 @@ instructions: |
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
## Available Tools:
{{__tools__}}
conversation_starters:
- 'Find how authentication is implemented'
+1 -1
View File
@@ -60,7 +60,7 @@ search_files() {
echo "" >> "$LLM_OUTPUT"
local results
results=$(search_files "${pattern}" "${project_dir}")
results=$(_search_files "${pattern}" "${project_dir}")
if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT"
+3
View File
@@ -108,3 +108,6 @@ instructions: |
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
## Available Tools:
{{__tools__}}
+3
View File
@@ -75,6 +75,9 @@ instructions: |
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
## Available Tools:
{{__tools__}}
conversation_starters:
- 'Review this architecture design'
+43 -5
View File
@@ -12,7 +12,7 @@ can_spawn_agents: true
max_concurrent_agents: 4
max_agent_depth: 3
inject_spawn_instructions: true
summarization_threshold: 4000
summarization_threshold: 8000
variables:
- name: project_dir
@@ -70,6 +70,45 @@ instructions: |
| coder | Write/edit files, implement features | Creates/modifies files, runs builds |
| oracle | Architecture decisions, complex debugging | Advisory, high-quality reasoning |
## Coder Delegation Format (MANDATORY)
When spawning the `coder` agent, your prompt MUST include these sections.
The coder has NOT seen the codebase. Your prompt IS its entire context.
### Template:
```
## Goal
[1-2 sentences: what to build/modify and where]
## Reference Files
[Files that explore found, with what each demonstrates]
- `path/to/file.ext` - what pattern this file shows
- `path/to/other.ext` - what convention this file shows
## Code Patterns to Follow
[Paste ACTUAL code snippets from explore results, not descriptions]
<code>
// From path/to/file.ext - this is the pattern to follow:
[actual code explore found, 5-20 lines]
</code>
## Conventions
[Naming, imports, error handling, file organization]
- Convention 1
- Convention 2
## Constraints
[What NOT to do, scope boundaries]
- Do NOT modify X
- Only touch files in Y/
```
**CRITICAL**: Include actual code snippets, not just file paths.
If explore returned code patterns, paste them into the coder prompt.
Vague prompts like "follow existing patterns" waste coder's tokens on
re-exploration that you already did.
## Workflow Examples
### Example 1: Implementation task (explore -> coder, parallel exploration)
@@ -81,12 +120,12 @@ instructions: |
2. todo__add --task "Explore existing API patterns"
3. todo__add --task "Implement profile endpoint"
4. todo__add --task "Verify with build/test"
5. agent__spawn --agent explore --prompt "Find existing API endpoint patterns, route structures, and controller conventions"
6. agent__spawn --agent explore --prompt "Find existing data models and database query patterns"
5. agent__spawn --agent explore --prompt "Find existing API endpoint patterns, route structures, and controller conventions. Include code snippets."
6. agent__spawn --agent explore --prompt "Find existing data models and database query patterns. Include code snippets."
7. agent__collect --id <id1>
8. agent__collect --id <id2>
9. todo__done --id 1
10. agent__spawn --agent coder --prompt "Create user profiles endpoint following existing patterns. [Include context from explore results]"
10. agent__spawn --agent coder --prompt "<structured prompt using Coder Delegation Format above, including code snippets from explore results>"
11. agent__collect --id <coder_id>
12. todo__done --id 2
13. run_build
@@ -135,7 +174,6 @@ instructions: |
## When to Do It Yourself
- Single-file reads/writes
- Simple command execution
- Trivial changes (typos, renames)
- Quick file searches
+2 -1
View File
@@ -467,11 +467,12 @@ inject_todo_instructions: true # Include the default todo instructions into pr
### 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__add`: Add a task to the list
- `todo__done`: Mark a task complete
- `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,
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:
| Command | Description |
|----------------------|------------------------------------------------------------|
| `.agent` | Use an agent |
| `.starter` | Display and use conversation starters for the active agent |
| `.edit agent-config` | Open the agent configuration in your preferred text editor |
| `.info agent` | Display information about the active agent |
| `.exit agent` | Leave the active agent |
| Command | Description |
|----------------------|-----------------------------------------------------------------------------------------------|
| `.agent` | Use an agent |
| `.starter` | Display and use conversation starters for the active agent |
| `.clear todo` | Clear the todo list and stop auto-continuation (requires `auto_continue: true` on the agent) |
| `.edit agent-config` | Open the agent configuration in your preferred text editor |
| `.info agent` | Display information about the active agent |
| `.exit agent` | Leave the active agent |
![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
### `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
When `auto_continue` is enabled, Loki automatically sends a continuation prompt if:
+1 -1
View File
@@ -85,7 +85,7 @@ async fn prepare_chat_completions(
let ready = oauth::prepare_oauth_access_token(client, &provider, self_.name()).await?;
if !ready {
bail!(
"OAuth configured but no tokens found for '{}'. Run: loki --authenticate {}",
"OAuth configured but no tokens found for '{}'. Run: 'loki --authenticate {}' or '.authenticate' in the REPL",
self_.name(),
self_.name()
);
+2 -2
View File
@@ -111,7 +111,7 @@ async fn prepare_chat_completions(
let ready = oauth::prepare_oauth_access_token(client, &provider, self_.name()).await?;
if !ready {
bail!(
"OAuth configured but no tokens found for '{}'. Run: loki --authenticate {}",
"OAuth configured but no tokens found for '{}'. Run: 'loki --authenticate {}' or '.authenticate' in the REPL",
self_.name(),
self_.name()
);
@@ -181,7 +181,7 @@ async fn prepare_embeddings(
let ready = oauth::prepare_oauth_access_token(client, &provider, self_.name()).await?;
if !ready {
bail!(
"OAuth configured but no tokens found for '{}'. Run: loki --authenticate {}",
"OAuth configured but no tokens found for '{}'. Run: 'loki --authenticate {}' or '.authenticate' in the REPL",
self_.name(),
self_.name()
);
+5
View File
@@ -476,6 +476,11 @@ impl Agent {
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 {
self.config.continuation_prompt.clone().unwrap_or_else(|| {
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__done`: Mark a task done by id. Call this immediately after completing each step.
- `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:
- Always create a todo list before starting work.
- 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."
};
+20
View File
@@ -67,6 +67,11 @@ impl TodoList {
self.todos.is_empty()
}
pub fn clear(&mut self) {
self.goal.clear();
self.todos.clear();
}
pub fn render_for_model(&self) -> String {
let mut lines = Vec::new();
if !self.goal.is_empty() {
@@ -149,6 +154,21 @@ mod tests {
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]
fn test_serialization_roundtrip() {
let mut list = TodoList::new("Roundtrip");
+21
View File
@@ -76,6 +76,16 @@ pub fn todo_function_declarations() -> Vec<FunctionDeclaration> {
},
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"),
}
}
"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}"),
}
}
+1 -1
View File
@@ -181,7 +181,7 @@ fn handle_direct_input(args: &Value) -> Result<Value> {
.and_then(Value::as_str)
.ok_or_else(|| anyhow!("'question' is required"))?;
let answer = Text::new(question).prompt()?;
let answer = Text::new(&format!("{question}\nYour answer: ")).prompt()?;
Ok(json!({ "answer": answer }))
}
+25 -1
View File
@@ -33,7 +33,7 @@ use std::{env, mem, process};
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(".info", "Show system info", AssertState::pass()),
@@ -137,6 +137,11 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 38]> = LazyLock::new(|| {
"Leave agent",
AssertState::True(StateFlags::AGENT),
),
ReplCommand::new(
".clear todo",
"Clear the todo list and stop auto-continuation",
AssertState::True(StateFlags::AGENT),
),
ReplCommand::new(
".rag",
"Initialize or access RAG",
@@ -804,6 +809,25 @@ pub async fn run_repl_command(
Some("messages") => {
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()?,
},
".vault" => match split_first_arg(args) {