Skip to content
Changelog

Accepted Company Tasks run on the durable queue and come back for review

ContributorAshpreet BediFounder & CEO

October 16, 20261 min read

Agno's POST /company/tasks/{id}/runs hands an accepted CompanyOS Task to its executor's ordinary run route on the durable queue and answers 202 with a run ID built from the Task ID and attempt number. Send attempt 1 twice and both calls get the same run. The executor receives one JSON message holding the Task brief, its evidence and the current company and Product wiki pages.

import json
 
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.job_queue import QueueConfig
from agno.os import AgentOS, CompanyConfig
from agno.workflow import Step, StepInput, StepOutput, Workflow
 
db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")
 
 
def draft(step_input: StepInput) -> StepOutput:
    task = json.loads(step_input.input)  # keys: brief, evidence, wiki
    return StepOutput(content="Draft ready for review: " + task["brief"])
 
 
agent_os = AgentOS(
    db=db,
    workflows=[Workflow(id="draft", db=db, steps=[Step(name="Draft", executor=draft)])],
    agents=[Agent(id="site-builder", db=db)],
    company=CompanyConfig(),
    queue=QueueConfig(durable=True, db=db),
)

We ran this setup on an embedded PostgreSQL with max_attempts=1 and one accepted Task per executor. The draft step finished its Task. The agent had no model key in our sandbox, so its run failed.

# real output: two accepted Tasks, one run each
POST /company/tasks/{id}/runs attempt 1 -> 202 {"status": "PENDING"}
repeat attempt 1 -> 202, same run_id: True
workflow Task -> in_review, result COMPLETED
agent Task    -> in_progress, result ERROR "OPENAI_API_KEY not set. ..."
GET /company/attention -> {"proposals": 1, "in_review": 1, "failed": 1}

A successful run moves the Task to in_review for a person, and the run stays readable at GET /company/tasks/{id}/runs. A run that exhausts its retries, fails permanently or gets cancelled leaves the Task in_progress with the result attached, ready for attempt 2 or a return to Backlog. GET /company/attention counts Backlog proposals, Tasks in review and those failures, and GET /company/tasks?failed=true lists the failures.

Archiving cascades. DELETE /company/products/{id} archives the Product's Projects and Tasks, a Project archive takes its Tasks, and each child records archived_by in its transition history so a restore brings back exactly those children. A parent with Ready, In progress or In review work refuses.

# real output: the same Product, before and after cancelling both Tasks
DELETE /company/products/{id} -> 409 {"detail": {"message": "Cannot archive with active Tasks", "tasks": [{"id": "6280...", "title": "Rebuild docs site"}, {"id": "af6f...", "title": "Draft release notes"}]}}
DELETE /company/products/{id} -> 204
POST /company/products/{id}/restore -> 200, visible Tasks: 3

Product and Project responses also carry counts, the open Projects and Tasks per status, computed in one grouped query for the whole page.

Three fixes round out the release. The standalone Product Manager now answers at /agents/product-manager-internal. Optional Company tool arguments advertise a nullable schema, and an empty string from a model counts as omitted. AsyncPostgresDb derives its default ID from the connection URL and schema, matching PostgresDb, so a Task's saved run database reference resolves after a restart or on another replica.

# real output: two AsyncPostgresDb instances on the same URL and schema
earlier releases: 0dbba9e9-... vs 3a725f2b-...   equal: False
this release:     f1e71e62-... vs f1e71e62-...   equal: True

See the cookbook, and learn more about durable background execution in the documentation.

Frequently asked questions

A JSON message with three keys, brief, wiki and evidence. It holds the Task brief, the readable company and Product wiki pages, and the evidence attached to the Task. Queue bookkeeping stays out of the model input.

Once the queue has no retries left, the Task stays in_progress with an ERROR or CANCELLED result, and GET /company/attention counts it under failed. Start a new numbered attempt or return the Task to Backlog to replan it.

A Product or Project with Ready, In progress or In review Tasks refuses to archive. The response lists each blocking Task id and title in detail.tasks. Finish or cancel that work, then archive again.

company=CompanyConfig() and queue=QueueConfig(durable=True), plus an executor with a run database. Engineering executors and framework agents such as Product Manager and OS Manager cannot be started as Task executors.

Shipped around the same time