Repository navigation
fix(memory): helpers and the reader respect a chat's memory-off setting - #529
Conversation
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
|
No actionable comments were generated in the recent review.
|
| 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:
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
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: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
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_turnmakes onememory_alloweddecision and copies it toToolContext.run_loopremovesMEMORY_TOOLSfrom 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.pycovers 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/v1hook 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.
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.
…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.
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) ranmemory_recallby 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/v1API 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 whilechat.incognito_uses_memoryis off. The turn's prompt and itsToolContext.memory_allowedboth come from that one answer.ToolContext.memory_allowedis copied into everything the turn starts: helpers (foreground, parallel and background), the reader, andexecute_codescripts (the script's tool context now takes the calling run'smemory_allowedandincognito).memory_recall,memory_search_by_source,memory_search_history,history_semantic_search,history_time_search,user_model_ask,memory_forget; one set,MEMORY_TOOLSintools/base.py):run_loop, whoever built the list (chat, helper, task);Tool.callitself, however they are reached.memory_remember/skill_saveeither (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 exampleif 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, soAPI_NO_MEMORY_TOOLSand the extra filter after_incognito_toolsin #479 can go (memory_forgetis already inMEMORY_TOOLS).INCOGNITO_MEMORY_TOOLSandAgent._incognito_toolsare gone here;run_loopdoes that filtering now.How I tested it
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 forcedmemory_recallcall is refused; parallel and background helpers inherit; an incognito chat with memory on still lets helpers read but never save; the reader refusesmemory_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.callrefuses directly; a script gets no memory tools. 7 of them fail on main.ruff check sentient tests packaging: clean.Real test
Setup: engine from this branch and from
origin/main(8c13aaf), each with its own freshSENTIENT_HOMEseeded with the fictional Maya Rao data (seed-memory-skills.pyandseed-chats-devices-channels.py, which store "Maya's sister Anika is a school teacher in Mysuru"), a random token, andchat.incognito_uses_memory: false. Driven overPOST /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:
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:
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, somemory_recallitself returns[]on both branches; the memory in the prompt is what answers.)claude-code/sonnet(chat; helpers onollama_chat/qwen3:8b, since helpers can't run on Claude Code):memory_forget; the branch offers 46 with no memory tool.memory_recall, Sonnet declined on both branches (it follows the incognito note), so the reader path couldn't be exercised on Claude.memory_recall; on the branch the helper got 1 tool (web_search), no memory tools, and wrote a general note.Not tested for real: #479's
/v1path (that code isn't on main). The hook it would use is covered bytest_another_rule_for_a_turn_without_memory_plugs_in_once.Checklist
pytestpasses locally for what I touchedruff check sentient testsis cleandocs/API.mdupdated (section 2 incognito notes; section 10 "Turns without memory")changes/525.fixed.mdSummary by CodeRabbit