Skip to content

fix(memory): store only what a person sent as a conversation memory - #5313

Merged
senamakel merged 4 commits into
tinyhumansai:mainfrom
yh928:fix/autosave-user-turns-only
Sep 11, 2026
Merged

senamakel merged 4 commits into
tinyhumansai:mainfrom
yh928:fix/autosave-user-turns-only

Conversation

@yh928

@yh928 yh928 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • An internal agent no longer stores its own prompt as the user's conversation memory.
  • AgentTurnOrigin::is_user_authored names the distinction the codebase already had: WebChat / ExternalChannel carry what a person sent, every other origin carries host text.
  • Gated at the session, so a new internal agent cannot forget to opt out.
  • Existing rows are left alone — this is the write path only.

Problem

Session's autosave persists the turn's "user message" as a MemoryCategory::Conversation document keyed user_msg:<uuid>, gated only on config.memory.auto_save. An internal agent built with Agent::from_config_for_agent(...) inherits that flag — and its "user message" is the prompt the host wrote for it.

Found live, in the global namespace of a workspace with two namespaces:

category: "conversation"   key: "user_msg:f7cf6a07-…"
content:  "Maintain the existing goals list. Call goals_list first, then make the
           MINIMAL set of changes (goals_add / goals_edit / goals_delete) justified
           by the context below. Do not churn goals that are still valid.
           …
           ## Context
           Recent conversation recap (segment seg-18c746e3…): The user asked twice to
           search Gmail for emails from Colorado…"

That is memory_goals::enrich's prompt, verbatim, stored as if the user had typed it. It then competes for slots in every later recall, which is what made a namespace-wide memory search read like a transcript dump.

The blast radius is every config-built internal agent, not just goals: the flag comes from config, so a caller has to remember to turn it off, and forgetting is invisible until the store is inspected.

Solution

Gate the autosave on the turn's origin. memory_goals::enrich already runs under AgentTurnOrigin::TrustedAutomation { source: Subconscious }; a live chat turn runs under WebChat (web chat / TUI) or ExternalChannel (Telegram, Discord, …).

is_user_authored is an allowlist, for the same reason the permission gate uses one: a new origin is a turn nobody has classified yet, and mistaking host text for a user message writes it where the user's own words belong, indistinguishable afterwards. Cli is excluded on the strength of its own doc comment — "command-line / sub-agent / one-off internal invocation" — and an unscoped Unknown is excluded because turn_origin already documents that every entry point must scope a real origin.

Tradeoff: a caller that relays a person's text without scoping an origin stops autosaving. That is the same contract the permission gate enforces, and both production user paths scope one today (web_chat/ops.rsWebChat, channels/runtime/dispatch/processor.rsExternalChannel).

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — an_automation_turn_does_not_store_its_prompt_as_the_users_memory, an_unscoped_turn_stores_no_user_message, and the existing round-trip test now scopes WebChat as production does
  • Diff coverage ≥ 80% — the changed lines are the origin predicate and the autosave gate, both covered in each direction by the tests above
  • Coverage matrix updated — N/A: behaviour-only change
  • All affected feature IDs from the matrix are listed under ## RelatedN/A: behaviour-only change
  • No new external network dependencies introduced
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release-cut surface
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

Desktop / CLI / channels: unchanged for user turns. Internal automation turns (cron, subconscious, goal continuation, workflow) stop writing user_msg:* conversation documents, so the memory store keeps only what a person sent and recall stops competing with prompt boilerplate. No migration: rows written before this change are left in place deliberately.

Related

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/autosave-user-turns-only
  • Commit SHA: 6d46baecaad7ba82cd09cb40efb40321d6906657

Validation Run

  • pnpm --filter openhuman-app format:check — N/A: no frontend files changed
  • pnpm typecheck — N/A: no frontend files changed
  • Focused tests: agent::tests 100, turn_origin 3, agent::harness::session 205, memory_goals 7 — all green
  • Rust fmt/check (if changed): cargo fmt --all, cargo clippy -p openhuman -- -D warnings clean
  • Tauri fmt/check (if changed): N/A: no Tauri files changed

