Skip to content

fix(reasoning): stop telling agents that reasoning left their context - #938

Merged
antra-tess merged 1 commit into
mainfrom
fix/no-reasoning-loss-notices
Oct 30, 2026
Merged

antra-tess merged 1 commit into
mainfrom
fix/no-reasoning-loss-notices

Conversation

@antra-tess

Copy link
Copy Markdown
Collaborator

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: no tellReasoningLoss; nothing is stored or injected on a strip or a drop. The thinking-binding-loss ops alert (noteThinkingLoss) for drops the record can't account for is unchanged, and a host's own Agent.onThinkingStrip observer still works.
  • Removed src/reasoning-loss.ts and src/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. The Agent.onThinkingStrip hook tests are kept.
  • src/types/agent.ts: the thinkingBinding docs say the agent is told nothing.
  • Changelog: changelog.d/no-reasoning-loss-notices.changed.md.

Checks: tsc --noEmit clean; 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

#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>

@github-actions github-actions 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.

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 on Agent.onThinkingStrip still 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. branch is still passed in StreamThinkingDrops though nothing reads it. The only reader of thinkingDrops.branch was the removed tellReasoningLoss/reasoningLoss.forDrops call in driveStream. startStream still builds thinkingBranch and passes it as branch, and StreamThinkingDrops.branch is 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, but placedDrops is now read only inside if (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 the recordedAfter check.
  • 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 only assert.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. The afterRun option of run() (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.

@antra-tess
antra-tess merged commit a64cfc0 into main Oct 30, 2026
15 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant