Todo Plugin

Plan registration and progress tracking. Enables the model to create execution plans, report progress on steps, and provide visibility into task completion.

Name todo
Type Tool Plugin (Registry-managed)
Tools Provided Core: createPlan, startPlan, setStepStatus, getPlanStatus, completePlan, addStep
Cross-Agent: subscribeToTasks, addDependentStep, completeStepWithOutput, getBlockedSteps, getTaskEvents, listSubscriptions, unsubscribe
User Commands plan
Auto-approved All except startPlan

Workflow

  1. createPlan: Register an execution plan with ordered steps
  2. startPlan: Request user approval (requires permission)
  3. setStepStatus: Report progress on each step
  4. addStep: Add new steps if needed during execution
  5. completePlan: Mark plan as finished
startPlan Requires Approval
The startPlan tool requires user permission because it commits the model to executing a plan. All other todo tools are auto-approved since they're purely informational.
Basic usage
from jaato import PluginRegistry

registry = PluginRegistry()
registry.discover()

# Expose todo plugin with defaults
registry.expose_tool('todo')

# With custom configuration
registry.expose_tool('todo', config={
    'reporter_type': 'console',
    'storage_type': 'file',
    'storage_path': '.jaato/plans.json',
})
Model workflow example
# 1. Create the plan
createPlan(
    title="Refactor auth module",
    steps=[
        "Review current auth implementation",
        "Extract token validation to separate module",
        "Add unit tests for new module",
        "Update imports in dependent files"
    ]
)

# 2. Request user approval
startPlan(message="Ready to proceed?")

# 3. Report progress on each step
setStepStatus(step_id="step_1", status="completed")
setStepStatus(step_id="step_2", status="in_progress")

# 4. Complete the plan
completePlan(status="completed", summary="Refactoring done")

Configuration Parameters

reporter_type

How to report plan progress to the user.

Type"console" | "webhook" | "file"
Default"console"

reporter_config

Configuration for the selected reporter.

TypeDict[str, Any]
Default{}

storage_type

How to persist plan data.

Type"memory" | "file" | "hybrid"
Default"memory"

storage_path

Path for file-based storage.

Typestr
Default".jaato/plans.json"

config_path

Path to todo.json configuration file.

Typestr
Default".jaato/todo.json"
Configuration examples
# Console reporter with file storage
registry.expose_tool('todo', config={
    'reporter_type': 'console',
    'storage_type': 'file',
    'storage_path': '.jaato/plans.json',
})

# Webhook reporter for remote monitoring
registry.expose_tool('todo', config={
    'reporter_type': 'webhook',
    'reporter_config': {
        'url': 'https://api.example.com/plans',
        'headers': {'Authorization': 'Bearer token'},
    },
})

# File reporter for log-based tracking
registry.expose_tool('todo', config={
    'reporter_type': 'file',
    'reporter_config': {
        'path': '/var/log/jaato/plans.log',
    },
})
todo.json config file
{
  "reporter_type": "console",
  "storage_type": "file",
  "storage_path": ".jaato/plans.json"
}

Tool Reference

createPlan

Register a new execution plan with ordered steps.

ParameterTypeRequired
titlestringYes
stepsstring[]Yes

startPlan

Request user approval to begin execution. Requires permission.

ParameterTypeRequired
messagestringNo

setStepStatus

Report progress on a specific step.

ParameterTypeRequired
step_idstringYes
status"in_progress" | "completed" | "failed" | "skipped"Yes
resultstringNo
errorstringNo

addStep

Add a new step during execution.

ParameterTypeRequired
descriptionstringYes
after_step_idstringNo

completePlan

Mark the plan as finished.

ParameterTypeRequired
status"completed" | "failed" | "cancelled"Yes
summarystringNo
createPlan response
{
  "plan_id": "plan_abc123",
  "title": "Refactor auth module",
  "status": "pending",
  "steps": [
    {"id": "step_1", "description": "Review current auth...", "status": "pending"},
    {"id": "step_2", "description": "Extract token...", "status": "pending"},
    {"id": "step_3", "description": "Add unit tests...", "status": "pending"},
    {"id": "step_4", "description": "Update imports...", "status": "pending"}
  ],
  "message": "Plan created. Call startPlan to request approval."
}
getPlanStatus response
{
  "plan_id": "plan_abc123",
  "title": "Refactor auth module",
  "status": "in_progress",
  "progress": {
    "completed": 2,
    "total": 4,
    "percentage": 50
  },
  "steps": [
    {"id": "step_1", "status": "completed", "result": "..."},
    {"id": "step_2", "status": "completed", "result": "..."},
    {"id": "step_3", "status": "in_progress"},
    {"id": "step_4", "status": "pending"}
  ]
}

User Command

plan

Show current or most recent plan status. This command can be invoked directly by the user without model mediation.

Commandplan
share_with_modelFalse
Auto-approvedYes

The output is NOT shared with the model since it's purely for user visibility into progress. This keeps the conversation focused while allowing users to check plan status at any time.

User command example
> plan

Current Plan: Refactor auth module
Status: in_progress

Steps:
  [x] step_1: Review current auth implementation
  [x] step_2: Extract token validation to separate module
  [>] step_3: Add unit tests for new module
  [ ] step_4: Update imports in dependent files

Progress: 2/4 (50%)

Step Status Rules

