Files
didactyl/plans/agent_tasks.md
T

216 lines
6.9 KiB
Markdown

# Agent Tasks: Short-Term Memory via Context-Injected Task List
## Summary
Add a **tasks** system that serves as the agent's short-term working memory. The agent can break down goals into steps, track progress, and see its current task list in every prompt context. Tasks are file-backed (not stored on Nostr) and managed via a dedicated `task_manage` tool.
## How It Works
```mermaid
flowchart TD
A[User sends message] --> B[Context builder runs]
B --> C[Template resolver hits tasks_content variable]
C --> D[Read tasks.json from disk]
D --> E{Tasks exist?}
E -->|Yes| F[Format tasks as readable text]
E -->|No| G[Return empty string - section skipped]
F --> H[Inject as system message in prompt]
G --> H
H --> I[LLM sees current tasks in context]
I --> J{LLM decides to update tasks?}
J -->|Yes| K[LLM calls task_manage tool]
K --> L[Tool updates tasks.json on disk]
L --> M[Tool result returned to LLM]
J -->|No| N[LLM responds normally]
```
## Design
### Storage: `tasks.json`
A simple JSON file in the agent's working directory. Structure:
```json
{
"tasks": [
{
"id": 1,
"text": "Query admin relay list to find active relays",
"status": "done",
"created_at": 1709535600,
"updated_at": 1709535660
},
{
"id": 2,
"text": "Draft long-form article about Nostr relay setup",
"status": "active",
"created_at": 1709535600,
"updated_at": 1709535600
},
{
"id": 3,
"text": "Publish article as kind 30023",
"status": "pending",
"created_at": 1709535600,
"updated_at": 1709535600
}
],
"next_id": 4
}
```
Task statuses: `pending`, `active`, `done`
### Tool: `task_manage`
A single tool with an `action` parameter that covers all operations:
| Action | Parameters | Description |
|--------|-----------|-------------|
| `list` | *(none)* | Return all tasks with status |
| `add` | `text`, optional `status` | Add a new task, default status `pending` |
| `update` | `id`, optional `text`, optional `status` | Update text and/or status of a task |
| `remove` | `id` | Remove a task by ID |
| `clear` | optional `status` | Remove all tasks, or all with a given status |
| `replace` | `tasks` (array of text strings) | Replace entire task list with new items |
The `replace` action is important — it lets the LLM rewrite the whole plan in one call rather than doing add/remove/update one at a time. This is the most common pattern: the agent works out a plan and writes all steps at once.
**Tool schema:**
```json
{
"name": "task_manage",
"description": "Manage the agent task list - short-term working memory for tracking steps in a plan. Tasks appear in your context on every message.",
"parameters": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["list", "add", "update", "remove", "clear", "replace"]
},
"text": { "type": "string" },
"id": { "type": "integer" },
"status": { "type": "string", "enum": ["pending", "active", "done"] },
"tasks": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["action"]
}
}
```
### Context Section: `agent_tasks`
New section in the context template, placed after `adopted_skills` and before `dm_history`:
```yaml
- section: agent_tasks
role: system
skip_if_empty: true
content: |
{{tasks_content}}
```
### Template Variable: `{{tasks_content}}`
New resolver in `agent_template_resolve_var()` that:
1. Reads `tasks.json` from the working directory
2. Parses the JSON
3. Formats active/pending tasks as readable text
4. Returns empty string if no tasks exist (section gets skipped via `skip_if_empty`)
**Rendered format in context:**
```
### Current Tasks
Your active task list - short-term working memory for tracking plan steps.
- [x] 1. Query admin relay list to find active relays
- [-] 2. Draft long-form article about Nostr relay setup
- [ ] 3. Publish article as kind 30023
```
Legend: `[x]` = done, `[-]` = active, `[ ]` = pending
Done tasks are included so the agent has continuity about what it already accomplished, but they could be pruned after a configurable count or age to save tokens.
### System Prompt Addition
Add to the agent's behavioral rules in the soul/system prompt:
```
### Task Management
- You have a task list that serves as your short-term working memory.
- When working on multi-step goals, use task_manage to track your plan.
- Update task status as you complete steps.
- Your current tasks appear in your context automatically.
```
## Implementation Steps
### 1. Add `task_manage` tool implementation in `tools.c`
- New `execute_task_manage()` function
- Reads/writes `tasks.json` in the working directory (uses `build_tool_path` for sandboxing)
- Handles all 6 actions: list, add, update, remove, clear, replace
- Returns JSON result with success/failure and current task list
### 2. Register `task_manage` tool schema in `tools_build_openai_schema_json()`
- Add tool definition (t35 or next available) with the schema above
### 3. Wire `task_manage` into `tools_execute()` dispatch
- Add `strcmp(tool_name, "task_manage")` branch calling `execute_task_manage()`
### 4. Add `{{tasks_content}}` template variable resolver in `agent.c`
- New `build_tasks_content_string()` function
- Reads `tasks.json`, formats as markdown checklist
- Add to `agent_template_resolve_var()` for var name `tasks_content`
### 5. Add `agent_tasks` section to context template
- Add the new section in `context_template.md`
- Place after `adopted_skills`, before `dm_history`
- Use `skip_if_empty: true` so it costs zero tokens when no tasks exist
### 6. Add section detection for context logging
- Add `agent_tasks` detection in `detect_context_section()` in `agent.c`
### 7. Add task management guidance to system prompt
- Brief behavioral instruction so the agent knows when/how to use the task list
## Token Budget Considerations
- Empty task list: **0 tokens** (skipped via `skip_if_empty`)
- Typical 5-task plan: **~80-120 tokens**
- Maximum reasonable list of 15 tasks: **~250-350 tokens**
- Consider pruning done tasks older than N turns or keeping only the last M done tasks
## Future: User-Facing To-Do List (Nostr)
This is explicitly **not** the user-facing to-do list. That future feature would:
- Store items as Nostr events (likely a NIP-51 style list or custom kind)
- Be visible to the user via Nostr clients
- Have its own separate tool (`todo_manage` or similar)
- Potentially reference agent tasks that graduate to user-visible items
The agent tasks system is purely internal working memory.
## Files Modified
| File | Change |
|------|--------|
| `src/tools.c` | Add `execute_task_manage()`, tool schema, dispatch entry |
| `src/agent.c` | Add `build_tasks_content_string()`, resolver entry, section detection |
| `context_template.md` | Add `agent_tasks` section |
| Soul/system prompt (kind 31120) | Add task management behavioral guidance |