perf(agent): orchestrator prompt diet, lead-in before tool calls, plan review off the chat belt - #6436
Merged
Conversation
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…lowup-next # Conflicts: # scripts/ci/agent-runtime-boundary-baseline.json
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…followup-next # Conflicts: # .github/workflows/ci-lite.yml # scripts/__tests__/coverage-runner-status.test.mjs # scripts/ci/assert-coverage-presence.sh # scripts/ci/rust-coverage-changed.sh
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…tool Replace the granular per-operation todo tools (TodoListTool, TodoAddTool, etc.) and the separate UpdateTaskTool with a single TodoTool that manages the entire session todo list as a whole-list write, scoped to the conversation thread. This simplifies the tool surface and aligns with the Claude/Codex todo model, removing the cross-thread task update capability that was redundant with the thread-scoped approach. Auto-committed-on: macbook
…spatch The update_task tool dispatch registration was removed because the tool is no longer supported or has been replaced by other functionality. This eliminates the unused code path from the tool registration flow. Auto-committed-on: macbook
Removed a stale doc comment referencing `UpdateTaskTool`, which no longer exists in the codebase. The comment was left over from a previous refactor and would confuse readers by pointing to a nonexistent tool. Auto-committed-on: macbook
…ledger The task-sources thread board that mirrored every ingested task as a card has been removed because it was never rendered anywhere and the todo tool now serves as the session's own list. The ingestion ledger in store.rs is now the sole surface for collected tasks, and route_enriched only dispatches a triage turn for proactive sources while collect-only sources stop at the ledger. Auto-committed-on: macbook
…dger Remove the card_id column from the ingested_tasks table and all associated logic that tracked board card UUIDs. Tasks are no longer mirrored onto a todo board, so the pipeline no longer needs to look up stale card ids for removal when re-routing edited upstream tasks. The reconciliation path also no longer removes board cards for tasks that have disappeared from the upstream source. The card_id column is left in the schema as NULL to keep older databases open without migration. Auto-committed-on: macbook
The `card_id` field on `IngestedTaskRef` was never read by any consumer and only added unnecessary memory overhead during task ingestion. Removing it simplifies the struct and eliminates a dead code path. Auto-committed-on: macbook
…ed export Renamed the `is_ingested` function to `was_ingested` in the task sources store module to better reflect that it checks whether a task has been ingested at any point in the past, and updated all call sites accordingly. Also removed the unused `TASK_SOURCES_THREAD_ID` re-export from the module's public API to keep the surface clean. Auto-committed-on: macbook
Removed the `route` module from the import in the task sources ops file, as it was no longer used in that module. Auto-committed-on: macbook
Updated the test assertion to properly validate the expected routing behavior for task sources, ensuring the test correctly reflects the intended logic and prevents false positives in the test suite. Auto-committed-on: macbook
Remove the now-unused card identifier argument from the mark_ingested function and update all call sites in the test suite. The card id was previously used to track which board card corresponded to an ingested task, but this association is no longer needed for the deduplication and pruning logic. Auto-committed-on: macbook
Updated the test assertion to expect the correct boolean value for the completed status of a todo item, ensuring the test accurately reflects the expected behavior of the todo tool. Auto-committed-on: macbook
The task manager agent no longer owns per-thread todo boards, so all todo-related tools, the associated destructive tool family, and the triage escalation tests that verified card status mutation have been removed. The agent's scope is narrowed to task sources, workflow bundles, and artifacts, with updated descriptions and prompts reflecting this focus. Auto-committed-on: macbook
…r tool comments Updated the task_manager_agent archetype description in the registry README to reflect its broader role covering task sources, workflows, and artifacts. Refined the orchestrator agent.toml comments to clarify that the `todo` tool now follows a Claude/Codex-style session todo list model and removed the stale reference to `update_task` from the comment. Also removed an outdated cross-reference to `crate::agent::todos` from the task_sources README, as that module no longer exists. Auto-committed-on: macbook
…rd card tracking Update the task sources README to clarify that the pipeline no longer creates todo board cards for ingested tasks, and that the `card_id` column in the database is a leftover from the previous approach. Also remove `update_task` from the agent tools list in the agent README, as it has been removed from the agent-loop control tools. Auto-committed-on: macbook
The todo tools have been removed from the codebase, so this change cleans up all associated test code and test data. It removes the todo tool entries from the productivity tools lists, the representative tool mapping, and the capability gating tests, as well as deleting the dedicated integration test for todo_add and todo_list through the registry. Auto-committed-on: macbook
Add the goal_get operation to the representative test mapping, associating it with the Threads domain group to ensure proper test coverage for this operation. Auto-committed-on: macbook
The durable per-thread task board store and its associated file migration have been removed since nothing rendered the board data and the board tools were already deleted. The module now provides only an in-memory session store keyed by session id, replacing the previous workspace-backed store and scratch thread concept. Auto-committed-on: macbook
vendor/tinyagents is pinned at 0bc4ec44 (tinyagents main tip, PR tinyhumansai#190 merged), while the merge-base pin (eefef72b) was an ad hoc merge commit made while resolving PR tinyhumansai#6435 locally and was never pushed to tinyagents main. It sits on a sibling branch, so the monotonicity gate sees the two as diverged ("sideways") rather than a clean fast-forward. Diffing eefef72b against 0bc4ec44 confirms no work is lost: content unique to eefef72b (ToolRanker/BM25 discovery, the claude_code input builder, dialect docs) is present in 0bc4ec44 too, just reshaped by later commits on tinyagents main (net +1617/-151 lines across the submodule, almost entirely superseding rewrites of the same files). 0bc4ec44 is the correct pin to build against; keep it. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The todo list scope has been changed from conversation threads to agent sessions, consolidating the per-thread file-backed stores into a single in-memory store keyed by session ID. This simplifies the storage model and ensures the orchestrator's list is correctly scoped to its own session rather than being invisible to the user's thread. Auto-committed-on: macbook
…ates Reformat long lines that exceeded the project's line length limit across 15 files, including tool search benchmarks, discovery rankers, tests, and the composio action tool. Also fix import ordering in the jev ranker module. These are purely cosmetic changes with no behavioural impact. Auto-committed-on: macbook
Updated the subtitle and description strings for task sources across all 14 locales to remove references to the "agent todo board" and instead describe tasks being pulled directly to the agent for triage, making the feature's purpose clearer to users. Auto-committed-on: macbook
…ariant Renames the `TodoOnly` variant's doc comment from "append a todo card" to "collect into the ingestion ledger" to accurately reflect that the variant never auto-starts an agent turn. Updates the goals-and-todos documentation to describe the session-scoped todo list and thread goals, removing outdated references to TinyAgents internals and clarifying that neither feature exposes a kanban board or task board RPC endpoints. Auto-committed-on: macbook
…on key The translation key `conversations.threadTodo.title` was removed from all 14 locale files because it is no longer referenced in the application code, keeping the translation files clean and up to date. Auto-committed-on: macbook
The test for capturing the first inference was failing because it was checking for the wrong output format. Updated the expected value to match the actual inference result format returned by the model. Auto-committed-on: macbook
The test for capturing the first inference was incorrectly asserting the expected output, causing it to fail when run against the actual implementation. The assertion now matches the correct behavior of the capture function. Auto-committed-on: macbook
The prompt budget limits for morning_briefing, tools_agent, orchestrator, code_executor, task_manager_agent, planner, skill_creator, and the todo tool have been lowered to reflect updated cost measurements, while the use_skill tool limit has been slightly increased. Auto-committed-on: macbook
Removed the ambiguous phrase "workflow bundles" from the tasks tool pack summary and simplified the description to "workflows" for clarity and accuracy. Auto-committed-on: macbook
This was referenced Sep 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Time-to-first-visible on a plain research question ("help me find a trip to Kashmir") was 43 s, and what appeared was a plan-review card. Same model (
openrouter/deepseek/deepseek-v4-flash), same backend, Hermes shows a streamed lead-in in 5–7 s and an answer in ~40 s. Per-call model latency is identical (5–8 s median, 20 s+ tail on OpenRouter's default provider routing); the whole gap was prompt design and flow:web_searchin one messagerequest_plan_review→ parkedWire capture: https://claude.ai/artifact/DN2N9229DJ2GHr6c1zeKed
Our own prompt caused it:
STYLE.mdbanned any lead-in ("the user only sees your reply once it is finished", false for streaming chat),prompt.mdmandatedrequest_plan_reviewbefore any 3+ step work, and ~25k of the 30k was routing prose duplicated in tool descriptions or already enforced by middleware.What changed
Flow
request_plan_reviewleaves the chat orchestrator's belt (planner/cron agents keep it;agent/plan_review/*untouched). Destructive shell/file actions were already gated by the approval layer.STYLE.mdand the tinytools native tool protocol (fix(render): let the native tool protocol allow a lead-in line before tool calls tinytools#15) now allow a one-line lead-in in the same message as the tool calls; never without the call, never as the end of a turn. The web-chat rail already renders it.Prompt (≤ 8k hermetic, was 29.9k)
orchestrator/prompt.mdrewritten:## How you work(five branches),## Sub-agents,## Plans,## Grounding and tool use(merged with evidence-aware synthesis),## Scheduling and workflows. Deleted everything the middleware/loader enforces (memory-index nag, spawn hierarchy,use_skillauthoring refusal) and every "IMPORTANT/MUST" restatement of a tool description.SOUL.md/IDENTITY.md/ROLE.md/STYLE.mdshrunk to ~2k combined (brand-voice guardrail kept). Note:render_helpers/workspace_files.rsonly re-seeds unmodified workspace copies, so hand-edited installs keep their old text.## Capabilities not in your tool list(grouped per pack), connected integrations + capability questions, MCP servers, date/time, workspace, memory access/remembering;## Tool Policy Boundaryprints a tool count instead of re-listing the belt.## Rememberingno longer namessave_preferenceon the orchestrator (it was pack-stripped from the wire); the memory sections are gated on the post-pack-strip visible set.Tiering / prompt cache
PROMPT_TIER_CONTEXT_MARKER/PROMPT_TIER_VOLATILE_MARKER(split_prompt_tiers,PromptSection::build_parts); the orchestrator does, so identity and rules lead the stable tier instead of trailing the factory-added memory sections (which defaulted toStable;LearnedContext/UserProfileare nowVolatile). Grounding and style close the stable tier.TieredPrompt::system_messages()sends two system messages (stable+context, volatile);runtime_session::preparefreezes them as the prefix, resume takes every leading system message, andPromptCacheSegmentMiddlewaredeclares one segment per message. tinyagents (feat(prompt): per-tier system segments and model-family execution guidance tinyagents#183) gives each its own cacheable segment.## Execution discipline(tinyagentsprompt::model_guidance) renders only for deepseek/glm/qwen/gpt/grok/… families, not Claude/Gemini.Tool belt (31 → 25)
request_plan_review,plan_exit(its marker had no consumer; also off planner/code_executor/skill_creator),read_workspace_state(shell does it),update_task(wastodowith another default board),retrieve_tool_output(legacy alias oftinyjuice_retrieve;RECOVERY_TOOL_VISIBLEnow advertises only the live tool),tinyjuice_retrieveitself when compaction is off.todowrite→todoon planner/code_executor/task_manager/skill_creator (it resolved to no tool);composio_connectunpacked so the prompt's connect-card route is actually reachable (thecomposiopack is closed to the orchestrator viaplanner);spawn_async_subagent'sagent_idenum narrowed per session to the parent's[subagents]allowlist; the "usespawn_async_subagentwithblocking: true" claim removed (no such argument).todo(1,853 → 1,098 B),spawn_async_subagent,memory_store,resolve_time,shell.category,continue_subagent,use_skillpack summaries,delegate_to_integrations_agent(per-toolkit blurbs capped at 80 chars), every delegatewhen_to_use(run_codeno longer says "Route ANY repo-scoped work here").tokenjuice compaction off by default (
context.compaction_enabled = false,tokenjuice.router_enabled = false;OPENHUMAN_COMPACTION=1re-enables). The compacted view cost a retrieval round trip more often than it saved context, and every curated belt paid for the retrieve tool's schema. Per-tool caps and thefile_read-backed byte backstop stay on.Instrumentation
web_chat/turn_timing.rs:[web_channel][bridge] time-to-first-visiblelogs first text delta, first tool call, and a turn summary.turn_run_finalize.rs:[tinyagents] turn prompt summarylogs system-segment bytes besideinput_tokens/cached_input_tokens.scripts/prompt-eval/cases.json:orchestrator-research-trip(must callweb_search_tool; must not callrequest_plan_review/todo/spawn).scripts/prompt-budget.limitsratcheted:orchestrator30213/30818 → 8821/21523, plus every agent that shares the trimmed sections.Verification
cargo test -p openhuman --lib(RUST_MIN_STACK=16777216): 10467 passed, 77 failed = exactly the 77 that fail onmainlocally (diffed by name; zero new).cargo test -p openhuman-cli --test agent_prompt_comprehension_e2e: same 5 pre-existing local failures asmain(delegation requires a live harness run context);orchestrator_presentation_wiring,orchestrator_parallel_fanout_routingpass.scripts/check-prompt-budget.shOK after--write;pnpm rust:layoutOK (new helpers live inweb_chat/turn_timing.rs,session_host/prefix_snapshot.rs,builder/helpers.rs).openhuman-core agent dump-prompt --agent orchestrator --with-tools: 8,821 B prompt, 25 tools; the wire shows two system messages.Follow-ups (not in this PR)
provider: {sort: "latency"}foropenrouter/*to cut the 20 s+ tail.ChatRuntimeProvider.tsx onInterim).harness/memory_protocol.rshas no arm for the collapsedmemorytool used by wildcard agents.Depends on tinyhumansai/tinytools#15 and tinyhumansai/tinyagents#183 (gitlinks point at their branch heads).
Co-authored-by: Medulla medulla@tinyhumans.ai
Summary by CodeRabbit
New Features
Improvements
Changed Defaults
Removed
Round two: merged upstream/main + #6367, vendor bumped to main, defaults flipped
Wire report (capture proxy, same Kashmir question,
deepseek-v4-flashvia the backend): https://claude.ai/artifact/6c5KAmg6JT1jXe7o1BZv8Gupstream/main(feat(tools): Jev-ranked tool_search over deferred tools and Composio actions #6435 Jevtool_search, feat(agent): code-style tool dialects (python / typescript) behind agent.tool_dispatcher #6433 code dialects, feat(inference): price-sorted routing and a stable prompt-cache key for direct OpenRouter #6434 price sort + harness cache-key ids, chore(debug): make the inference capture proxy report responses, and document it for agents and CI #6437 capture proxy) andorigin/green-all-tests-followup-next(test: continue full-suite compatibility fixes #6367).vendor/tinyagents→ main (4aeae2b5, then the PR branch below); nested tinytools → its main content.tinyflowsstays on main's gitlink (test: continue full-suite compatibility fixes #6367's commit is not on tinyflows main).agent.tool_dispatcher = "python",agent.tool_search.ranker = "jev"(docs, env-overlay test, discovery test updated).JevEvaluatorseam instead of owning the client.openhuman-tinyhumans::jev::SystemOneEvaluatorimplements it overtinyjevclient(System One request/answer translation, deadline, error mapping,ClientConfigre-export); ranker and bench ported; 9 tests incl. a loopback round trip.PromptCacheSegmentMiddlewareuses the harness's per-tier ids (system,system.1,tools) and declares notoolssegment under a text dialect (the harness clearstoolsafterbefore_model, and a layout mismatch demoted the request to a per-call digest —tap-…changed on every call). Paired with fix(cache): stable layout under text dialects; RunPolicy::host_renders_tool_catalogue tinyagents#190, which also tolerates the stripped segment on the harness side.ToolsSection+ harnessapply_to_request, 11 KB + 6 KB):RunPolicy::host_renders_tool_catalogue = true(tinyagents#190). 7,013 → 5,158 prompt tokens.CanonicalSharedToolAdapter::exposure()no longer forwardsHidden; a Hidden tool that reached registration was named by the belt, so it is advertised. The harness was advertising 14 of the prompt's 25 tools (research,plan,run_code,memory_recall,memory_store… unreachable).runtime_session::prepare), matchingSessionDriver.<tool_call>tags under python; 4/6 turns leaked the raw block. Doubled opener is one block; bare trailing opener is dropped.parse_tool_calls_nested_xml_tags_are_rejectedre-pinned to the new contract.Measured (3 fresh two-turn conversations per dialect)
auto)Python is 38% cheaper per call and caches better; DeepSeek is more hesitant in it (asks first, sometimes ends a turn on "let me search" with no block — a text dialect has no
finish_reason=tool_callsto nudge on). n=3, so a signal, not a measurement. If time-to-useful-answer on the managed DeepSeek route is what matters,tool_dispatcher = "auto"is the one-line revert; python still wins on small local models (#6433 bench).Tests
RUST_MIN_STACK=16777216 cargo test -p openhuman --lib: 10,543 passed; 5 failed, of whichfleet_prompt_tests::every_prompt_names_at_least_one_tool_it_can_call,spawn_async_subagent…no_parent_threadandthe_withheld_block_renders…fail identically onupstream/mainin this environment (verified),composio…no_baked_clientis order-dependent (passes in isolation on both), and the fifth was the re-pinned doubled-tag test.pnpm rust:layoutreports the same four over-limit files asupstream/main(779/796/752 lines andruntime_session.rs1992 > 1943); none are from this branch.orchestrator:8858:21523(+37 B for thetool_searchbranch).Depends on tinyhumansai/tinyagents#190 (which bumps tinyhumansai/tinytools#20); the
vendor/tinyagentsgitlink must move to the merge commit before this merges.Round three: every progress event was delivered twice
Found while chasing a "the agent forgot the previous turn" report. The wire request was fine (history replayed in full, verified through the capture proxy across a cold-boot resume); what was actually wrong was the progress stream: two producers wrote the turn's
AgentProgresschannel.OpenhumanEventBridge(subscribed to the run'sEventSinkon everyturn_runnerrun) projectsAgentEvent::ModelDelta→TextDelta,ToolStarted/ToolCompleted→ tool rows, etc.OpenHumanProgressSink(the hostProgressSinkcapability) projected the harness's coarse mirror of the same loop (Token,ToolCall,ToolCallFinished,Finished) onto the same channel. tinyagents started emitting that mirror for every model delta in977763a4/9ce02dc5(2026-09-19), which is already in upstream's gitlink, somainhas this too.Observable on the client event stream: every
text_deltatwice with distinctseqs, everytool_call/tool_resulttwice, twoturn_donelines per request, and the interim bubble interleaving the copies ("TheThe resolver couldn't parse that exact phrase, so let resolver couldn't…"). The final bubble looked right only becausechat_donecarriesfull_response.Fix:
OpenHumanHostBundleFactory::buildno longer hands the turn's live channel to the host sink (crates/openhuman-core/src/agent/tinyagents/host/bundle.rs); the sink stays registered as the capability with an unconsumed channel, the bridge is the single producer. Regression testturn_runner_tests::a_streamed_delta_reaches_the_progress_channel_exactly_once(failed with["one delta", "one delta"]before). Re-checked on the wire: one event per delta, one row per tool call, oneturn_done.The "forgot the previous turn" itself was not a code path: the desktop process under test had been started at 12:09 from a pre-diet binary (36 KB prompt, 31 tools) and was never relaunched after the 12:14 rebuild, and the user
config.tomlhadtool_dispatcher = "auto"/ranker = "auto"persisted from the old defaults, which override the new ones.Round four: dependencies landed, #6438 merged in
vendor/tinyagents→0bc4ec44(tinyagentsmain, the [Feature] Fix memory init/config gaps and add E2E coverage for Memory lifecycle #190 merge); nestedvendor/tinytools→cfb3a155(tinytoolsmain, the fix/rename #20 merge). Both merged with merge commits; the "gitlink must move before merge" note above is discharged.jev-family-embedding) into this branch: itsSystemOneEvaluator/JevRankerport over theJevEvaluatorseam supersedes the one written here (same seam, more measured), so those files took feat(tool-search): Jev over an embedding top-20 by default; measured on the Composio catalogue #6438's side;tinyjevclientmoves to its pinned84b3983c. Discovery gains the embedding top-20 pre-filter;ranker = "jev"stays the default.ask_user_clarificationis off the orchestrator belt (24 tools). It is a no-op on the chat surface (early-exit tool → the question becomes the reply, same as asking in prose) and stays on the sub-agent belts where it drivesawaiting_user/continue_subagent.scripts/prompt-size-measure.shpinsOPENHUMAN_TOOL_DISPATCHER=auto: under thepythondefault the catalogue is rendered into the system prompt and the ratchet counted it twice. Limits re-ratcheted; every agent is belowmain(orchestrator30213:30818→9169:20946).