Skip to content

fix(memory): helpers and the reader respect a chat's memory-off setting - #529

Merged
itsskofficial merged 2 commits into
mainfrom
fix/525-memory-gate
Oct 27, 2026
Merged

itsskofficial merged 2 commits into
mainfrom
fix/525-memory-gate

Conversation

@itsskofficial

@itsskofficial itsskofficial commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Closes #525.

A chat turn that shouldn't see memories (an incognito chat with "use what Sentient knows" off) had the memory tools taken out of its own list, but whatever it started built a fresh list from the full set: a helper (delegate_task / delegate_tasks) got every memory and past-chat search tool back, and the reader (read_with_reader) ran memory_recall by name. On main the incognito case only stayed safe because each memory tool also checks whether the chat is incognito. A turn without memory that isn't an incognito chat (the /v1 API turns in #479) has no such backstop, which is how the #479 tester saw a stored fact come back.

The fix: one decision per turn, carried in the turn's context, applied in code wherever tools are handed on.

  • Agent.memory_allowed(session, channel) decides once per chat turn whether the turn may use what Sentient knows. Today: false in an incognito chat while chat.incognito_uses_memory is off. The turn's prompt and its ToolContext.memory_allowed both come from that one answer.
  • ToolContext.memory_allowed is copied into everything the turn starts: helpers (foreground, parallel and background), the reader, and execute_code scripts (the script's tool context now takes the calling run's memory_allowed and incognito).
  • Without memory, the memory tools (memory_recall, memory_search_by_source, memory_search_history, history_semantic_search, history_time_search, user_model_ask, memory_forget; one set, MEMORY_TOOLS in tools/base.py):
    • are never offered by run_loop, whoever built the list (chat, helper, task);
    • are left out of a helper's list even when the goal names them;
    • are refused by the reader before anything runs;
    • are not listed to scripts and are refused by the script bridge;
    • refuse to run in Tool.call itself, however they are reached.
  • A helper of such a turn also gets no user name in its prompt (it already got no memories or profile).
  • An incognito chat's helpers are now never offered memory_remember / skill_save either (they refused before, but were offered), so a helper's list is never wider than its chat's.

How #479 plugs in: add its condition to Agent.memory_allowed, for example if channel == "api" and not self.config.openai_api.allow_memory: return False. That one line then covers the API turn's prompt, its own tools and every helper, reader call and script it starts, so API_NO_MEMORY_TOOLS and the extra filter after _incognito_tools in #479 can go (memory_forget is already in MEMORY_TOOLS). INCOGNITO_MEMORY_TOOLS and Agent._incognito_tools are gone here; run_loop does that filtering now.

How I tested it

  • New tests/test_memory_off_turns.py (8 tests): a helper of a memory-off incognito chat gets no memory tools, no saving tools and no name, and its forced memory_recall call is refused; parallel and background helpers inherit; an incognito chat with memory on still lets helpers read but never save; the reader refuses memory_recall; a normal chat is unchanged (the helper gets memory tools and finds the fact); a turn made memory-off by another rule (standing in for feat(api): let other apps chat with Sentient through an OpenAI-compatible API #479's hook) covers its prompt, its tools and its helpers in a non-incognito chat; Tool.call refuses directly; a script gets no memory tools. 7 of them fail on main.
  • ruff check sentient tests packaging: clean.
  • Affected areas (incognito, subagents, sandbox, toolselect, memory): 425 passed.
  • Full suite: 2988 passed, 3 skipped.

Real test

Setup: engine from this branch and from origin/main (8c13aaf), each with its own fresh SENTIENT_HOME seeded with the fictional Maya Rao data (seed-memory-skills.py and seed-chats-devices-channels.py, which store "Maya's sister Anika is a school teacher in Mysuru"), a random token, and chat.incognito_uses_memory: false. Driven over POST /api/chat. A small harness logged every model call's offered tool names (no product code changed for that). The Ollama lock was held for every run, with a wall-clock watchdog.

ollama_chat/qwen3:8b (primary, fast and helpers). Asked in an incognito chat: "Use the delegate_task tool: start a helper and have it search your memories and past chats for everything you know about Maya's sister Anika", then "Call read_with_reader with tool memory_recall ...".

On main:

helper executor tools 10 mem= [history_semantic_search, history_time_search, memory_recall, memory_remember,
                               memory_search_by_source, memory_search_history, user_model_ask]
  helper ran: memory_recall -> []   memory_search_by_source -> []   memory_search_history -> []
              history_semantic_search -> "No relevant conversation summaries found."
read_with_reader(tool="memory_recall") -> ran; a reader subagent completed with {"job": null, "where_she_lives": null}

So the helper was handed and ran four memory and history searches, and the reader ran memory_recall. Nothing came back only because each tool also checks for an incognito chat, a check a non-incognito memory-off turn (#479) doesn't get.

On this branch:

helper executor tools 3 mem= []        (its goal named history_semantic_search; dropped)
read_with_reader -> {"error": "memory_recall isn't available in this chat: it doesn't use what Sentient knows about the user."}
                    no reader subagent was started

The fact (Mysuru, school teacher) appears in no model call of either incognito turn.

Normal (not incognito) chat on the branch: the chat's prompt has the memory, the helper is offered memory_recall, and the reply uses what Sentient knows. (The seeded home has no real embeddings, so memory_recall itself returns [] on both branches; the memory in the prompt is what answers.)

claude-code/sonnet (chat; helpers on ollama_chat/qwen3:8b, since helpers can't run on Claude Code):

  • The incognito chat's own tool list: main offered 47 tools including memory_forget; the branch offers 46 with no memory tool.
  • Asked directly to delegate a memory search or call the reader on memory_recall, Sonnet declined on both branches (it follows the incognito note), so the reader path couldn't be exercised on Claude.
  • A neutral ask ("Use delegate_task to have a helper write a three-line birthday note for my sister Anika, using whatever it can find out about her"): on main the helper was offered all 7 memory tools and called memory_recall; on the branch the helper got 1 tool (web_search), no memory tools, and wrote a general note.
  • Normal chat: Sonnet answered "Anika is a school teacher in Mysuru" from memory on both branches.

Not tested for real: #479's /v1 path (that code isn't on main). The hook it would use is covered by test_another_rule_for_a_turn_without_memory_plugs_in_once.

Checklist

  • Tests added or updated, and pytest passes locally for what I touched
  • ruff check sentient tests is clean
  • docs/API.md updated (section 2 incognito notes; section 10 "Turns without memory")
  • A changelog entry: changes/525.fixed.md
  • No keys, tokens or personal data in the code, tests or screenshots

Summary by CodeRabbit

  • Bug Fixes
    • Incognito chats with memory disabled no longer expose stored memories or the user’s name to helpers and readers.
    • When a turn runs without memory, memory and history tools are unavailable to the main chat, helpers, readers, and scripts, and calls to those tools are refused.
    • Chats configured to allow memory retain memory access; normal chats are unaffected.
  • Documentation
    • Updated API guidance to explain memory access and tool availability for incognito chats and turns without memory.

A chat turn decides once whether it may use what Sentient knows (Agent.memory_allowed) and carries the
answer in ToolContext.memory_allowed. run_loop, helpers, the reader and execute_code scripts all apply it,
and the memory tools refuse to run for a run without memory.

Closes #525
@github-actions github-actions Bot added documentation Improvements or additions to documentation area: engine Agent loop, models, approvals, storage, gateway area: sandbox Code execution labels Oct 27, 2026
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1a1f6f93-351b-42a0-a010-b98b91134e79
Commits

Reviewing files that changed from the base of the PR and between 27af251 and 102b325.

Files selected for processing: 9
  • changes/525.fixed.md
  • docs/API.md
  • sentient/agent/loop.py
  • sentient/agent/subagents.py
  • sentient/sandbox/policy.py
  • sentient/sandbox/service.py
  • sentient/tools/base.py
  • sentient/tools/builtin/subagents_tool.py
  • tests/test_memory_off_turns.py

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Walkthrough

Each turn now determines whether memory is allowed. That decision controls memory-tool availability and calls in the chat, delegated helpers, readers, and sandbox scripts. Tests cover disabled-memory behavior and cases where memory remains available.

Changes: Memory access by turn

Layer / File(s) Summary
Shared memory-tool guard
sentient/tools/base.py, tests/test_memory_off_turns.py
ToolContext now carries a memory permission setting. Memory and history tools can be filtered from tool lists, and calls to those tools return an error when memory is disabled.
Agent turn policy
sentient/agent/loop.py, docs/API.md, tests/test_memory_off_turns.py
The agent determines memory permission for each turn and uses it for prompt construction and tool selection. Quick turns also set the permission on the tool context.
Helper and reader propagation
sentient/agent/subagents.py, sentient/tools/builtin/subagents_tool.py, docs/API.md, changes/525.fixed.md, tests/test_memory_off_turns.py
Delegated helpers inherit the parent’s memory setting, and memory tools are filtered even when explicitly requested. The reader refuses withheld sources. Tests cover helpers, readers, and memory-enabled cases.
Sandbox script enforcement
sentient/sandbox/policy.py, sentient/sandbox/service.py, tests/test_memory_off_turns.py
Sandbox policies use the originating context’s memory setting to exclude memory tools and refuse calls to them. Tests cover policy checks and script tool lists.

Priority: Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant ToolContext
  participant SubagentTool
  participant SubagentManager
  Agent->>ToolContext: Set memory_allowed for the turn
  ToolContext->>SubagentTool: Provide turn context
  SubagentTool->>SubagentManager: Pass memory_allowed to delegated work
  SubagentManager->>SubagentManager: Filter memory tools when disallowed
Loading

Suggested labels: documentation, area: engine, area: memory

Merge Risk: Minimal · up to 102b3

The change makes memory-off chats consistently withhold memory tools from helpers, the reader, and scripts. No concrete merge-blocking risk remains.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surv… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "fix(memory): helpers and the reader respect a chat's memory-off setting" matches the main change. The changeset makes one per-turn memory_allowed decision flow to the chat, helpers, the sub…
Linked Issues check Passed The changes satisfy #525. Agent.run_turn makes one memory_allowed decision and copies it to ToolContext. run_loop removes MEMORY_TOOLS from every offered list. Tool.call, `read_with_reader…
Out of Scope Changes check Passed The changes stay within #525. The code changes enforce turn-wide memory restrictions. The tests verify those restrictions and preserve normal-chat behavior. The documentation and changelog describe th…
Full details: Title check

Explanation

The title "fix(memory): helpers and the reader respect a chat's memory-off setting" matches the main change. The changeset makes one per-turn memory_allowed decision flow to the chat, helpers, the subagent reader, and sandbox scripts. Memory tools are filtered from offered lists and refused when called directly while memory is off. Helpers also omit the user's name and saving tools in incognito chats. The title is specific, uses a conventional commit prefix, and is under 72 characters. It does not name the incognito-only scope or the sandbox scripts, which is acceptable for a short title.

Full details: Linked Issues check

Explanation

The changes satisfy #525. Agent.run_turn makes one memory_allowed decision and copies it to ToolContext. run_loop removes MEMORY_TOOLS from every offered list. Tool.call, read_with_reader, helper delegation, and the sandbox bridge refuse withheld tools. Helpers also inherit the decision and omit the user name when memory is disabled. tests/test_memory_off_turns.py covers helper, parallel and background delegation, the reader, direct calls, scripts, normal chats, and another memory-off rule. The reported qwen3:8b run covers the required real run. The /v1 hook is not present in the reviewed tree, but the generic decision hook and its non-incognito test are present.

Full details: Out of Scope Changes check

Explanation

The changes stay within #525. The code changes enforce turn-wide memory restrictions. The tests verify those restrictions and preserve normal-chat behavior. The documentation and changelog describe the same behavior. No unrelated product behavior is demonstrated.

Full details: Docstring Coverage

Explanation

Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 36 functions across 7 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the turn’s green light,
And keeps the memory tools in sight.
When memory is off, they stay away,
The helpers follow rules that day.
The reader does not peek or roam,
And scripts respect the setting at home.

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

@coderabbitai coderabbitai Bot added the area: memory Facts, user model, nightly consolidation label Oct 27, 2026
@itsskofficial
itsskofficial added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit c857e80 Oct 27, 2026
10 checks passed
@itsskofficial
itsskofficial deleted the fix/525-memory-gate branch October 27, 2026 15:58
itsskofficial added a commit to Tanmay-Ts/Sentient that referenced this pull request Oct 28, 2026
…e-api

Resolve the conflict with existence-master#529 (fix existence-master#525): a /v1 turn with openai_api.allow_memory off now goes through the shared Agent.memory_allowed gate, so its prompt, its tools and every helper, reader call and script it starts skip memory. Drop the PR's own API_NO_MEMORY_TOOLS filter (MEMORY_TOOLS covers it, memory_forget included) and add a test for a /v1 turn's helper and reader.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: engine Agent loop, models, approvals, storage, gateway area: memory Facts, user model, nightly consolidation area: sandbox Code execution documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incognito chats without memory can still reach memories through helpers and the reader

1 participant