Skip to content

fix(agent): give the orchestrator direct memory tools so recall/store don't over-delegate#4764

Merged
senamakel merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/GH-4762-direct-memory-tools
Jul 13, 2026
Merged

fix(agent): give the orchestrator direct memory tools so recall/store don't over-delegate#4764
senamakel merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/GH-4762-direct-memory-tools

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Problem (issue #4762)

Memory ops via the agent path over-delegate and hang, and the one profile write that returned gave a 0-char result with persistence unconfirmed:

  • "store my Q3 OKR": called manage_profile_memory@12s, tool_result {"elapsed_ms":0,"output_chars":0} (redundantly spawned a sub-agent that did 0 sub tool calls) — write persistence unconfirmed.
  • "what is my Q3 OKR" (fresh thread): a trivial single-fact recall spawned a sub-agent and hung.
  • ingest/reconcile turns: serial spawns, empty replies, never completed.

Root cause — openhuman agent-registry config (not tinyagents/harness)

I checked whether this was a harness matter: it isn't. The delegation machinery works; the problem is routing. The chat orchestrator has a direct read surface for files/web ("answer small lookups itself instead of spawning a sub-agent") but no direct memory tools. Memory recall/store are reachable only via sub-agent delegates:

  • retrieve_memory → spawns agent_memory
  • manage_profile_memory → spawns profile_memory_agent

So a trivial recall spawns a whole worker (over-delegation, #4744) that hangs, and a simple write spawns profile_memory_agent which (per the eval) made 0 tool calls and returned empty — the delegate then surfaces outcome.output verbatim (agent_orchestration/tools/dispatch.rs:247), hence the output_chars:0 result with persistence unconfirmed. The orchestrator prompt even labels the retrieve_memory delegate a "direct tool", reinforcing the wrong route.

Fix (minimal: config + prompt + test)

Memory is the product's core — recall/store must be first-class direct tools:

  1. orchestrator/agent.toml — add memory_recall, memory_store, save_preference to the direct [tools].named surface. They're already registered in the shared tool pool (tools/ops.rs:337-377), and memory_recall reads the same store memory_store writes, so store → recall closes the loop inline with a real confirmation and confirmed persistence. These are memory-store ops, not file/shell mutation, so the chat agent's "no write tools" rule (which is about editing files / running commands) is unaffected. Deep memory-tree walks, ingest/reconcile, people-graph, and persona edits still delegate to retrieve_memory / manage_profile_memory.
  2. orchestrator/prompt.md — correct the mislabeled "direct" retrieve_memorymemory_recall, and add a bullet: recall/store are direct; reserve the delegates for deep work; never spawn a sub-agent for a single recall or "remember this".
  3. agents/loader.rs — the orchestrator_has_chat_hint_and_named_tools test now asserts the three direct memory tools are wired (fails before, passes after).

Why this resolves both asks

  • (1) recall is a direct tool call, not a sub-agent spawn.
  • (2) the 0-char write is fixed: a "remember X" write is now a direct memory_store / save_preference call that runs synchronously, returns a real confirmation (non-zero output), and persists before returning. Direct-tool persistence itself is already covered by the existing unit tests in memory/tools/store.rs + recall.rs.

The manage_profile_memory delegate remains for deep-only requests; its empty-output edge is no longer on the hot path for trivial store/recall.

Checks

Per the active no-local-builds directive (disk pressure), CI verifies — no cargo/pnpm run locally.

Closes #4762

Summary by CodeRabbit

  • New Features

    • Memory actions like recalling saved information, storing updates, and saving preferences are now handled directly by the orchestrator for faster, more seamless interactions.
    • Common memory requests should complete inline more often, reducing unnecessary handoffs.
  • Bug Fixes

    • Improved handling of memory-related requests so they follow the intended direct path before falling back to more complex processing.

@M3gA-Mind M3gA-Mind requested a review from a team July 9, 2026 17:36
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0eccc02e-cfcc-4d9c-b41c-3632809368b5

📥 Commits

Reviewing files that changed from the base of the PR and between 781563a and aad1a16.

📒 Files selected for processing (3)
  • src/openhuman/agent_registry/agents/loader.rs
  • src/openhuman/agent_registry/agents/orchestrator/agent.toml
  • src/openhuman/agent_registry/agents/orchestrator/prompt.md
📝 Walkthrough

Walkthrough

This PR adds memory_recall, memory_store, and save_preference to the orchestrator agent's direct tool allowlist in agent.toml, updates prompt.md guidance to clarify these are direct-tool operations rather than delegated sub-agent work, and extends the loader.rs test to assert these tools are present.

Changes

Direct Memory Tool Allowlisting

Layer / File(s) Summary
Allowlist and prompt guidance
src/openhuman/agent_registry/agents/orchestrator/agent.toml, src/openhuman/agent_registry/agents/orchestrator/prompt.md
Adds memory_recall, memory_store, and save_preference to the direct tool allowlist with comments, and updates delegation decision tree guidance to treat these as direct-tool operations rather than sub-agent delegations.
Loader test assertions
src/openhuman/agent_registry/agents/loader.rs
Extends orchestrator_has_chat_hint_and_named_tools test to assert the three memory tools appear in the orchestrator's named tool list, under contract tag #4762.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

Possibly related PRs

  • tinyhumansai/openhuman#4577: Both PRs modify the orchestrator's direct/named tool allowlists and the corresponding loader.rs test assertions.

Suggested labels: agent, bug

Poem

A rabbit hums a memory tune,
No sub-agent needed, not even at noon.
Recall and store, save your delight,
Direct as a carrot, straight and right! 🥕
Hop, hop, the orchestrator's now light!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: giving the orchestrator direct memory tools to prevent over-delegation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de744a3195

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/openhuman/agent_registry/agents/orchestrator/agent.toml
… don't over-delegate

Memory recall and store were reachable from the chat orchestrator ONLY via
sub-agent delegates (retrieve_memory -> agent_memory, manage_profile_memory ->
profile_memory_agent). So a trivial recall ("what is my Q3 OKR") spawned a
whole worker that hung (over-delegation, tinyhumansai#4744), and a simple write ("store my
Q3 OKR") spawned profile_memory_agent which — per the eval — made 0 tool calls
and returned empty, so the delegate surfaced outcome.output verbatim as a
0-char result with the write's persistence unconfirmed. The orchestrator prompt
even called the retrieve_memory DELEGATE a "direct tool", reinforcing the
wrong route.

Memory is the product's core; recall/store must be first-class DIRECT tools:
- agent.toml: add memory_recall, memory_store, save_preference to the direct
  [tools].named surface (already registered in the shared pool, tools/ops.rs).
  memory_recall reads the same store memory_store writes, so store->recall
  closes the loop inline with a real confirmation and confirmed persistence.
  These are memory-store ops, not file/shell mutation, so the chat agent's
  "no write tools" rule (files/commands) is unaffected. Deep tree walks,
  ingest/reconcile, people-graph, and persona edits still delegate to
  retrieve_memory / manage_profile_memory.
- prompt.md: correct the mislabeled "direct" retrieve_memory -> memory_recall
  and add a bullet — recall/store are direct; reserve the delegates for deep
  work; never spawn a sub-agent for a single recall or "remember this".
- loader test asserts the three direct memory tools are wired.

Closes tinyhumansai#4762
@M3gA-Mind M3gA-Mind force-pushed the fix/GH-4762-direct-memory-tools branch from de744a3 to 781563a Compare July 9, 2026 18:49
@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Jul 9, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 9, 2026
@M3gA-Mind M3gA-Mind self-assigned this Jul 9, 2026
…t-store memory protocol loop

A successful `memory_store` is a durable write, so the memory-protocol
middleware appends a "call `update_memory_md` to keep the MEMORY.md index
in sync" note to the tool result and warns at run end when the index is
never reconciled. Now that the orchestrator takes the direct-store path
itself (this PR), it must own that closing step — otherwise the guidance
is unsatisfiable and MEMORY.md (which the orchestrator loads,
`omit_memory_md = false`) drifts from the store.

`update_memory_md` is locked to the MEMORY.md / SKILL.md workspace index
files (enum-gated + symlink-hardened), not a general file-editor — it is
the index-sync counterpart of `memory_store`, so it sits in the same
memory bucket as the writes already allowlisted. The "no write tools"
rule (edit / file_write / apply_patch / shell) is unaffected.

- agent.toml: allowlist `update_memory_md` with rationale.
- prompt.md: instruct the reconcile after a `memory_store` write
  (save_preference needs none — it writes the preference store).
- loader.rs: assert the closing step is wired.

Addresses @chatgpt-codex-connector (P2) on
agent.toml — "Add index syncing for direct memory stores".
@YellowSnnowmann

Copy link
Copy Markdown
Contributor

Verdict: Approve — feature satisfied

Delivers what #4762 asks. memory_recall, memory_store, save_preference are promoted to the orchestrator's direct [tools].named surface, so a trivial recall or a single "remember this" no longer pays a blocking sub-agent round-trip. Verified against the code, not just the diff:

  • All four added tool names resolve to real registered tools: memory/tools/recall.rs:22, memory/tools/store.rs:25, agent/tools/save_preference.rs:98, tools/impl/filesystem/update_memory_md.rs:169.
  • The prompt.md:26 mislabel ("retrieve_memory" as a direct tool) is correctly fixed to memory_recall; the remaining retrieve_memory mentions (prompt.md:240,249,253) are the delegate path and are correctly left in place.
  • memory_store / save_preference enforce ToolOperation::Act (store.rs:88, save_preference.rs:158); in readonly tier this returns a structured "read-only mode" ToolResult::error (policy_tests.rs:62) — graceful, non-Sentry. Correct error taxonomy.
  • The 0-char delegate bug is not fixed at its source, but it's taken off the hot path for trivial store/recall, which the body states plainly. Deep ingest/reconcile/people-graph/persona still delegate. Reasonable scope.

Codex P2 thread (agent.toml:239) — addressed

The concern was valid: a direct memory_store classifies as MemoryOp::Write (memory_protocol.rs:79), which appends "call update_memory_md" guidance and warns on drift at run end — but update_memory_md wasn't in scope, making the reconcile unsatisfiable and letting MEMORY.md (omit_memory_md = false) drift. Fixed in aad1a161c by adding update_memory_md to the allowlist + a prompt reconcile instruction + a loader assertion. Verified: save_preference is MemoryOp::Other (memory_protocol.rs:90) and correctly exempt; update_memory_md is enum-gated to MEMORY.md/SKILL.md with symlink hardening (update_memory_md.rs:13,185,64-80), so it's the index-sync counterpart of the store, not a general editor — the no-write-tools invariant (loader.rs:724) still holds. Please mark the thread resolved on GitHub; it's the only thing left open.

CI is green (Rust Quality, Rust Core Coverage, all PR gates); frontend/E2E lanes correctly skipped for this Rust-config-only change.

@YellowSnnowmann YellowSnnowmann left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — feature satisfied, no blocker/major issues, review comments addressed in code, CI green. Detailed review in the PR comment above.

@senamakel senamakel merged commit 383581b into tinyhumansai:main Jul 13, 2026
16 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[agent][memory] Memory ops over-delegate + hang via agent path; profile-write returns 0-char output (persistence unconfirmed)

3 participants