Summary by CodeRabbit

  • New Features

    • Added support for identifying direct-chat messages as user-authored.
    • Direct-chat actions now proceed without an approval prompt, consistent with interactive chat.
  • Bug Fixes

    • User messages from direct chats and external channels are now saved to conversation history.
    • Internal, automated prompts are no longer incorrectly saved as user messages.
    • Unscoped or non-user-authored turns continue to be excluded from conversation autosave.

@yh928
yh928 requested a review from a team August 1, 2026 00:40
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: ad9d3ed0-858f-4b76-bffc-9f98a4c1462d

📥 Commits

Reviewing files that changed from the base of the PR and between 8e65c40 and 1748ce6.

📒 Files selected for processing (8)
  • src/openhuman/agent/agent_tests.rs
  • src/openhuman/agent/agent_tests_part_01_tests.rs
  • src/openhuman/agent/agent_tests_part_03_tests.rs
  • src/openhuman/agent/harness/session/turn/core_turn.rs
  • src/openhuman/agent/turn_origin.rs
  • src/openhuman/inference/local/ops_part_01.rs
  • src/openhuman/security/approval/gate_intercept.rs
  • src/openhuman/security/approval/gate_tests_part_03_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/openhuman/agent/turn_origin.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The change adds DirectChat, classifies user-authored origins, gates conversation autosave on that classification, updates local chat fallback behavior, and allows direct-chat approval requests without prompting. Tests cover user, automation, unscoped, and approval flows.

Changes

User-authored autosave

Layer / File(s) Summary
User-authored origin classification
src/openhuman/agent/turn_origin.rs
Adds AgentTurnOrigin::DirectChat, is_user_authored, and current_is_user_authored. WebChat, ExternalChannel, and DirectChat are user-authored.
Direct-chat origin integration
src/openhuman/inference/local/ops_part_01.rs, src/openhuman/security/approval/gate_intercept.rs, src/openhuman/security/approval/gate_tests_part_03_tests.rs
Uses DirectChat as the local chat fallback. The approval gate allows DirectChat without prompting.
Autosave guard and regression coverage
src/openhuman/agent/harness/session/turn/core_turn.rs, src/openhuman/agent/agent_tests.rs, src/openhuman/agent/agent_tests_part_01_tests.rs, src/openhuman/agent/agent_tests_part_03_tests.rs
Autosave stores user messages only inside user-authored origin scopes. Tests cover WebChat, ExternalChannel, DirectChat, TrustedAutomation, and unscoped turns.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 1748c

The change limits conversation-memory autosave to turns authored by a person and prevents internal prompts from being stored as user messages. No actionable merge-blocking risk remains, so it is merge-ready after normal checks and review.

Suggested labels: rust-core, agent, memory, bug

Suggested reviewers: senamakel

Poem

A rabbit guards the message gate
Direct chats now arrive as fate
Web turns save what people say
Automation hops away
No scope, no memory trail
Clean recall follows the trail

🚥 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 pull request title 'fix(memory): store only what a person sent as a conversation memory' directly describes the main change. The changeset adds an origin-based gate to the conversation-memory auto…
Linked Issues check ✅ Passed The pull request meets all acceptance criteria from issue #5312. It prevents internal-agent turns from writing user_msg:* conversation documents by introducing an origin-based gate (`current_is_user…
Out of Scope Changes check ✅ Passed All changes are directly within the scope of issue #5312. The PR introduces the DirectChat origin variant and user-authorship classification functions to support the origin-based gate [turn_origin.r…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 8 files.
Full details: Title check

Explanation

The pull request title 'fix(memory): store only what a person sent as a conversation memory' directly describes the main change. The changeset adds an origin-based gate to the conversation-memory autosave system to exclude internal-agent turns and unscoped turns, storing only user-authored turns as conversation memories. This title captures the core objective and is specific and concise.

Full details: Linked Issues check

Explanation