The model is instructed to be honest about step outcomes:

StatusWhen to Use
completed Step was FULLY accomplished with expected outcome
failed Could NOT achieve the step's goal for ANY reason
skipped Step became unnecessary or was intentionally bypassed
in_progress Currently working on this step
blocked Waiting for dependencies from other agents (auto-unblocks when resolved)
Honest Reporting
The model is instructed: "If you say 'could not', 'unable to', or similar in the result, the status MUST be 'failed'. Being honest about failures is more valuable than false completion reports."
Status examples
# Correct: Step fully achieved
setStepStatus(
    step_id="step_1",
    status="completed",
    result="Found 3 auth-related files: auth.py, tokens.py, session.py"
)

# Correct: Step failed
setStepStatus(
    step_id="step_2",
    status="failed",
    error="Could not find token validation logic in expected location"
)

# Correct: Step became unnecessary
setStepStatus(
    step_id="step_3",
    status="skipped",
    result="Tests already exist, no need to add more"
)

# WRONG: Marking partial success as completed
setStepStatus(
    step_id="step_4",
    status="completed",  # Should be "failed"!
    result="Unable to update 2 of 5 imports due to circular dependencies"
)

Cross-Agent Collaboration

The TODO plugin supports coordination between parent agents and subagents through an event-driven architecture with automatic dependency resolution.

Auto-Unblock Mechanism

When a parent agent adds a dependent step:

  1. Registration: addDependentStep registers the dependency with the event bus
  2. Blocking: The step is created with status blocked
  3. Completion: When the subagent calls completeStepWithOutput, a step_completed event is published
  4. Resolution: The event bus automatically resolves the dependency
  5. Unblocking: The parent's step transitions from blockedpending
  6. Output Delivery: The subagent's output is stored in received_outputs
No Polling Required
Steps auto-unblock when all dependencies complete. The parent agent doesn't need to poll - just wait for the step to become ready.

Key Concepts

ConceptDescription
blocked statusStep waiting for dependencies from other agents
depends_onList of {agent_id, step_id} references this step waits for
blocked_byUnresolved dependencies (auto-updated as they complete)
received_outputsMap of outputs from completed dependencies
Parent-Subagent coordination workflow
# === PARENT AGENT ===

# 1. Subscribe to events BEFORE spawning subagents
subscribeToTasks(
    event_types=['plan_created', 'step_completed']
)

# 2. Spawn subagent (subagent creates its own plan)
# ... spawn_subagent('implementer', task="...")

# 3. When you see plan_created event, add dependent step
addDependentStep(
    description="Review implementation results",
    depends_on=[
        {"agent_id": "implementer", "step_id": "final_step"}
    ]
)
# → Step created as BLOCKED

# 4. Wait... step auto-unblocks when subagent completes

# 5. Check received outputs
status = getPlanStatus()
# status.received_outputs = {'implementer': {...}}
Subagent completing with output
# === SUBAGENT ===

# Complete the step WITH structured output
completeStepWithOutput(
    step_id="final_step",
    output={
        "passed": True,
        "results": ["file1.py created", "tests pass"],
        "errors": []
    },
    result="Implementation completed successfully"
)
# → Parent's dependent step auto-unblocks
# → Output delivered to parent's received_outputs

Cross-Agent Tool Reference

subscribeToTasks

Subscribe to task events from other agents. Call this BEFORE spawning subagents.

ParameterTypeRequired
agent_idstringNo (use * for any)
event_typesstring[]Yes
plan_idstringNo
step_idstringNo

Event types: plan_created, plan_started, plan_completed, plan_failed, plan_cancelled, step_added, step_started, step_completed, step_failed, step_skipped, step_blocked, step_unblocked

addDependentStep

Add a step that waits for tasks from other agents. Auto-unblocks when all dependencies complete.

ParameterTypeRequired
descriptionstringYes
depends_on{agent_id, step_id}[]Yes
after_step_idstringNo
providesstringNo

completeStepWithOutput

Complete a step AND pass structured data to dependent tasks. Use this for subagent final steps.

ParameterTypeRequired
step_idstringYes
outputobjectYes
resultstringNo

getBlockedSteps

See which steps are waiting on other agents.

ParameterTypeRequired
plan_idstringNo

getTaskEvents

Review recent cross-agent activity.

ParameterTypeRequired
agent_idstringNo
event_typesstring[]No
limitnumberNo (default: 20)
addDependentStep response
{
  "step_id": "step_abc123",
  "sequence": 2,
  "description": "Review implementation results",
  "status": "blocked",
  "blocked_by": [
    {"agent_id": "implementer", "step_id": "final_step"}
  ],
  "message": "Step added with 1 dependencies (BLOCKED)"
}
getBlockedSteps response
{
  "blocked_steps": [
    {
      "step_id": "step_abc123",
      "description": "Review implementation results",
      "blocked_by": [
        {"agent_id": "implementer", "step_id": "final_step"}
      ],
      "received_outputs": {}
    }
  ],
  "count": 1
}
After dependency resolves
{
  "blocked_steps": [],
  "message": "No blocked steps"
}

// Step status in getPlanStatus:
{
  "step_id": "step_abc123",
  "status": "pending",  // Was "blocked", now ready
  "received_outputs": {
    "implementer:final_step": {
      "passed": true,
      "results": ["file1.py created"]
    }
  }
}