Repository navigation
[EPIC-D][LOU-D44] AgentExecutor.fork() creates a runnable session from a historical checkpoint - #104
Merged
Merged
Conversation
…m a historical checkpoint
AgentExecutor.fork({ sessionId, fromStep, newSessionId?, checkpointStore, patch? })
takes the newest history entry of fromStep, applies the patch (messages
rewrite, businessState, a tool result replaced in place, appendInput queued
as a user message) and saves it as the in-progress checkpoint of a new
session (default <sessionId>.fork-<n>), leaving the original untouched.
execute({ sessionId, checkpointStore, input: [] }) or agent.resume()
continues the fork. A missing step throws LOUSHY_CHECKPOINT_NOT_FOUND (new
registry code, documented in docs/errors.md).
createAgent() agents gain agent.fork(sessionId, options) over
store.checkpoints. compareTrajectories(a, b) in src/evals/drift.ts compares
two checkpoints or transcripts step by step (steps, first diverging step and
the eval --drift diff) for Forge's side-by-side view.
Docs: durable-execution.md "Fork and replay", llms-full.txt regenerated,
CHANGELOG (Added).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
# Conflicts: # CHANGELOG.md
…fter multimodal parts (LOU-V11) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
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.
What
AgentExecutor.fork({ sessionId, fromStep, newSessionId?, checkpointStore, patch? })(delegates to the newsrc/execution/fork.ts). It picks the newest history entry offromStep(D43history()) and appliespatchin this order:messages(messages),businessState,toolResult: { toolCallId, result },appendInput. The tool result is replaced in place, keeping its position, call id and tool name; if the call has no result yet, the result is recorded and the load-timesplitPendingTurnputs it behind its turn.appendInputqueues a user message. The result is saved as the'in-progress'checkpoint ofnewSessionId(default<sessionId>.fork-<n>, the first freen), withapprovalIddropped. The call returns{ sessionId, step, checkpoint }, and the original session's checkpoint and history are not touched.execute({ sessionId: fork.sessionId, checkpointStore, input: [] })continues the fork.Errors:
SDKErrorwith the new registry codeLOUSHY_CHECKPOINT_NOT_FOUND. The message lists the steps that are kept.history(), anewSessionIdthat already has a checkpoint, or atoolCallIdthat is not in the transcript throwsConfigurationError.agent.fork(sessionId, { fromStep, newSessionId?, patch? })oncreateAgent()agents runs overstore.checkpoints. Without a store it rejects withLOUSHY_CONFIG_MISSING_CHECKPOINT_STORE.agent.resume(fork.sessionId)continues the fork.compareTrajectories(a, b)insrc/evals/drift.tsreuses the D46diffTrajectories. It takes twoCheckpoints or twoMessage[]and returns:divergedAt: the first step that differs; tool call ids are ignoreddrift: the tool order, argument, step count and finish reason diffIt is exported with
TrajectoryComparison,TrajectoryStepandDriftEntryfrom./evalsand the root.New types
ForkOptions,ForkPatchandForkResultincheckpoint.ts.Docs: a "Fork and replay" section in docs/durable-execution.md, a
LOUSHY_CHECKPOINT_NOT_FOUNDsection in docs/errors.md, regenerated llms-full.txt, and a CHANGELOG entry under Added.Why
This is differentiator 3 of
.agent-loop/AUDIT.md§7 (Agent Forge time-travel), built on the D43 checkpoint history (#98). It lets you fork a run at step N with a "what if" change and replay it.compareTrajectoriesgives Forge (D45) the data for a side-by-side view.Acceptance criteria
AgentExecutor.fork()loads the history entry atfromStep. A missing step throwsLOUSHY_CHECKPOINT_NOT_FOUND, which is in the LOU-D2 registry and documented in docs/errors.md.appendInputqueues a user message.newSessionId(default<sessionId>.fork-<n>) with status in-progress. The original is untouched. The call returns{ sessionId, step, checkpoint }.execute({ sessionId: newSessionId, checkpointStore, input: [] })andagent.resume(newSessionId)continue the fork.agent.fork(sessionId, options)oncreateAgentagents with a checkpoint store. Only theSimpleAgent.forkmember, the method and one type import were added to src/createAgent.ts.compareTrajectories(a: Checkpoint | Message[], b)returns the step and tool-call diff, reusing src/evals/drift.ts.mockModel+memoryStore():appendInput, plus themessagesandbusinessStatepatchesagent.forkthroughcreateAgent, with and without a storeHow verified
npx tsc --noEmit: exit 0npm run lint: 0 errors, 404 warnings. The changed and new files add no warnings; the oneanywarning in AgentExecutor.ts at line 127 was already there.npm run test:types: Type Errors: no errorsnpx vitest run(after the build): Test Files 1 failed | 153 passed (154); Tests 1 failed | 2231 passed | 3 skipped (2235).src/deploy/adapters/cloudflare.test.ts > real wrangler tooling > wrangler dev serves the worker on the local workerd runtime:/healthnever answered within 90s.src/execution/fork.test.ts6/6,src/evals/drift.test.ts9/9.npm run build && npm run build --workspace=packages/create-loushy-agent: both exit 0npm run docs:verify-snippets -- --skip-build: all 117 snippets type-check against src/, and 6 also run cleanlynpm run docs:llms:check: exit 0npm run test:coverage: same single wrangler failure as above, so no coverage report was written. I re-ran it with--coverage.reportOnFailure(same tests, nothing skipped) to get the coverage file for fallow. Coverage:fork.ts98.59% lines,drift.ts100% lines.npm run fallow: exit 0, "No issues found", dead exports 0.0%. The only clone group was already there (AnthropicProvider/OpenAIProvider).Not in this PR: reusing
recordReplaycassettes for the unchanged prefix. That was a nice-to-have; a fork does not re-run the prefix anyway, because it resumes from the checkpoint.🤖 Generated with Claude Code
https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
Generated by Claude Code