Skip to content

feat: expose tool traces to memory providers#28065

Closed
devwdave wants to merge 5 commits into
NousResearch:mainfrom
devwdave:memori-memory-trace-provider-docs
Closed

feat: expose tool traces to memory providers#28065
devwdave wants to merge 5 commits into
NousResearch:mainfrom
devwdave:memori-memory-trace-provider-docs

Conversation

@devwdave

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds Memori to the community plugin documentation as a standalone external memory provider, and adds the generic memory-provider trace support needed for providers like Memori to capture meaningful agent memory.

This does not add Memori as an in-tree provider. Memori remains distributed as a standalone community plugin/package. The Hermes-side change is a backward-compatible optional trace payload for external memory providers, so they can receive structured completed-turn tool execution metadata.

Trace data is critical for agent memory because the final assistant response often does not contain the actual work the agent performed. For example, an assistant may say “Tests passed,” but the useful memory is that Hermes ran terminal({"command": "pytest"}), received the test output, and then summarized it. Without trace data, external memory only captures what the assistant said, not what the agent actually did.

Existing memory providers remain compatible. Hermes only passes trace to providers whose sync_turn() accepts it.

Related Issue

Fixes #

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • README.md

    • Adds hermes-memori to the community plugins section.
  • website/docs/user-guide/features/memory-providers.md

    • Adds Memori under community providers as a standalone external memory provider.
  • agent/memory_provider.py

    • Adds optional trace parameter to the MemoryProvider.sync_turn() interface.
    • Documents that providers can ignore the trace if they do not need tool execution metadata.
  • agent/memory_manager.py

    • Updates sync_all() to accept an optional trace.
    • Uses provider signature inspection to preserve compatibility with providers that still implement the older sync_turn(user, assistant, session_id=...) shape.
  • agent/agent_init.py

    • Initializes per-turn tool trace storage on the agent.
  • agent/conversation_loop.py

    • Resets tool trace storage at the start of each turn.
    • Passes the final message list into completed-turn external memory sync.
  • agent/tool_executor.py

    • Records structured trace entries for sequential and concurrent tool calls.
    • Includes tool name, arguments, result content, duration, error state, blocked state, and cancelled state.
    • Records skipped/cancelled tool calls so interrupted execution is represented accurately.
  • run_agent.py

    • Adds helpers to record per-turn tool observations and build a versioned external memory trace payload.
    • Sends the trace to memory providers during completed-turn sync when tool calls occurred.
  • tests/agent/test_memory_provider.py

    • Adds coverage proving trace is passed to opted-in providers and omitted for legacy providers.
  • tests/run_agent/test_memory_sync_interrupted.py

    • Adds coverage for completed-turn trace sync, ordering, final tool result content, and blocked/cancelled status.
  • website/docs/developer-guide/memory-provider-plugin.md

    • Documents the optional trace argument for sync_turn().
    • Adds a note that cloud providers should document what trace data may be sent off-device.

How to Test

  1. Run the focused memory provider and trace tests:

    uv run pytest tests/agent/test_memory_provider.py tests/run_agent/test_memory_sync_interrupted.py
  2. Verify the focused test suite passes:

    83 passed
    
  3. Optionally test with a memory provider whose sync_turn() accepts trace=None and confirm it receives a payload like:

    {
      "version": 1,
      "capture_policy": "full_raw_after_hermes_processing",
      "tool_calls": [
        {
          "order": 1,
          "name": "terminal",
          "arguments": {"command": "pytest"},
          "result_content": "...",
          "duration_seconds": 2.21,
          "is_error": false,
          "blocked": false,
          "cancelled": false
        }
      ]
    }

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

N/A

Screenshots / Logs

Focused test run:

uv run pytest tests/agent/test_memory_provider.py tests/run_agent/test_memory_sync_interrupted.py

83 passed

@devwdave devwdave mentioned this pull request May 18, 2026
23 tasks
@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent loop, run_agent.py, prompt builder tool/memory Memory tool and memory providers P3 Low — cosmetic, nice to have labels May 18, 2026
@devwdave devwdave force-pushed the memori-memory-trace-provider-docs branch from b9f553b to ffbab38 Compare May 18, 2026 19:30
@devwdave devwdave force-pushed the memori-memory-trace-provider-docs branch from ffbab38 to d531b1b Compare May 18, 2026 19:32
devwdave and others added 2 commits May 28, 2026 09:46
Added a new function, _record_turn_tool_trace, to facilitate per-turn tool tracing for agents. This function is invoked in multiple locations within execute_tool_calls_concurrent and execute_tool_calls_sequential to record trace data, enhancing the tracking of tool call executions. This change improves the observability of agent actions during tool interactions.
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Thanks for reworking this as a standalone plugin + generic hook rather than an in-tree provider — that's the right call, and the backward-compat signature inspection is clean.

