Repository navigation
fix(reasoning): stop telling agents that reasoning left their context - #938
Merged
Merged
Conversation
#826 stored a `[reasoning]` notice for an agent whenever a binding compile stripped, or the provider dropped, thinking from its earlier replies ("The reasoning behind your earlier replies is no longer in your context…"). Text about the mechanics of an agent's own memory and reasoning gives it nothing to act on and keeps it thinking about it; under a binding model any tool-list change would have triggered one. Remove the notices (and the ReasoningLossTeller and notice builder that only served them). The operators' thinking-binding-loss alert and Agent.onThinkingStrip stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Automated review
Recommendation: merge. The review found nothing of medium or high severity.
Reviewed commit 664473a with claude-opus-5-5, by reading the code. It did not run the code or look at the checks.
Does this change belong in the project?
It fits the project. This PR removes the [reasoning] notices to agents, which shipped in 0.28.0 and are internal and unexported. It keeps the operator alert and the host's onThinkingStrip hook, and it follows the repo's conventions for tests, changelog and AI disclosure.
More
One small gap: the companion PR is named without saying which side should merge first.
Findings
No findings of medium or high severity.
5 more findings, less severe
- Low severity, documentation,
src/agent.ts:1446-1452. onThinkingStrip doc still says the framework sets its own observer that tells a[reasoning]line. The doc comment onAgent.onThinkingStripstill says "The framework sets it at the agent's first stream unless a host has set one, so a host's own observer stands in for the framework's, whose[reasoning]line for the agent then isn't told." This PR removes that assignment from framework.ts, so the framework never sets the hook and no[reasoning]line is ever told. A host author reading this comment would think setting the hook turns off a framework behaviour, and would think leaving it unset changes what the agent sees. Neither is true any more. The comment should say the hook is purely for hosts. - Low severity, maintainability,
src/framework.ts:24363-24369.branchis still passed in StreamThinkingDrops though nothing reads it. The only reader ofthinkingDrops.branchwas the removedtellReasoningLoss/reasoningLoss.forDropscall in driveStream. startStream still buildsthinkingBranchand passes it asbranch, andStreamThinkingDrops.branchis still declared in src/thinking-drops.ts:36. Nothing reads it now. This dead path suggests some caller still depends on the stream's branch, which makes later work on drop handling harder. Remove the field and the code that computes it, or document why it stays. - Low severity, maintainability,
src/framework.ts:26551-26554. Drops are placed on every reported call even when nothing will read the result.thinkingDrops.place(...)builds a signature index over the compiled messages and places every entry. It runs whenever a report exists, butplacedDropsis now read only insideif (thinkingDrops?.recordedAfter !== undefined). Before this change the reasoning-loss teller read it on every call. Now, on a branch with no binding record, the work is computed and thrown away on every call that drops blocks, and drops can repeat on each call of a stream. Move the placement inside therecordedAftercheck. - Low severity, test coverage,
test/reasoning-loss-turn.test.ts:170-178. The resident test cannot fail on main and no longer checks what it was written for. The old test ('tells the run's own agent, not the primary') checked that the worker got the line and the resident did not. The new version keeps onlyassert.deepEqual(resident, []), which already passed on main because the notice was always stored for the run's own agent. It also uses a response with no tool round. So this test adds no protection for this change. The "leaves a host's own strip observer" test also passed on main. Drop the resident test or merge it into the head-strip test. The PR's claim that three tests fail on main should also be checked against this. - Low severity, maintainability,
test/reasoning-loss-turn.test.ts:12. Leftover unused import and test option after the deleted tests.forkAgentName(line 12) was used only by the deleted conversation-fork test and is now unused. TheafterRunoption ofrun()(lines 96 and 117) was used only by the deleted 'agent no longer registered' test, and no caller passes it now. Both are dead code left over from the removal.
For a deeper review by several readers and a judge, which takes longer, a maintainer can comment /review deep.
tel-0s
added a commit
that referenced
this pull request
Oct 30, 2026
…s notices (#938) main at a64cfc0 is one merge past 71193a9, #938, with no release in it, so package.json stays 0.31.0 and this branch's floors stand (chronicle ^0.6.0, context-manager ^0.25.0, membrane ^0.8.0). #938 stops telling an agent that reasoning left its context: #826's `[reasoning] <agent>:` notices are no longer stored for it or passed to its stream, on a binding compile's strip or on a block the provider drops. src/reasoning-loss.ts (ReasoningLossTeller) and src/reasoning-loss-notice.ts go, with their unit tests; in framework.ts the teller, its forget at the three disposal sites, tellReasoningLoss, the onThinkingStrip the framework set at an agent's first stream, and the drops' notice after noteThinkingLoss go. The thinking-binding-loss ops alert and a host's own Agent.onThinkingStrip stay; thinkingBinding's docs say the agent is told nothing. 2 paths conflicted (5 regions). - src/framework.ts (1, imports): the ReasoningLossTeller import goes; this branch's presentation and read-marks imports beside it stay. The rest of #938's removals merged cleanly. Nothing on this branch read the notice past the teller: it was stored through addMessage with forAgent, as the framework's other notices are, and no presentation code names its kind or tag. onThinkingStrip had no other consumer in src: the framework's setting of it goes, as on main, and the hook stays for hosts. - test/reasoning-loss-turn.test.ts (4): main's version. This branch had read the head-change test onto #291 (windowOrder, from getPresentedMessages: the line, stored at once, entered the window at the tool boundary, after the first reply) and had run() return the teller's memory (remembered). Both were about the notice and go with it; no other assertion rode on them. Main's tests that no line reaches the agent (a head-change strip, drops on every call, a fold mid-turn that takes the turn's own step, a resident beside the run, a host's observer kept) and the Agent.onThinkingStrip hook test pass here unchanged. Read onto this branch past the conflicts: - src/agent.ts: Agent.onThinkingStrip's doc no longer says the framework sets it at the first stream, with a `[reasoning]` line a host's observer stands in for (main left that sentence); the framework sets none of its own and tells the agent nothing of a strip. branchHead's doc drops "for the reasoning-loss notice, which tells this turn's stored replies from earlier ones". Left as main has them: TurnRouting.start, set from Agent.branchHead() when a turn opens, has no reader now (reasoning-loss.ts was its only one); NOTICE_TAG.reasoning stays, so supplied text that opens `[reasoning]` is still marked. storeSees: no new site; main opens no context manager. agentSees: no new view decision. Notice tags: none new. Changelog: no release in main's range, so no fragment was consumed; main's no-reasoning-loss-notices.changed.md comes in as written. No 291-* fragment, docs/dendrite.md or README described the notice. Tests: built with zero errors; dist's compiled reasoning-loss and reasoning-loss-notice (src and test) removed, no other stale compiled file; fixtures copied. reasoning-loss-turn, presentation*, read-marks, thinking-binding*, foreign-rounds, thinking-drops, thinking-loss-alert and thinking-token-stamp (15 files): 160 tests, 160 pass. Full suite (npm test, at concurrency 4 under load): 9638 tests, 9589 pass, 0 fail, 49 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An agent is no longer told when reasoning leaves its context. #826's
[reasoning] <agent>:notices ("The reasoning behind your earlier replies is no longer in your context, because your tools changed… Re-read before relying on an earlier decision.", and the per-reply form) are no longer stored for an agent or passed to its stream, whether a binding compile strips thinking or the provider drops a block.Why
This is standing, recurring text about the mechanics of the agent's own memory and reasoning. It gives the agent nothing it can act on, and it keeps the agent thinking about it. Under a binding model, any change to the tool list strips thinking, so with the host's toolsets (tabs, settings, visibility) agents would have received these notices routinely. It also depended on the host's binding record, which doesn't match where binding actually applies (no account dimension; models marked binding that aren't). It shipped in 0.28.0 with only the automated review; no resident has received one yet, since no deployed host sets binding.
Changes
src/framework.ts: notellReasoningLoss; nothing is stored or injected on a strip or a drop. Thethinking-binding-lossops alert (noteThinkingLoss) for drops the record can't account for is unchanged, and a host's ownAgent.onThinkingStripobserver still works.src/reasoning-loss.tsandsrc/reasoning-loss-notice.ts(not exported), with their unit tests.test/reasoning-loss-turn.test.ts: a head-change strip, provider drops on every call, a fold mid-turn that takes the turn's own step, and a resident beside the run all put nothing in the agent's context or stream; a host's strip observer is still called. Three of these fail on main's code. TheAgent.onThinkingStriphook tests are kept.src/types/agent.ts: thethinkingBindingdocs say the agent is told nothing.changelog.d/no-reasoning-loss-notices.changed.md.Checks:
tsc --noEmitclean; binding-related test files (thinking-binding, thinking-binding-binds, thinking-drops, thinking-loss-alert, thinking-token-stamp, reasoning-loss-turn) 41/41.Companion: connectome-host#552 removes the host's standing paragraph about folds for the same reason.
🤖 Generated with Claude Code