Skip to content

refactor(agent-core-v2): carry the context fold cursor in state and converge fold/projection internals - #2875

Draft
7Sageer wants to merge 5 commits into
mainfrom
refactor-context
Draft

refactor(agent-core-v2): carry the context fold cursor in state and converge fold/projection internals#2875
7Sageer wants to merge 5 commits into
mainfrom
refactor-context

Conversation

@7Sageer

@7Sageer 7Sageer commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — this is a preventive internal refactor of the agent-core-v2 context subsystem; the problem is explained below.

Problem

Three structural gaps in the context subsystem were held together by convention rather than by structure:

  1. The loop-event fold cursor (openStepUuid / pending / deferred) lived in a module-level WeakMap keyed by state-array identity. Every wholesale state replacement (undo / clear / compaction / swarm-mode exit) had to remember to call resetFold — five call sites in a different module from the cursor's definition, with no compile-time signal if forgotten.
  2. The same fold semantics existed twice — the live/replay fold (loopEventFold) and the display transcript fold (contextTranscript) — kept in sync only by a doc comment ("semantics mirror the v1 fold exactly"), so any semantic change could silently drift the display from the live view.
  3. The context projector exposed four overlapping methods (project / projectStrict / projectMediaDegraded / projectMediaStripped), and the LLM requester selected among them with nested ternaries keyed by retry state.

What changed

  • Fold cursor moves into the model state. ContextModel state is now { messages, fold }. Wholesale replacements reset the cursor by returning EMPTY_FOLD in the same return that replaces the messages — there is no out-of-band reset left to forget. ContextState is deeply frozen at the op boundary to preserve the consumer immutability the wire's shallow freeze gave the bare array state. Blob rehydrate now also covers messages still deferred in the cursor (previously their blob references were never resolved after a restore).
  • One fold kernel. The fold logic is generic over the entry type (FoldFrame<E> + FoldEntryAdapter<E>); the wire model folds bare messages and the display transcript folds time-stamped entries through the same kernel, keeping only its display bookkeeping (times, foldedLength, clearFloor). Two deliberate defensive tightenings fall out of the unification: events tagged with a non-open step uuid are dropped instead of mis-appended to a retried step, and step.end settles only the step it names. Well-formed streams (including v1 replay records) are unaffected.
  • Projector converges to project(messages, policy). Projection variability is now data (ProjectionPolicy { wire, media }); the requester builds the policy from retry state instead of selecting among four methods. Repair telemetry is unchanged.

Verification: agent-core-v2 full suite green (312 files / 4906 tests, including new transcript↔model prefix-parity tests and fold-reset assertions), typecheck, build, lint (0 errors), import-boundary check, plus downstream kap-server / klient typechecks.

No journal record format changes (v1/v2 replay compatibility preserved); the IAgentContextMemoryService contract is untouched (its ~20 consumers needed no changes).

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. (No changeset: agent-core-v2 internal refactor, not user-perceivable.)
  • Ran gen-docs skill, or this PR needs no doc update. (No user-facing behavior change.)

…onverge fold/projection internals

- ContextModel state is now { messages, fold }: the loop-event fold cursor
  (openStepUuid / pending / deferred) lives in the state instead of a
  module-level WeakMap keyed by array identity, so wholesale replacements
  (undo / clear / compaction / swarm exit) reset it structurally via
  EMPTY_FOLD instead of a manual resetFold at five call sites.
- The display transcript and the wire model now share one generic fold
  kernel (FoldFrame / FoldEntryAdapter), eliminating the mirrored second
  implementation. Events tagged with a non-open step uuid are dropped and
  step.end settles only the step it names — defensive in abnormal streams,
  identical on well-formed ones (v1 replay unaffected).
- IAgentContextProjectorService converges to project(messages, policy) with
  a ProjectionPolicy data object; llmRequester builds the policy from retry
  state instead of selecting among four methods.
- Blob rehydrate now also covers messages still deferred in the fold cursor.
- ContextState is deeply frozen at the op boundary to preserve the consumer
  immutability the wire's shallow freeze gave the bare array state.
@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e7fe16e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@7Hanrui

7Hanrui commented Aug 13, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8780d6a0d8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +351 to +356
// Asserts after EVERY prefix of the stream that the transcript's current
// conversation (the tail of `foldedLength` entries) matches the model fold.
// Compaction pauses the check until the next clear: the display's
// foldedLength is a UI collapse metric (kept users + summary marker, and
// legacy records put the summary first in the model but last in the
// display), so the two views diverge there BY DESIGN; clear realigns them.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move inline test explanations into the header

This newly added inline explanation sits inside a helper body, and there are similar scenario notes in the added test case below; package guidance for agent-core-v2 requires comments to live only in the top-of-file /** */ block, so please move the parity rationale into the file header or encode it in helper/test names instead.

AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L36-L38

Useful? React with 👍 / 👎.

@7Hanrui

7Hanrui commented Aug 13, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d419454df

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

openStepUuid: string | undefined;
pending: Set<string>;
deferred: ContextMessage[];
/** A fold position: the entries reduced so far plus the fold cursor. */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move declaration comments into the file header

This new declaration-level JSDoc sits outside the top-of-file module header; in agent-core-v2, comments are restricted to the file header rather than beside declarations. Please move the fold-frame explanation into the module header or encode it in the exported names, and apply the same cleanup to the other added declaration comments in this change.

AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L36-L38

Useful? React with 👍 / 👎.

@7Hanrui

7Hanrui commented Aug 13, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: e83ad3f20d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@7Hanrui

7Hanrui commented Aug 13, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 2b47fe707e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…s in context memory

- isVacuousContentPart and dehydrateRecord now switch exhaustively over
  ContentPart / LoopRecordedEvent variants, so a new variant fails
  compilation until it takes an explicit position
- the transcript/model parity comparator spreads whole messages and masks
  only summary content, so new ContextMessage fields join the comparison
  automatically
- correct two stale header comments: local message ids persist with
  append_message records, and undo's prompt-owned-injection pairing
  depends on them after a resume
@7Hanrui

7Hanrui commented Aug 13, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: e7fe16e64f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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.

2 participants