Before we lock in a new structured-trace contract though, I want to make sure we're not adding core surface we already have. A couple of things:

The full message list — tool calls and results included — is already handed to providers via on_session_end(messages) and on_pre_compress(messages). So the raw "what did the agent actually do" data was never inaccessible; a provider can read every terminal(...) call and its result from there today.

The genuine gap is timing/granularity: sync_turn is the only per-turn persistence hook and it's text-only, while on_session_end deliberately fires only at real session boundaries (not after each turn). That's a real limitation for incremental background ingestion.

But the minimal fix for that is to thread the existing messages list into sync_turn — which this PR already plumbs through _sync_external_memory_for_turn — and let providers extract what they need from it. That's a few lines and no new contract for us to maintain.

The part that genuinely isn't reconstructable from the message list is the per-tool duration_seconds / blocked / cancelled metadata and the capture_policy final-content normalization. If those signals are central to Memori, could you make the case for them specifically? Otherwise we'd prefer to keep the core contract as sync_turn(..., messages=...) and avoid owning a structured-trace schema (version, capture_policy, per-tool envelope) on behalf of an out-of-tree package — that's a stability commitment that's hard to walk back once it ships.

Happy to merge a slimmed-down version quickly. Feel free to push back if I'm missing why the structured envelope needs to live in core.

@devwdave

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback!

That framing makes sense.

I’ll slim this down to pass the existing OpenAI-style messages list into sync_turn with backward-compatible signature inspection, and remove the structured trace envelope from core. Memori can extract tool calls/results from the transcript and use plugin hooks for richer provider-local telemetry if needed.

I don’t think duration/block/cancel metadata is essential, so that is fine!

I'll have an updated PR shortly!

@devwdave

Copy link
Copy Markdown
Contributor Author

@kshitijk4poor this PR has been slimmed down and incorporates the feedback you've provided.

Thank you!!

kshitijk4poor pushed a commit to kshitijk4poor/hermes-agent that referenced this pull request May 28, 2026
Adds an optional `messages` keyword to the `MemoryProvider.sync_turn`
contract so external/community memory plugins can receive the OpenAI-style
conversation message list for the completed turn — including assistant tool
calls and tool result content — not just the final assistant text.

Dispatch uses signature inspection (`_provider_sync_accepts_messages`): only
providers that declare a `messages` parameter (or `**kwargs`) receive it; all
existing in-tree providers keep their legacy text-only signature and are
called unchanged. No structured-trace envelope is added to core — providers
reconstruct whatever they need from the standard message list.

Also documents Memori as a standalone community memory provider.

Salvaged from NousResearch#28065 — rebased onto current main.

Co-authored-by: Dave Heritage <david@memorilabs.ai>
kshitijk4poor added a commit to kshitijk4poor/hermes-agent that referenced this pull request May 28, 2026
Maps both commit emails (david@memorilabs.ai, dave@devwdave.com) used on
NousResearch#28065 to the devwdave GitHub account so the contributor audit in
scripts/release.py passes.
kshitijk4poor added a commit that referenced this pull request May 28, 2026
…sages

