Appearance
Tasks
A task is a piece of work the agent is in the middle of across several messages — a booking being arranged, a multi-step lookup, something waiting on the user or on an external event. Tasks live in the agent_tasks table (migration 074) and are scoped to a conversation and, where known, a contact and a business, so two customers of the same agent never see each other's open work. They replaced the single per-agent ongoing_task value in agent_state (the legacy row is migrated once, on first use).
Shape
| Column | Meaning |
|---|---|
goal | the outcome, e.g. Book a table for 4 at Luigi's on Friday and confirm to the user |
status | pending, in_progress, waiting_user, waiting_external (open) · done, failed, cancelled (closed) |
steps | JSON list of { title, status: pending | in_progress | done | failed | skipped, note? } |
next_step | the next concrete action |
context | JSON scratch data collected so far (ids, dates, choices); merged on update |
wake_at | ISO time at which a waiting task is re-activated |
superseded_by | set when a newer task replaces this one |
source | who last changed it: agent, loop, auto_close, wake, user, legacy |
How the agent uses tasks
Three tools, all scoped to the current conversation:
| Tool | Effect |
|---|---|
set_ongoing_task(task, status, nextStep?, steps?, context?, wakeAt?, taskId?) | create or update a task; wakeAt only applies to the waiting_* statuses |
get_ongoing_task() | list the open tasks in this conversation |
clear_ongoing_task(outcome?, reason?, taskId?) | close the newest open task (or taskId) as done, failed or cancelled |
Every turn, the system prompt gets an Open Tasks In This Conversation block listing each open task's status, age, step checklist, next step and wake time. The block is informational: the model is told to prioritise the user's current message and continue a task only when the message relates to it.
Auto-close
After each turn, Jev judges every open task in the conversation against the latest exchange — completed? and abandoned? — and closes it at a probability ≥ 0.75 (done or cancelled, source = auto_close). The judgement is logged in the decision log as kind task_close. Where the data-flow policy withholds the partner model, tasks stay open until the agent or the user closes them.
Wake-ups
The Durable Object alarm sweep (the same one that runs schedules) flips every task whose wake_at has passed from waiting_* back to in_progress and inserts a system message into its conversation — Task wake-up: "…" is due. Next step: … — so the next turn picks it up. The event is published on the agent.schedule diagnostics channel as task_woken.
Dashboard and API
The chat page shows an open-tasks panel per conversation with steps, next step and wake time, and buttons to mark a task done or cancel it.
| Route | Purpose |
|---|---|
GET /api/agents/{id}/tasks?conversationId=&includeClosed=1 | list tasks (open first, newest first, max 30) |
PATCH /api/agents/{id}/tasks/{taskId} { status, note? } | change status from the dashboard (source = user); a closed status closes the task |
Relation to memory
Tasks are the task memory kind: they are indexed for keyword search but never vectorised, so a finished booking does not resurface semantically weeks later. Durable facts learned while doing a task belong in remember, not in the task's context.
Learning from tasks and conversations
When a task closes as done, the traces behind it (every tool call in the conversation since the task started) are queued and the alarm sweep distils them into a learned skill — a procedure with trigger phrases, the exact tools and argument shapes that worked, and the pitfalls. Skills → Learned lists them with versions and success rates; a skill is retired automatically after repeated failures.
A conversation that never had a task can be learned from on demand: POST /api/agents/:id/skills/learned/distill-conversation/:conversationId (the Learn from this conversation button on the chat page). The first user message stands in as the goal.