The pull request meets all acceptance criteria from issue #5312. It prevents internal-agent turns from writing user_msg:* conversation documents by introducing an origin-based gate (current_is_user_authored()) in the session-level autosave logic [core_turn.rs]. It preserves autosave for WebChat and ExternalChannel turns by classifying them as user-authored [turn_origin.rs]. It applies the behavior generally at the session level via a single conditional gate in core_turn.rs rather than requiring each internal agent to opt out. It provides regression coverage with positive tests for user-authored origins (WebChat, ExternalChannel, DirectChat) in agent_tests_part_03_tests.rs and negative tests for automation (TrustedAutomation) and unscoped turns. The PR adds 159 lines of test code and 52 lines of new origin logic for an estimated diff coverage well above 80%. It does not modify existing conversation-memory rows, addressing only the write path as specified.

Full details: Out of Scope Changes check

Explanation

All changes are directly within the scope of issue #5312. The PR introduces the DirectChat origin variant and user-authorship classification functions to support the origin-based gate [turn_origin.rs]. It adds a test module and helper to verify origin-gated autosave behavior [agent_tests.rs, agent_tests_part_03_tests.rs]. It updates existing tests to use proper origin scopes [agent_tests_part_01_tests.rs]. It implements the session-level autosave gate [core_turn.rs]. It updates the effective-origin fallback and documents why DirectChat is preferred [ops_part_01.rs]. It extends the approval gate to handle DirectChat uniformly with Cli [gate_intercept.rs, gate_tests_part_03_tests.rs]. All changes serve the stated objective of preventing internal-agent prompts from being stored as conversation memories while preserving user-authored turn autosave.

Warning

Your free Security trial is over. An organization admin can upgrade to Advanced for continuous pull request security review or dismiss this notice.


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

@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels Aug 1, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/agent/tests.rs`:
- Around line 738-744: Extend the negative autosave test wait in
src/openhuman/agent/tests.rs lines 738-744 to use the positive test’s
50-iteration, one-second polling window before checking mem.list; apply the same
wait change at lines 764-767. Update both sites consistently, or replace both
waits with a deterministic store-call signal.
- Around line 658-671: Extend the autosave regression coverage in the existing
test around the WebChat-scoped turn by adding an
AgentTurnOrigin::ExternalChannel turn through turn_origin::with_origin. Verify
the resulting persisted documents include a user_msg: entry, matching the
existing positive WebChat persistence assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa187edd-e21a-4d0c-b775-f1071c794c07

📥 Commits

Reviewing files that changed from the base of the PR and between 43cc1b4 and 6d46bae.

📒 Files selected for processing (3)
  • src/openhuman/agent/harness/session/turn/core.rs
  • src/openhuman/agent/tests.rs
  • src/openhuman/agent/turn_origin.rs

Comment thread src/openhuman/agent/tests.rs Outdated
Comment thread src/openhuman/agent/tests.rs Outdated
@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a bug where internal agent prompts (e.g., memory_goals::enrich) were being stored as MemoryCategory::Conversation documents keyed user_msg:<uuid>, indistinguishable from actual user messages, because config-built internal agents inherit the auto_save flag.

  • Adds AgentTurnOrigin::DirectChat to decouple the "trusted caller" axis (approval gate) from the "person authored this text" axis (autosave), and migrates agent_chat RPC from Cli to DirectChat so direct-chat messages are saved.
  • Adds is_user_authored() to AgentTurnOrigin as an allowlist (WebChat | ExternalChannel | DirectChat), and gates autosave in turn/core.rs on both auto_save and current_is_user_authored().
  • New tests cover: external-channel turns store, direct-chat turns store, automation turns do not store, and unscoped turns do not store.

Confidence Score: 5/5

Safe to merge. The change only restricts the autosave write path — no reads, no data deletion, no migration — and the two production user-facing paths (WebChat, ExternalChannel) are correctly included in the allowlist.

The fix is a single boolean guard at the one chokepoint where all autosaves originate, backed by an allowlist that fails closed on unrecognised origins. The new DirectChat variant is correctly plumbed through both the autosave predicate and the approval gate without changing the gate's trust decision. Tests cover all four branches with a symmetric polling window that would catch a broken guard even if it fires asynchronously.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
src/openhuman/agent/turn_origin.rs Adds DirectChat variant, is_user_authored() allowlist method, and current_is_user_authored() free function. Design is sound — allowlist ensures new origins fail closed.
src/openhuman/agent/harness/session/turn/core.rs Autosave gate tightened to require both auto_save and current_is_user_authored(); fix is a single-line addition at the right chokepoint.
src/openhuman/inference/local/ops.rs agent_chat RPC switched from Cli to DirectChat; approval-gate behavior is unchanged, but now correctly marks the turn as user-authored so direct-chat messages are saved.
src/openhuman/security/approval/gate.rs DirectChat added to the Cli match arm with no change in trust policy; origin.class() is now logged so the distinction is visible in traces.
src/openhuman/agent/tests.rs Four new tests covering positive (ExternalChannel, DirectChat) and negative (TrustedAutomation, unscoped) autosave cases with a symmetric polling window.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["agent.turn(user_message)"] --> B{"self.auto_save?"}
    B -- No --> C[Skip autosave]
    B -- Yes --> D{"current_is_user_authored()"}
    D -- false --> C
    D -- true --> E[tokio::spawn autosave]
    E --> F["memory.store user_msg:uuid"]
Loading

Reviews (3): Last reviewed commit: "fix(agent): keep autosave for the direct..." | Re-trigger Greptile

@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: 6d46baecaa

ℹ️ 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/turn_origin.rs Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/agent/tests.rs`:
- Around line 704-710: Update the key collection flow around Memory::list in the
affected test helper to propagate listing failures instead of converting them
into an empty collection. Replace unwrap_or_default with unwrap, or change the
helper to return Result and propagate the error, while preserving the existing
key-mapping behavior on success.

