Conversation
A framework that can pause a generation for user input returns a response whose last message is an unfinished turn: an assistant tool-call turn or a tool result. capture_and_persist_generation recorded that as a generation and an assistant row. When the generation later resumes, its prompt messages are the restored conversation, so the after_prompt callback persist_prompt_to_context stored the restored turn as a new user message. persist_generation_to_context now returns early for a response answering awaiting_input? with true, and persist_prompt_to_context returns early while resuming_generation? is true. The agent counts as resuming when it defines a public resuming? (the framework flag) that returns true, or when the host sets self.resuming_generation = true for a replay it drives itself. Both checks are duck-typed, so framework releases without a pause behave as before. persist_tool_messages_to_context already skipped a tool_call_id present on the context; it now also skips one seen earlier in the same stack, so a resumed stack that repeats a call persists it once even when the context's messages scope cannot see rows written in the same pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ActiveAgent treats every public method an agent class gains as one of its actions, and the action list is part of the release manifest the framework digests into an agent's version. Public resuming_generation? and resuming_generation= would therefore change the digest of every agent that includes HasContext on upgrade, and calling either on the class would build a generation through method_missing. Both now live in the private section. A host still sets the flag with self.resuming_generation = true from an action or a callback, since a private writer accepts a literal self receiver. The CHANGELOG lists the predicate and writer under Added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README and CHANGELOG said a generation that pauses for user input persists nothing. The prompt callback only checks for a resume, so the paused run still stores its prompt as the user turn; only the response (generation record, assistant row, tool rows) is skipped. A host that believed the docs could add the user turn again on resume and store it twice. Keeping the user turn at pause time is deliberate: it survives a request that is declined or never answered. The README also now says the resumed generation must load the paused generation's context. With an anonymous context each agent instance creates its own, so the user turn and the answer would land in two contexts. The prompt callback's comment now gives the reason that holds however the framework rebuilds the resumed prompt: the paused run already stored the user turn. A new test pins the paused run's prompt row, and the pause-then-resume test is renamed for what it asserts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
⏳ Superconductor is working — View implementation I'll get back to you soon! |
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.
Closes #9
Why
The framework is about to gain a way to pause a generation for user input, so a tool call can ask the user a question or wait for approval and then continue. A paused response ends on an unfinished turn: an assistant tool-call turn or a tool result.
HasContextwould record that turn as a generation and an assistant row. When the generation resumes, its prompt replays the stored conversation, so theafter_promptcallback would store the user turn a second time.This change makes
HasContexthandle both sides of a pause. Both checks are duck-typed, so framework releases that cannot pause behave exactly as before, and nothing here depends on an unreleased activeagent.What changes
persist_generation_to_contextreturns early, with an info log, when the response answersawaiting_input?with true. The paused response writes no generation record, assistant row or tool rows.persist_prompt_to_contextreturns early whileresuming_generation?is true. The paused run already stored the user turn, and the resumed prompt replays it.resuming_generation?. It is true when the agent defines a publicresuming?(the framework flag) that returns truthy, or afterself.resuming_generation = true. The private writer is for a host that replays a stored conversation itself and sets the flag from an action or a callback.prompt_optionskeys reach the provider request.resuming?, so a frameworkBase#resuming?is never overridden.persist_tool_messages_to_contextdedupes bytool_call_idwithin one response's stack as well as against rows already on the context. A resumed stack repeats the restored tool results, so each is stored once even when the context'smessagesscope cannot see rows written earlier in the same pass.contextual:param naming the same record orload_<name>(context_id:)in the action. Otherwise each agent instance creates its own anonymous context.resuming_generation?and its writer; "Fixed" for the persistence change and the in-stack dedupe. The version is not bumped.Tests
New file
test/solid_agent/has_context_paused_generation_test.rb(13 tests, using fake response and context objects). It covers:All runs on Ruby 3.4.8:
bundle exec ruby -Itest -Ilib test/solid_agent/has_context_paused_generation_test.rbgives 13 runs, 28 assertions, 0 failures, 0 errors, 0 skips. Against the origin/main library the same file gives 13 runs, 4 failures, 5 errors.bundle exec rake testgives 251 runs, 595 assertions, 0 failures, 0 errors, 0 skips. Baseline on origin/main: 238 runs, 567 assertions, 0 failures, 0 errors.bundle exec rake test:recordsgives 195 runs, 494 assertions, 0 failures, 0 errors, 0 skips (same as baseline).inherit_gem: { rubocop-rails-omakase: rubocop.yml }config.Not run:
require "ostruct"in test_helper fails. That is unrelated to this change, and CI tests 3.2 to 3.4.🤖 Generated with Claude Code