feat: expose completed-turn message context to memory providers (salvage #28065)
Bryce-huang pushed a commit to wbkunlun/hermes-agent that referenced this pull request May 29, 2026
Adds an optional `messages` keyword to the `MemoryProvider.sync_turn`
contract so external/community memory plugins can receive the OpenAI-style
conversation message list for the completed turn — including assistant tool
calls and tool result content — not just the final assistant text.

Dispatch uses signature inspection (`_provider_sync_accepts_messages`): only
providers that declare a `messages` parameter (or `**kwargs`) receive it; all
existing in-tree providers keep their legacy text-only signature and are
called unchanged. No structured-trace envelope is added to core — providers
reconstruct whatever they need from the standard message list.

Also documents Memori as a standalone community memory provider.

Salvaged from NousResearch#28065 — rebased onto current main.

Co-authored-by: Dave Heritage <david@memorilabs.ai>

#AI commit#
Bryce-huang pushed a commit to wbkunlun/hermes-agent that referenced this pull request May 29, 2026
Maps both commit emails (david@memorilabs.ai, dave@devwdave.com) used on
NousResearch#28065 to the devwdave GitHub account so the contributor audit in
scripts/release.py passes.

#AI commit#
zwolniony pushed a commit to zwolniony/hermes-agent that referenced this pull request May 29, 2026
Adds an optional `messages` keyword to the `MemoryProvider.sync_turn`
contract so external/community memory plugins can receive the OpenAI-style
conversation message list for the completed turn — including assistant tool
calls and tool result content — not just the final assistant text.

Dispatch uses signature inspection (`_provider_sync_accepts_messages`): only
providers that declare a `messages` parameter (or `**kwargs`) receive it; all
existing in-tree providers keep their legacy text-only signature and are
called unchanged. No structured-trace envelope is added to core — providers
reconstruct whatever they need from the standard message list.

Also documents Memori as a standalone community memory provider.

Salvaged from NousResearch#28065 — rebased onto current main.

Co-authored-by: Dave Heritage <david@memorilabs.ai>
zwolniony pushed a commit to zwolniony/hermes-agent that referenced this pull request May 29, 2026
Maps both commit emails (david@memorilabs.ai, dave@devwdave.com) used on
NousResearch#28065 to the devwdave GitHub account so the contributor audit in
scripts/release.py passes.
pty819 added a commit to pty819/hermes-agent that referenced this pull request May 29, 2026
The OpenVikingMemoryProvider.sync_turn() previously only received
user_content and assistant_content as plain text (truncated to 4K
each), discarding all tool calls, tool results, and intermediate
messages from multi-step agent turns.

This commit wires the optional messages kwarg (added in commit
5a95fb2 / NousResearch#28065) to OpenViking's session sync path:

- Accept messages: Optional[List[Dict]] in sync_turn() signature
- Track _last_message_count to POST only the delta of new messages
  on each turn (avoids re-sending entire history every call)
- Serialize tool_calls to compact text form for the session API
- Include role:tool messages (tool results) alongside user/assistant
- Skip system prompts (session-wide state, not conversational turns)
- Reset _last_message_count in initialize() and on_session_switch()
- Keep legacy text-only fallback when messages is not provided

Closes NousResearch#34762
mosaiq-systems pushed a commit to mosaiq-systems/hermes-agent that referenced this pull request May 29, 2026
Adds an optional `messages` keyword to the `MemoryProvider.sync_turn`
contract so external/community memory plugins can receive the OpenAI-style
conversation message list for the completed turn — including assistant tool
calls and tool result content — not just the final assistant text.

Dispatch uses signature inspection (`_provider_sync_accepts_messages`): only
providers that declare a `messages` parameter (or `**kwargs`) receive it; all
existing in-tree providers keep their legacy text-only signature and are
called unchanged. No structured-trace envelope is added to core — providers
reconstruct whatever they need from the standard message list.

Also documents Memori as a standalone community memory provider.

Salvaged from NousResearch#28065 — rebased onto current main.

Co-authored-by: Dave Heritage <david@memorilabs.ai>
mosaiq-systems pushed a commit to mosaiq-systems/hermes-agent that referenced this pull request May 29, 2026
Maps both commit emails (david@memorilabs.ai, dave@devwdave.com) used on
NousResearch#28065 to the devwdave GitHub account so the contributor audit in
scripts/release.py passes.
KKT-OPT pushed a commit to KKT-OPT/hermes-agent that referenced this pull request May 31, 2026
Adds an optional `messages` keyword to the `MemoryProvider.sync_turn`
contract so external/community memory plugins can receive the OpenAI-style
conversation message list for the completed turn — including assistant tool
calls and tool result content — not just the final assistant text.

Dispatch uses signature inspection (`_provider_sync_accepts_messages`): only
providers that declare a `messages` parameter (or `**kwargs`) receive it; all
existing in-tree providers keep their legacy text-only signature and are
called unchanged. No structured-trace envelope is added to core — providers
reconstruct whatever they need from the standard message list.

Also documents Memori as a standalone community memory provider.

Salvaged from NousResearch#28065 — rebased onto current main.

Co-authored-by: Dave Heritage <david@memorilabs.ai>
KKT-OPT pushed a commit to KKT-OPT/hermes-agent that referenced this pull request May 31, 2026
Maps both commit emails (david@memorilabs.ai, dave@devwdave.com) used on
NousResearch#28065 to the devwdave GitHub account so the contributor audit in
scripts/release.py passes.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
Adds an optional `messages` keyword to the `MemoryProvider.sync_turn`
contract so external/community memory plugins can receive the OpenAI-style
conversation message list for the completed turn — including assistant tool
calls and tool result content — not just the final assistant text.

Dispatch uses signature inspection (`_provider_sync_accepts_messages`): only
providers that declare a `messages` parameter (or `**kwargs`) receive it; all
existing in-tree providers keep their legacy text-only signature and are
called unchanged. No structured-trace envelope is added to core — providers
reconstruct whatever they need from the standard message list.

Also documents Memori as a standalone community memory provider.

Salvaged from NousResearch#28065 — rebased onto current main.

Co-authored-by: Dave Heritage <david@memorilabs.ai>
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
Maps both commit emails (david@memorilabs.ai, dave@devwdave.com) used on
NousResearch#28065 to the devwdave GitHub account so the contributor audit in
scripts/release.py passes.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…ri-trace-messages

feat: expose completed-turn message context to memory providers (salvage NousResearch#28065)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P3 Low — cosmetic, nice to have tool/memory Memory tool and memory providers type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants