Skip to content

feat(ai-sdk-middleware): ModelMessage-altitude durable compaction API#40

Merged
MyPrototypeWhat merged 16 commits into
mainfrom
m13t/determined-black-24ff53
Jun 17, 2026
Merged

feat(ai-sdk-middleware): ModelMessage-altitude durable compaction API#40
MyPrototypeWhat merged 16 commits into
mainfrom
m13t/determined-black-24ff53

Conversation

@MyPrototypeWhat

Copy link
Copy Markdown
Owner

Summary

Adds a ModelMessage[]-altitude durable-compaction API to @context-chef/ai-sdk-middleware — the message type generateText/prepareStep actually use — and deprecates the mis-altitude LanguageModelV3Prompt variants. Additive, non-breaking.

Motivation

"Durable compaction" means summarize, then write the result back to your persisted message store. Hosts persist UIMessage[] / ModelMessage[]; nobody persists LanguageModelV3Prompt — that's the ephemeral provider-protocol layer a middleware's transformParams briefly sees. The existing compactHistory / planCompaction were typed at that wrong altitude, so a host driving compaction from a ToolLoopAgent prepareStep (which hands messages: ModelMessage[] and model: LanguageModel) had no safe path — and a bare cast crashes, because ModelMessage content may be a plain string that the V3 adapter's .content.filter(...) chokes on. (middleware.ts already documented the V3-vs-ModelMessage type split.)

Design + plan: docs/superpowers/specs/2026-06-17-model-message-durable-compaction-altitude-design.md, docs/superpowers/plans/2026-06-17-model-message-durable-compaction-altitude.md.

Changes

  • New host-facing API (thin shells over core's provider-agnostic engine + createCompressionAdapter; no new compaction/flattening logic):
    • compactModelMessages(messages, model, opts) → ModelMessage[] — one-shot durable compaction; returns the input reference on a no-op so callers can skip persistence.
    • planCompactionModelMessages(messages, opts) → { system, toSummarize, toKeep } (all ModelMessage[]).
    • summarizeModelMessages(messages, model, opts?) → string.
  • New ModelMessage ↔ IR adapter (modelMessageAdapter.ts), parallel to the V3 adapter.ts — handles the three ModelMessage-only shapes (string-shorthand content, ImagePart, approval parts); lossless round-trip incl. reasoning byte-exact.
  • createCompressionAdapter widened LanguageModelV3ai's LanguageModel (string id | V3 | V2) — the type prepareStep/generateText give you. Backward-compatible (existing callers pass a V3 subtype).
  • Deprecated compactHistory / planCompaction (still exported & working) → JSDoc points to the ModelMessage versions; repointed the persistence warning, READMEs (EN + zh), and CompressOptions JSDoc. summarizeMessages is not deprecated.
  • Review-driven correctness fixes, applied to BOTH adapters (the V3 ones were pre-existing, now fixed and kept in sync):
    • provider-executed / inline assistant tool-result no longer triggers a spurious [No tool result available] placeholder on round-trip;
    • tool-message-level providerOptions (e.g. Anthropic cache breakpoint) preserved on round-trip;
    • tool-call with input: undefined serializes to '{}' (a string), not undefined.

Testing

  • Added/updated tests — adapter round-trips (string shorthand, image/file, reasoning byte-exact, tool-call/result, approval parts, providerOptions); the three fixes above; durable no-op reference contract; turn-safe split.
  • All existing tests pass — 126 passing (pnpm --filter @context-chef/ai-sdk-middleware test).
  • Type checking passes (tsc --noEmit, exit 0).
  • Linting passes (biome check on src, clean).

@context-chef/core is unchanged (the IR engine was already at the correct altitude).

Breaking changes

None. Additive; the V3 compactHistory / planCompaction remain exported and functional (now @deprecated, scheduled for removal in the next major).

Note: No changeset is included yet — it'll be added at release time per our batching workflow. Planned bump: @context-chef/ai-sdk-middleware minor.

Reviewer notes — intentionally deferred (low / cleanup / V3-inherited)

Adjacent-tool-message coalescing, cross-message leading-approval drop (edge shape), no-op-vs-firing sanitization asymmetry, media-only tool output → empty summary text, multi-reasoning collapse, adapter duplication (acknowledged in-file), asMM/CompactionPlan clones, eager 3-slice conversion in planCompactionModelMessages, user-branch multi-pass, and the LanguageModel string-id widening dropping a compile-time guard. None are correctness blockers; happy to address in follow-ups if desired.

…erOptions, undefined tool input (both adapters)
…ns (not ToolLoopAgent steps) + summary/consecutive-user behavior
@MyPrototypeWhat MyPrototypeWhat merged commit deafa2f into main Jun 17, 2026
2 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 17, 2026
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