4.9 KiB
Bounded research delivery
New general research tasks should use task_type: "research" on
delegate_task or spawn_agent. The coordinator is instructed to select this
for search-and-guide requests. Omitted task types remain standard, including
old saved tasks. Existing research is not migrated or restarted.
Delivery
The runtime stores research state with the task, including its start time, source reservations, publications and source results.
- Initial collection stops at 120 seconds, two searches or six page reservations. Limits are checked before starting a new source call and on each model round; already running calls may finish.
publish_researchsends a first guide with summary, actionable steps, observed source URLs and unknowns. It can declare zero to two material gaps.- Each gap permits one additional search and two pages. Every supplement source
call needs its zero-based
gapindex. Supplement collection also stops after 120 seconds. - A second publication contains only additions and remaining unknowns. It cannot introduce new gaps. An empty gap list completes the task immediately.
- Opening acknowledgements and meaningful progress messages remain available. They do not count as a first guide.
- Synthesis gets at most three model rounds after collection closes. Failure to publish then ends the task with an explicit error and retains existing evidence and publications, rather than resuming exploration.
- Other execution/delegation tools cannot bypass research limits. Browser navigation counts as a page; browser snapshots and page reading are available during collection for HTTP-blocked sources. Shell/MCP-based research requires a standard task.
The 2–3 minute first-guide target is a goal, not a deadline for external model responses. Network source calls have a 30 second timeout; model response and queue delays are measured separately.
Reuse and persistence
Within a research task, identical search queries and canonical URLs reuse the saved result, including errors and timeouts. Different URLs can run concurrently; concurrent requests for the same URL share one execution. URL fragments are ignored; query parameters remain significant. Cached access does not spend a new reservation. Failed source attempts spend their original reservation.
Large tool results include output_id. Use read_tool_output with that ID,
offset (default 0) and limit (default 6000, maximum 12000). Offsets count
Unicode characters. The reader is scoped to the current task/session and
rejects traversal or another task's output. It does not access the Docker
filesystem.
Publications are stored independently of foreground conversation checkpoints, restored into chat history and made available to subsequent foreground turns. Completed research does not enqueue a redundant coordinator summary.
Diagnostics and validation
Persisted timing events carry task/agent association, timestamp and elapsed
milliseconds. Model queue and response records share a round identifier; source
and tool records carry the task's request number, with call IDs on tool records.
Stages include worker_model_queue, foreground_model_queue, model_response,
model_retry_backoff, tool_execution, research_source,
research_first_delivery, and research_final_delivery.
Retry-backoff values are scheduled wait durations. No prompts, arguments or
credentials are stored in timing events. LAZYBOY_TIMING=1 additionally prints
latency logs.
Offline checks:
cargo test -p lazyboy-core --lib
cargo build -p lazyboy
python3 tests/research_flow.py
The integration fixture makes no paid model calls and is not evidence of live provider latency. After restarting the local backend, use new ordinary research requests to measure actual first-guide latency from the recorded events.
Accurate work indicators
GET /api/agents/:id/activity (daemon RPC activity) reports
idle | queued | running | waiting_input, active task IDs and a pending question.
Roster and agent detail use the same computation. A saved running task is
active only while the service owns a live worker. Completing one task does not
hide another active task; a human question by itself is not active computation.
The frontend reconciles on lifecycle events and every two seconds, independently of streamed progress text. Only running activity animates; queued work has a static label. The old OR of typing, stale detail, stale roster and status text is removed. Status requests time out after eight seconds; connection failure shows an unknown/reconnecting message instead of an endless working animation. Late responses from another agent or before a newly sent message are discarded.
Browser regression (with the local Vite UI running):
node tests/activity_ui.mjs. This uses an isolated headless browser and mocked
API responses to verify running, idle, queued, waiting-input, disconnect and
reconnect, including stale progress events and roster data.