In `@src/openhuman/approval/gate.rs`:
- Around line 819-830: Add a regression case to the existing approval tests that
invokes the gate through with_origin using AgentTurnOrigin::DirectChat and
asserts GateOutcome::Allow without parking. Keep the test setup and assertions
aligned with the existing Cli coverage to verify both origins follow the same
approval path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 03baa7f7-138f-4607-a10a-8a288cfbf846

📥 Commits

Reviewing files that changed from the base of the PR and between 6d46bae and 6170a77.

📒 Files selected for processing (4)
  • src/openhuman/agent/tests.rs
  • src/openhuman/agent/turn_origin.rs
  • src/openhuman/approval/gate.rs
  • src/openhuman/inference/local/ops.rs

Comment thread src/openhuman/agent/tests.rs Outdated
Comment thread src/openhuman/security/approval/gate.rs Outdated
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot added the memory Memory store, memory tree, recall, summarization, and embeddings in src/openhuman/memory/. label Aug 5, 2026
yh928 added a commit to yh928/openhuman that referenced this pull request Aug 5, 2026
`poll_for_stored_user_message` turned a failed `Memory::list` into an empty key
list, so the tests that assert on which keys the autosave wrote could conclude
"no `user_msg:` key was stored" from a storage error rather than from the
behaviour under test — the automation and unscoped cases would pass without
reading storage at all. It now expects, and says why in the message.

Also adds the `DirectChat` approval case. `Cli` and `DirectChat` share one arm
because this gate decides on trust, not on whether the turn's text was
person-written — and an arm covered by only one of its origins is an arm that
can be split without anything failing. The test asserts `Allow` and that nothing
was parked, since allowing without a prompt means exactly that.

approval::gate 50, agent::tests 107 pass.

Reported by CodeRabbit on tinyhumansai#5313.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy

@greptile-apps greptile-apps 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 5, 2026
@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Maintainer review — no changes pushed, read-only assessment.

State: CONFLICTING, 6,576 commits behind main, untouched since 2026-08-05.

