Skip to content

refactor(core): route title and compaction through shared model requests - #43555

Merged
kitlangton merged 5 commits into
v2from
unify-request-envelope
Aug 20, 2026
Merged

refactor(core): route title and compaction through shared model requests#43555
kitlangton merged 5 commits into
v2from
unify-request-envelope

Conversation

@kitlangton

@kitlangton kitlangton commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What

Routes the two remaining hand-rolled session-scoped LLM request envelopes — title generation and compaction summaries — through the shared SessionModelRequest.prepare boundary established in #42680. Callers keep policy (transcript selection, streaming, usage recording, event publishing); prepare owns all provider-visible shaping.

Before / After

session/title.ts (title generation)

Aspect Before After
Model title agent's model (or session model fallback) via SessionRunnerModel.resolve unchanged — caller-resolved model passed to prepare scope
Session headers hand-rolled SessionModelHeaders.make unchanged, via prepare
Prompt cache key none fork-aware session cache key (same prefix identity as the session's main requests)
model.request hooks hand-rolled SessionModelHook.apply unchanged, via prepare
Session context hooks not applied applied, with an empty tool snapshot
Media filtering / image bounds none applied (no-op today: title requests are text-only)
Tools tools: [] empty Tool.Snapshot → still [] on the wire
Transport always HTTP with hook middleware HTTP only (webSocket never passed); hook middleware attached only when HTTP hooks are registered, matching the runner

session/compaction.ts (compaction summary)

Aspect Before After
Model runner/manual-resolved model passed as separate model/ref/cost fields one SessionRunnerModel.Resolved carried through AutoInput/Plan
Session headers hand-rolled unchanged, via prepare
Prompt cache key SessionPromptCacheKey.make(session.id) — a forked session used its own id fork-aware: forked sessions reuse the fork root's cache key, matching the runner
model.request hooks already applied (bolted on by cb39ea1 — same drift pattern generate-node.ts had before #42680) unchanged, now uniform via prepare
Session context hooks not applied applied, with an empty tool snapshot
Media filtering / image bounds none applied (no-op today: the summary prompt is serialized text)
Tools tools: [] empty Tool.Snapshot → still [] on the wire
Transport always HTTP with hook middleware HTTP only (webSocket never passed); hook middleware attached only when HTTP hooks are registered

Audited and left alone

  • core/src/generate.ts (Generate.text) — a generic, non-session generate utility. It has no session scope: no session ID, no agent, no session headers, cache key, or hooks apply. Putting it behind a session-scoped boundary would invent scope it does not have, so it stays on LLMClient.generate directly.
  • The webSocket: "session" opt-in remains exclusive to the durable runner; neither title nor compaction passes it.

Session context hooks stay away from title and compaction requests.
Unification must not change observable plugin behavior: title and compaction requests never ran session.context hooks before, so they still do not. prepare gains a contextHooks: false opt-out — context hooks shape the agent conversation, and title/compaction requests are not part of it, so their transcripts pass through unchanged — and both behaviors are pinned by tests ("keeps session context hooks away from title/compaction requests"). Conversation requests (runner, session.generate) keep running them. scope.tools also became optional: title and compaction requests carry no tools, and the empty snapshot now lives at the boundary that required it instead of duplicated at each call site.

How

  • SessionTitle and SessionCompaction now depend on SessionModelRequest.Service and build their requests with prepare({ scope, transcript }), dropping their private uses of SessionModelHeaders, SessionModelHook, SessionModelHttp, SessionPromptCacheKey, PluginHooks, and App.
  • Each passes an empty Tool.Snapshot; Prepared.executeTool is unused since neither site executes tools.
  • SessionCompaction.AutoInput/Plan collapse model/ref/cost into one SessionRunnerModel.Resolved; the runner's compactionInput passes loaded.model directly.
  • No fields were added to Prepared; policy stays in the callers.

Scope

  • packages/core/src/session/title.ts
  • packages/core/src/session/compaction.ts
  • packages/core/src/session/runner/llm.ts (compaction input shape only)
  • packages/core/test/session-title.test.ts, test/session-compaction.test.ts, test/config/compaction.test.ts (fixture shape + new coverage)
  • .changeset/unify-request-envelope.md

Testing

  • New: title requests assert the session prompt cache key, empty tool list, and system prompt; a registered session.context hook now reaches title requests; forked-session compaction asserts the fork root prompt cache key.
  • Existing manual-compaction test still pins headers, cache key, and durable event order.
  • bun typecheck and bun run test from packages/core: 1891 pass, 0 fail.

@kitlangton
kitlangton enabled auto-merge (squash) August 20, 2026 03:19
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
#
# Conflicts:
#	packages/core/src/session/compaction.ts
#	packages/core/src/session/runner/llm.ts
#	packages/core/test/config/compaction.test.ts
#	packages/core/test/session-compaction.test.ts
@kitlangton
kitlangton disabled auto-merge August 20, 2026 03:40
@kitlangton
kitlangton enabled auto-merge (squash) August 20, 2026 16:34
@kitlangton
kitlangton merged commit ebc2504 into v2 Aug 20, 2026
10 checks passed
@kitlangton
kitlangton deleted the unify-request-envelope branch August 20, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant