Lesson 11 · Collaboration

Agents that find their own work

"The agent finds work itself." No human dispatcher needed - it pulls tasks from the board on its own.

⏱ ~12 min · 📝 3 interactive widgets · 🧑‍💻 Based on shareAI-lab · s11_autonomous_agents.py

From 'being awakened' to 'finding work'

s09/s10 teammates are reactive: someone has to send them a message before they start; they finish and go idle, waiting to be called again.

s11's autonomous teammate is proactive: even when idle it doesn't truly stop. It scans .tasks/ every 5 seconds, and when it spots an unclaimed task it calls claim_task and gets to work.

Teammate lifecycle:
  +-------+
  | spawn |
  +---+---+
      |
      v
  +-------+
  | WORK  | <-- LLM tool_use loop
  +---+---+
      |
      | stop_reason != tool_use OR tool_use("idle")
      v
  +-------+
  | IDLE  | poll every 5s, up to 12x = 60s timeout
  +---+---+
      |
      +--> inbox has new messages? -> resume WORK
      |
      +--> scan .tasks/, find unclaimed task -> claim -> resume WORK
      |
      +--> 60s with nothing -> status = shutdown, thread exits

Auto-claim: pulling tasks off the board

The core function:

def scan_unclaimed_tasks() -> list:
    unclaimed = []
    for f in sorted(TASKS_DIR.glob("task_*.json")):
        task = json.loads(f.read_text())
        if (task.get("status") == "pending"
                and not task.get("owner")
                and not task.get("blockedBy")):
            unclaimed.append(task)
    return unclaimed

Three filters = pending + no owner + no blockedBy. All three are required.

The claim action itself holds a lock (_claim_lock) to prevent two agents simultaneously spotting the same unclaimed task and both trying to grab it. Inside the lock: re-read, check owner is still empty, then write in_progress + owner to disk.

Simulate multiple agents scanning the task board

3 teammates in idle state, 3 tasks waiting. Click "Next poll" and see who claims what first.

Identity reinjection: knows who it is after compaction

s06 showed that auto_compact replaces messages with a summary. An autonomous agent running for an hour easily triggers this - after compaction the model doesn't know its own name or role.

s11's fix: when claiming a new task, if len(messages) <= 3 (indicating recent compaction), automatically prepend an identity block:

if len(messages) <= 3:
    messages.insert(0, {
        "role": "user",
        "content": f"<identity>You are 'alice', role: coder, team: my-team. Continue your work.</identity>",
    })
    messages.insert(1, {"role":"assistant", "content": f"I am alice. Continuing."})

This pattern is called identity reinjection - a synthetically constructed exchange that tells the model who it is, restoring its sense of role.

Interactive

Widget 1 · Task Scanner · 3 agents competing for 3 tasks

alice / bob / charlie are all idle. Click the poll button and watch who claims which task first (alphabetical order).

.tasks/ directory
Teammate status
All idle, waiting to scan
Interactive

Widget 2 · Identity Drift · does the agent remember who it is after compaction?

Simulate the before/after of context compaction and compare how the agent responds to "who are you?" with and without identity reinjection.

Without identity reinjection
With identity reinjection
Interactive

Widget 3 · Autonomous vs Assisted · which roles suit autonomy?

Decide whether each role should run autonomously or remain human-assisted. Think about "cost of runaway" and "required intervention frequency".

Correct: 0 / 5