The bug is still live on main, unchanged. I checked rather than assuming, because the memory subsystem was largely rewritten in that window (ba088ec20 deleted 282 files / 72,489 lines of the legacy memory tree). The autosave survived the rewrite intact:

// src/openhuman/agent/harness/session/turn/core_turn.rs:242
if self.auto_save {
    ...
    let autosave_key = format!("user_msg:{}", uuid::Uuid::new_v4());
    ... memory.store(CONVERSATION_RAW_NAMESPACE, &autosave_key, &user_msg,
                     MemoryCategory::Conversation, session_id_for_autosave.as_deref())

Still gated on self.auto_save alone, still no notion of who authored the turn. An internal agent built with Agent::from_config_for_agent(...) still inherits the flag and still files the host's own prompt as the user's conversation memory. Your memory_goals::enrich reproduction stands.

What rebasing costs

The file moved: session/turn/core.rssession/turn/core_turn.rs. AgentTurnOrigin still exists (agent/turn_origin.rs, used from embed/harness/access.rs), so the type your gate hangs off is intact and is_user_authored is still absent — the addition is still the addition. The other three files (agent/tests.rs, inference/local/ops.rs, security/approval/gate.rs) will need their current homes checked; note agent/tests.rs no longer matches the layout gate's naming rule, which now requires descriptive *_tests.rs siblings.

One thing that has changed under you, and is worth a decision

main now writes this document into a dedicated CONVERSATION_RAW_NAMESPACE and applies a same-session exclusion filter in UnifiedMemory::recall / memory_hybrid_search, so the agent's own triggering request no longer echoes back within the turn. That is a partial, different mitigation of the neighbouring complaint (#5312).

It does not subsume your fix: segregating the namespace and filtering the current session does not stop an internal agent's host prompt being stored as user-authored conversation, and it does not stop that row surfacing in a later session's recall. Gating the write by origin is still the correct fix, and it is upstream of the filter.

Worth being explicit about it in the PR description when you rebase, though, because a reviewer looking at main today will see the namespace + filter and reasonably ask whether this is still needed. It is.

Related: #5315 (same author, same area) proposes removing the conversation copies outright rather than gating them. If both are still wanted they need sequencing — the two answers overlap.

Not approving; a maintainer reviews and merges.

yh928 and others added 3 commits September 2, 2026 20:57
`auto_save` says the workspace keeps its chat in memory; it did not say whether
a turn is chat at all. An internal agent is built from the same config
(`Agent::from_config_for_agent`), so it inherits the flag — and its "user
message" is the prompt the host wrote for it.

Live, that put `memory_goals::enrich`'s prompt in the `global` namespace as a
`Conversation` document keyed `user_msg:<uuid>`:

    "Maintain the existing goals list. Call goals_list first, then make the
     MINIMAL set of changes (goals_add / goals_edit / goals_delete)…
     ## Context
     Recent conversation recap (segment seg-18c746e3…)…"

Prompt boilerplate then competes for slots in every later recall, which is what
made a namespace-wide search read like a transcript dump.

The distinction already existed: `AgentTurnOrigin`. `WebChat` and
`ExternalChannel` carry what a person sent; `TrustedAutomation` (cron,
subconscious, goal continuation, workflow), `Cli` — documented as "command-line
/ sub-agent / one-off internal" — and an unscoped `Unknown` carry host text.
`AgentTurnOrigin::is_user_authored` is an allowlist for the same reason the
permission gate uses one: a new origin is a turn nobody has classified, and
mistaking host text for a user message writes it where the user's own words
belong, indistinguishable afterwards.

Gated at the session, not at each caller, so a new internal agent cannot forget
to opt out — the kind of omission nothing surfaces until the store is inspected.

Existing rows are left alone; this is the write path only.

Tests: an automation turn and an unscoped turn store no `user_msg:*` document;
the existing round-trip test now scopes `WebChat`, as every production entry
point does. agent::tests 100, turn_origin 3, agent::harness::session 205,
memory_goals 7. clippy clean.

Closes tinyhumansai#5312

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy
… allowlist

Review follow-ups on the user-authored autosave gate.

`agent_chat` scoped `AgentTurnOrigin::Cli`, and its comment says why: to tell
the approval gate "trusted caller, do not fail closed". That variant also
covers sub-agent and internal invocations, so reusing it to answer "did a
person write this" dropped a real user message — the desktop Settings
agent-chat panel calls this RPC, and with `memory.auto_save` on its messages
stopped being stored.

The two questions need two variants. `DirectChat` is user-authored and shares
the gate's `Cli` arm, so the trust decision is unchanged and cannot drift.

Tests:
- `ExternalChannel` gets its own positive case; the allowlist had three
  members and only `WebChat` was covered by a stored-message assertion.
- `DirectChat` gets one, so the regression above stays fixed.
- Both negative tests now poll the same one-second window as the positive
  ones via `poll_for_stored_user_message`. The store is fire-and-forget, so a
  fixed 200 ms sleep would let a broken guard pass while failing live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy
`main` moved the conversation autosave into a dedicated
`CONVERSATION_RAW_NAMESPACE`, so the helper's `list(None, …)` came back
empty. That failed the two positive cases outright — and, worse, would
have passed the two negative ones without ever reading storage, which is
the exact false green the helper's own doc comment warns about.

List the namespace explicitly so both directions read the same rows.
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.0089 · 96,635 in / 1,884 out · 512 cached (1%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash · 692 embedded
critique:    $0.0043 · 47,475 in / 340 out   · 0 cached (0%)   · deepseek/deepseek-v4-flash
security:    $0.0020 · 23,116 in / 147 out   · 512 cached (2%) · deepseek/deepseek-v4-flash
tests:       $0.0017 · 16,641 in / 1,320 out · 0 cached (0%)   · deepseek/deepseek-v4-flash
description: $0.0008 · 9,403 in  / 77 out    · 0 cached (0%)   · deepseek/deepseek-v4-flash

@tinysweeper

tinysweeper Bot commented Sep 2, 2026

Copy link
Copy Markdown

How this change flows

4 changed behaviours across 22 relationships. 6 surrounding behaviours are shown (60 graph nodes walked). 37 further behaviours left out to keep the diagram readable.

flowchart LR
  n0["AgentTurnOrigin<br/>changed"]:::changed
  n1["grant_turn_cwd<br/>changed"]:::changed
  n2["grant_turn_cwd_is_the_only_mutation<br/>changed"]:::changed
  n3["...ept_with_cli_origin_allows_without_prompt<br/>changed"]:::changed
  n4["with_origin"]:::impacted
  n5["test_gate"]:::impacted
  n6["intercept_audited"]:::impacted
  n7["flow_origin"]:::impacted
  n8["chat_origin_park_has_no_source_context"]:::impacted
  n9["..._insert_flow_trust_composes_to_auto_allow"]:::impacted
  n2 -->|calls| n1
  n2 -->|tests| n1
  n3 -->|calls| n4
  n3 -->|tests| n4
  n3 -->|calls| n5
  n3 -->|tests| n5
  n4 -->|uses| n0
  n7 -->|uses| n0
  n8 -->|calls| n4
  n8 -->|tests| n4
  n8 -->|calls| n5
  n8 -->|tests| n5
  n8 -->|calls| n6
  n8 -->|tests| n6
  n9 -->|calls| n4
  n9 -->|tests| n4
  n9 -->|calls| n5
  n9 -->|tests| n5
  n9 -->|calls| n6
  n9 -->|tests| n6
  n9 -->|calls| n7
  n9 -->|tests| n7
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading

Green: changed behaviour. Grey: surrounding behaviour. Arrows name the call, use, implementation, or test relationship. Orange: has findings. Red: has a finding that blocks the merge.

tinysweeper 0.1.0

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Sep 2, 2026
@yh928

yh928 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main (8e65c4008) and pushed — 1748ce6f2. Thanks for checking the bug was still live rather than assuming; that saved the wrong kind of rebase.

Where the change landed. The four moves your review flagged, plus one you couldn't have known about:

was now
session/turn/core.rs session/turn/core_turn.rs (core.rs is a header + include!)
agent/tests.rs (deleted) new agent_tests_part_03_tests.rs, wired from agent_tests.rs
inference/local/ops.rs inference/local/ops_part_01.rs
security/approval/gate.rs gate_intercept.rs (arm) + gate_tests_part_03_tests.rs (test)

turn_origin.rs merged clean, so DirectChat and is_user_authored sit where they did.

The tests went to a new part file rather than into agent_tests_part_01_tests.rs: appending them there put it at 789 lines, over the layout gate's 750 limit.

main also refactored the agent_chat origin under me, and the result is better. It now reads effective_agent_chat_origin(), which keeps an ambient origin an embedder scoped and only falls back to a default. I changed the fallback from Cli to DirectChat, which is a strictly smaller change than the original and leaves the embedder path alone.

A real defect the rebase exposed. main moved the autosave into a dedicated CONVERSATION_RAW_NAMESPACE, so the test helper's mem.list(None, …) came back empty. That failed the two positive cases outright — and would have let the two negative cases pass without ever reading storage, which is the exact false green the helper's own doc comment was written to prevent. It now lists the namespace explicitly, so both directions read the same rows. Worth flagging because that failure mode is silent by construction.

On your "worth a decision" point — agreed, and stated here for the next reader: main's namespace split plus the same-session exclusion filter is a different mitigation. It stops this document echoing back inside the turn that wrote it. It does not stop an internal agent's host prompt being written as user-authored conversation, and does not stop that row surfacing in a later session's recall. This gate is upstream of the filter. I've left the reasoning in the code comment at the gate so a reviewer looking at main today doesn't have to re-derive it.

Verified locally: cargo test --lib on the touched paths — 7/7 pass (4 new, plus both pre-existing auto_save_* and the new intercept_with_direct_chat_origin_allows_without_prompt). cargo fmt --check, layout gate, and the full pre-push suite clean.

Re #5315 (remove the conversation copies outright): still true that the two overlap and need sequencing. This one is the smaller, reversible half — it changes what gets written, not whether the feature exists — so it should be safe to land first regardless of how #5315 goes.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 2, 2026
`effective_agent_chat_origin` now falls back to `DirectChat` rather than
`Cli`, and this test still pinned the old label — CI caught it, I did not.

Also asserts `is_user_authored()`, which is the whole reason for the new
variant: `Cli` answers the trust question correctly and the authorship
question wrongly, so a real user message typed into the desktop Settings
panel would have been dropped from conversation memory.
@yh928

yh928 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

CI caught something I missed, and it was a real one — fixed in 1c11ad3c7.

inference::local::ops::tests::effective_origin_defaults_to_cli_outside_any_scope asserted the unscoped agent_chat origin is Cli. This PR changes that fallback to DirectChat, and I updated the code without updating the test that pins it. Straightforwardly my error.

The test now asserts DirectChat and is_user_authored(), which is the point of the new variant rather than an incidental property: Cli answers the trust question correctly and the authorship question wrongly, so labelling this RPC Cli would silently drop a real user message — someone typing into the desktop Settings agent-chat panel — out of conversation memory. That is the bug the variant exists to prevent, so the test should fail if a future change collapses the two again.

Verified against the coverage lane's filter locally: cargo test --lib -- 'core::runtime' 'openhuman::agent' 'openhuman::inference::local' 'openhuman::security::approval'2712 passed, 0 failed.

@senamakel
senamakel merged commit 4fa0194 into tinyhumansai:main Sep 11, 2026
27 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Sep 11, 2026
senamakel added a commit to HDZTony/openhuman that referenced this pull request Sep 11, 2026
…ns-only\n\nfix(memory): store only what a person sent as a conversation memory\n
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 memory Memory store, memory tree, recall, summarization, and embeddings in src/openhuman/memory/. priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Internal agents store their own prompts as the user's conversation memories

3 participants