Background AI system
TOW runs AI work through configurable Agents and durable Agent Runs.
Runtime shape
The backend starts background AI work from the FastAPI lifespan in backend/app/main.py.
- Built-in Agents are seeded or refreshed.
agent_run_queue.start()starts Agent Run workers.- If
operating_background_enabledis true,OperatingBackgroundRunner.run_forever()checks scheduled operating work. - User actions such as check-ins, doc edits, chat turns, snapshots, roadmap generation, and ticket scans enqueue Agent Runs.
- Workers load the Agent's published version and run the generic Agent runner. Built-in platform workflows can delegate to deterministic service handlers after the Agent gate is resolved.
Agent Runs are database records, so queued, running, completed, and failed status is auditable across backend restarts.
Status flow
Frontend status comes from GET /api/agent-runs. The frontend polls active queued and running runs for the signed-in user and shows recent failure counts.
Item responses that start background work include agent_runs for immediate UI indicators.
Agent gating
Agent availability resolves through server, organisation, project, and user bindings. Any disabled binding disables the Agent. Schedules come from the most specific configured binding, then the Agent version trigger defaults.
Direct AI endpoints check the effective Agent before queuing work. Mixed actions save non-AI data when appropriate and skip the disabled AI follow-up.
See Agent configuration for the admin-facing model and built-in Agent catalog.
Agent Run catalog
| Agent key | Typical trigger | Target | Output |
|---|---|---|---|
proactive_daily_review | Scheduled daily review and opening Today, only while the AI brief is enabled. | daily_session | Updates proactive-review sessions and Inbox proposals. |
operating_pulse | Scheduled interval pulse and opening or refreshing Today, only while the AI brief is enabled. | daily_session | Updates Today pulse context. |
today_checkin_processor | Project Check-In submitted. | daily_session | Coaching, memory diffs, ticket automation, and proposals. |
roadmap_proposal_generator | Roadmap generation request and scheduled daily suggestions for recently active organisations. | daily_session | Roadmap and epic proposals. |
docs_memory_reviewer | Doc create/update/archive/manual extract. | doc | Doc indexing, references, embeddings, and memory diffs. |
snapshot_generator | Snapshot generation request. | snapshot | Snapshot draft for review. |
ticket_conflict_scanner | Manual ticket conflict scan and scheduled daily scan for recently seen users. | ticket_conflicts, daily_session | Duplicate/conflict proposals and nightly conflict scan summaries. |
chat_memory_reviewer | Chat turn completed. | raw_event | Pending memory diffs. |
AI brief: review and pulse
The proactive daily review and the operating pulse feed the AI brief on Today. Both are off by default. While features.ai_brief_enabled is false, TOW generates neither: the background runner does not schedule them, opening or refreshing Today queues nothing, runs that were already queued finish as skipped without calling the model, and the AI brief widget is hidden from dashboards. A hidden widget stays in the saved dashboard layout and returns in place when the feature is enabled.
When ai_brief_enabled is true:
- Opening Today queues whichever of the two is due for that user and scope.
- Each background tick also queues a due review and pulse, for the organisation and for every active project, for users seen recently (see below).
Every pulse is an AI call per user and per scope on each interval, so the cost grows with the number of recently active users and their projects.
Scheduled AI work follows recent activity
The background runner queues scheduled AI work only where someone has been seen in a browser session within operating_background_user_activity_window_days:
- The nightly ticket conflict scan, and the review and pulse while the AI brief is enabled, are queued only for users seen within the window.
- Scheduled roadmap suggestions are queued only for organisations that have at least one active member seen within the window.
Dormant accounts and organisations are skipped, and the per-tick user limit is spent on recently seen users. Goal reminders, the clean-up of old roadmap suggestions, and scheduled automation flows are not AI work and run regardless. Anything a person starts themselves, such as a manual conflict scan or roadmap generation, is unaffected.
Today reads only the current day's pulse. Hourly pulse sessions and their Agent Runs older than operating_pulse_retention_days are deleted by the background runner, whether or not the AI brief is enabled. Each tick deletes at most 5,000 of each and stops early after about 20 seconds, so an existing backlog drains over many ticks. A pulse that still has a queued or running Agent Run is never deleted. PostgreSQL reuses the freed space but does not return it to the operating system without a VACUUM FULL.
Operational controls
| Setting | Purpose |
|---|---|
operating_background_enabled | Enables or disables the periodic operating background runner. |
operating_background_interval_seconds | Controls how often the background runner scans for scheduled Agent work. |
features.ai_brief_enabled | Turns on the proactive daily review, the operating pulse, and the AI brief widget. Off by default. |
operating_background_user_activity_window_days | How recently a user must have been seen in a browser session for the background runner to queue scheduled AI work for them or their organisation. Default 7. |
operating_pulse_retention_days | Days of hourly pulse sessions and their Agent Runs to keep. Default 30; 0 keeps everything. |
ticket_conflict_scan_hour_utc | First UTC hour when the nightly conflict scan may be queued. |
ai_queue_max_concurrency | Maximum number of Agent Runs processed concurrently. |
ai_queue_status_retention_seconds | Status retention window used by polling clients. |
OPENAI_API_KEY | Required for OpenAI Responses and embedding calls. |
openai_reasoning_model | Default reasoning model for Agent work. |
openai_fast_model | Default model for structured JSON helper calls. |
openai_title_model | Optional model used for generated chat titles. |
openai_onboarding_model | Optional model used for website scan extraction during organisation onboarding. |
openai_embedding_model | Embedding model used for semantic retrieval and doc indexing. |
max_tool_rounds | Maximum tool-call rounds per AI response before the model must answer from available tool results. |
chat_context_window_tokens | Optional total chat token budget; defaults to the reasoning model's known context window. |
chat_max_output_tokens | Optional per-answer reserve and provider output cap; defaults to the reasoning model's known maximum and scales down with a smaller configured context window. |
EXA_API_KEY | Enables configured web search and scrape tools. |
Troubleshooting
| Symptom | What to check |
|---|---|
| Runs stay queued | Confirm the backend lifespan started and Agent Run workers are running. Check ai_queue_max_concurrency. |
| A run failed | Check /api/agent-runs, backend logs, and the persisted agent_runs.error value. |
| AI action is disabled | Check the effective Agent row in Server, Organisation, Project, or User Settings. |
| Expected output is missing | Check Agent Runs, Inbox, Memory Diffs, Today, snapshots, tickets, or roadmap proposals. |
| Nightly scans or roadmap suggestions never run | Confirm background mode, user membership, UTC hour, the Agent's effective daily schedule, and that the user (or a member of the organisation) was seen within the activity window. |
| No AI brief, pulse, or proactive review | Expected while features.ai_brief_enabled is false. When it is true, the background runner still skips users not seen within the activity window; opening Today prepares both for them. |