Skip to content

feat(eve): authored compaction strategies and ctx.compact() - #5410

Draft
ruiconti wants to merge 19 commits into
mainfrom
pe/ctx-compact
Draft

ruiconti wants to merge 19 commits into
mainfrom
pe/ctx-compact

Conversation

@ruiconti

Copy link
Copy Markdown
Contributor

Replaces #5332.

An agent exports a compaction strategy from agent/compaction.ts. eve keeps owning when compaction can run, and the events, memory capture and recall, failure and billing around it. The strategy decides what the compacted history looks like.

When it runs

  • Overflow guard: a request that would fill 90% of the context window compacts first. This is fixed; compaction.thresholdPercent is removed.
  • A hook calls ctx.compact(): the next model call compacts first, including a request made between turns. Repeated requests before it runs compact once.
  • If a strategy's result is still over the guard, the default fixedTail() runs once on it and must fit.

ctx.view.context.inputTokens is the input tokens of the latest settled model call.

fixedTail() (the default, with #5290's behavior) is unchanged. context.started gains an optional strategy field.

Tests

  • strategy.integration.test.ts: one compaction for a request that alone exceeds the limit; the guard runs the default once after an over-limit strategy; a hook request compacts once, forced.
  • runner.test.ts: a compact request between turns is kept and taken once; a cancel does not take it.
  • Existing compaction, reactions and execution suites pass.

Not in this PR yet: extension-shipped compaction.ts, a named ctx.compact(name), the summary model moving into fixedTail({ model }), and a compile error for the removed thresholdPercent.

An agent exports a compaction strategy from agent/compaction.ts. The core
owns when compaction can run (a fixed 90% overflow guard, or a hook's
ctx.compact()) and the events, memory, failure and billing around it; the
strategy owns what the compacted history looks like. If a strategy's result
is still over the guard, the default fixedTail() runs once on it.

Adds ctx.view.context.inputTokens for hooks. Removes
compaction.thresholdPercent. Supersedes #5332.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
eve-docs Ready Ready Preview, v0 Oct 27, 2026 3:49am UTC
eve-pkg Ready Ready Preview, v0 Oct 27, 2026 3:49am UTC

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Bundle + Package Summary: apps/fixtures/weather-agent

Key takeaways

  • No notable deltas vs main (a5328b4).

Delta vs main (a5328b4)

Area Metric Baseline Current Delta
Package Packed tarball 7.90 MB 7.91 MB +7.4 kB ⚠️
Package Unpacked publish size 27.57 MB 27.59 MB +19.6 kB ⚠️
Package Installed footprint 76.02 MB 76.04 MB +19.6 kB ⚠️
Package Published files 4366 4389 +23
Package Installed files 8363 8386 +23
Package Installed package instances 34 34 0
Package Distinct installed package names 32 32 0
Package Installed dependency edges 51 51 0
Package Installed optional peer edges 17 17 0
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 21.86 MB 21.87 MB +7.2 kB ⚠️
Runtime Public routes 18 18 0
Changed function payloads vs main (a5328b4) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 10.93 MB 10.94 MB +3.6 kB ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 10.93 MB 10.94 MB +3.6 kB ⚠️ none

eve init install

Metric Baseline Current Delta
Installed footprint 114.40 MB 114.42 MB +19.6 kB ⚠️
Installed packages 97 97 0
dependencies 4 4 0
devDependencies 2 2 0
Dependency package bytes 46.10 MB 46.12 MB +19.6 kB ⚠️
devDependency package bytes 5.11 MB 5.11 MB 0 B ➖
Build Metadata
  • Preset: vercel
  • Nitro: nitro@3.0.260903-beta
  • Output directory: apps/fixtures/weather-agent/.vercel/output
  • Build metadata timestamp: 2026-10-27T03:49:29.776Z
  • Route aliases: 18 public, 1 internal (19 total aliases)
  • Vercel routes in config: 21
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.84.0
  • Package directory: packages/eve
  • Tarball: 7.91 MB (eve-0.84.0.tgz)
  • Unpacked payload: 27.59 MB across 4389 published files
  • Installed footprint: 76.04 MB across 8386 installed files
  • Installed root package: 27.59 MB
  • Installed dependencies: 48.44 MB
  • Installed package instances: 34
  • Distinct installed package names: 32
  • Installed dependency edges: 51
  • Installed optional peer edges: 17
  • Runtime dependencies: 2
  • Peer dependencies: 15 (14 optional)

Installed footprint is measured from an isolated temporary npm install of the packed tarball.
Graph metrics read only package.json files in package directories directly beneath a node_modules boundary, including nested boundaries. Each directory is one package instance; distinct names come from those manifests. Dependency edges count each unique name in dependencies or optionalDependencies per instance; optional peer edges count peerDependencies marked optional.

Heavy installed dependencies

  • eve: 27.59 MB (36.3%)
  • @rolldown/binding-linux-x64-gnu: 19.89 MB (26.2%)
  • ai: 8.50 MB (11.2%)
  • zod: 6.15 MB (8.1%)
  • undici: 3.58 MB (4.7%)
Publish payload breakdown
Published file size
🟠 dist/src/compiled/shadcn-registry/index.js       [#####...................] 3.85 MB 13.9%
🟠 dist/src/compiled/@photon-ai/chat-adapter-ime... [###.....................] 2.29 MB 8.3%
🟠 dist/src/compiled/run/index.js                   [#.......................] 1.00 MB 3.6%
🟡 dist/src/compiled/@vercel/blob/index.js          [#.......................] 604.9 kB 2.2%
🟡 dist/src/compiled/_chunks/workflow/undici-D-I... [#.......................] 521.9 kB 1.9%
🔴 Other published files                            [########################] 19.33 MB 70.0%
Installed footprint breakdown
Installed package size
🔴 eve                             [########################] 27.59 MB 36.3%
🔴 @rolldown/binding-linux-x64-gnu [#################.......] 19.89 MB 26.2%
🔴 ai                              [#######.................] 8.50 MB 11.2%
🔴 zod                             [#####...................] 6.15 MB 8.1%
🟠 undici                          [###.....................] 3.58 MB 4.7%
🟠 nitro                           [##......................] 1.89 MB 2.5%
🔴 Other installed packages        [#######.................] 8.44 MB 11.1%
Runtime dependencies (2)
Package Range Notes
nitro 3.0.260903-beta
undici 8.10.2
Peer dependencies (15)
Package Range Notes
@linqapp/sdk ^0.47.1 optional peer
@nuxt/kit ^4.0.0 optional peer
@nuxt/schema ^4.0.0 optional peer
@opentelemetry/api ^1.0.0 optional peer
@vercel/connect ^2.5.2 optional peer
ai catalog:
braintrust ^3.0.0 optional peer
chat ^4.42.0 optional peer
dd-trace ^6.13.0 optional peer
just-bash ^3.6.0 optional peer
next ^16.0.0 optional peer
react ^19.0.0 optional peer
svelte ^5.0.0 optional peer
vite ^8.3.1 optional peer
vue ^3.5.0 optional peer
eve init install drill-down

eve init install details

  • Command: eve init my-agent
  • Package manager: npm
  • Installed footprint: 114.42 MB across 10303 installed files
  • Installed packages: 97 total (91 transitive-only)
  • dependencies: 4 direct packages totaling 46.12 MB
  • devDependencies: 2 direct packages totaling 5.11 MB
  • Other transitive package files: 63.19 MB

Installed footprint is measured from an isolated temporary eve init my-agent using the current packed eve tarball.

Heavy installed dependencies

  • @typescript/typescript-linux-x64: 27.95 MB (24.4%)
  • eve: 27.59 MB (24.1%)
  • @rolldown/binding-linux-x64-gnu: 19.89 MB (17.4%)
  • zod: 9.76 MB (8.5%)
  • ai: 8.50 MB (7.4%)
Installed footprint breakdown
Installed package size
🔴 @typescript/typescript-linux-x64 [########################] 27.95 MB 24.4%
🔴 eve                              [########################] 27.59 MB 24.1%
🔴 @rolldown/binding-linux-x64-gnu  [#################.......] 19.89 MB 17.4%
🔴 zod                              [########................] 9.76 MB 8.5%
🔴 ai                               [#######.................] 8.50 MB 7.4%
🟠 undici                           [###.....................] 3.58 MB 3.1%
🔴 Other installed packages         [###############.........] 17.16 MB 15.0%
dependencies (4)
Package Range Installed size Share
@vercel/connect 2.5.2 276.3 kB 0.2%
ai ^7.0.137 8.50 MB 7.4%
eve file:eve-0.84.0.tgz 27.59 MB 24.1%
zod 4.5.4 9.76 MB 8.5%
devDependencies (2)
Package Range Installed size Share
@types/node 24.x 2.61 MB 2.3%
typescript 7.0.2 2.50 MB 2.2%
Function Drill-Down

Payload Size Graph

Unique function payload size and share of total
🔴 functions/.well-known/workflow/v1/flow.func     [########################] 10.94 MB 50.0%
🔴 functions/__server.func                         [########################] 10.94 MB 50.0%

Top Function Payloads

🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 10.94 MB
Metric Value
Public routes /.well-known/workflow/v1/flow
Runtime nodejs24.x
Handler index.mjs
Payload 10.94 MB
Function files 10.94 MB across 124 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/vercel.web.mjs is 2.60 MB (23.8%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/vercel.web.mjs                         [#############...........] 2.60 MB 23.8%
🟡 _libs/undici.mjs                               [#####...................] 994.5 kB 9.1%
🟡 _chunks/sandbox2.mjs                           [#####...................] 937.1 kB 8.6%
🟡 _chunks/compiled-artifacts-instrumentation.mjs [####....................] 730.7 kB 6.7%
🟡 _chunks/token-util-Bla5Z0G9.mjs                [###.....................] 704.5 kB 6.4%
🔴 Other bundled files                            [########################] 4.97 MB 45.4%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x",
  "maxDuration": "max",
  "experimentalTriggers": [
    {
      "type": "queue/v2beta",
      "topic": "__eve776561746865722d6167656e74_wkf_workflow_*",
      "consumer": "default",
      "retryAfterSeconds": 5,
      "initialDelaySeconds": 0
    }
  ],
  "environment": {
    "WORKFLOW_PRECONDITION_GUARD": "1"
  }
}

🟠 functions/__server.func • 17 public routes, 1 internal alias • 10.94 MB
Metric Value
Public routes /
/.well-known/workflow/v1/webhook/[token]
/eve/v1/callback/[token]
/eve/v1/connections/[name]/callback/[attemptId]/[token]
/eve/v1/connections/[name]/callback/[token]
/eve/v1/health
/eve/v1/info
/eve/v1/session
/eve/v1/session/[parentSessionId]/subagents/[callId]/[childSessionId]/stream
/eve/v1/session/[sessionId]
/eve/v1/session/[sessionId]/cancel
/eve/v1/session/[sessionId]/clear
/eve/v1/session/[sessionId]/compact
/eve/v1/session/[sessionId]/descendants/stream
/eve/v1/session/[sessionId]/reset
/eve/v1/session/[sessionId]/stream
/eve/v1/session/[sessionId]/stubs
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 10.94 MB
Function files 10.94 MB across 124 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/vercel.web.mjs is 2.60 MB (23.8%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/vercel.web.mjs                         [#############...........] 2.60 MB 23.8%
🟡 _libs/undici.mjs                               [#####...................] 994.5 kB 9.1%
🟡 _chunks/sandbox2.mjs                           [#####...................] 937.1 kB 8.6%
🟡 _chunks/compiled-artifacts-instrumentation.mjs [####....................] 730.7 kB 6.7%
🟡 _chunks/token-util-Bla5Z0G9.mjs                [###.....................] 704.5 kB 6.4%
🔴 Other bundled files                            [########################] 4.97 MB 45.4%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x"
}

Build Timing: e2e/fixtures/agent-tools-sandbox

This is an informational timing measurement inside eve build, from preflight through publication. Output-size measurement and profile writing are excluded.

Build mode: deployable Vercel build with sandbox template prewarm included.

  • Build pipeline: 3.95 s -> 3.60 s (-354.6 ms) vs main (a5328b4).
  • Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `main (a5328b4)`
Phase Baseline Current Delta
extension.check 15.1 ms 16.8 ms +1.7 ms
project.resolve 8.5 ms 0.4 ms -8.1 ms
workspace.create 0.8 ms 0.5 ms -0.3 ms
host.prepare 789.2 ms 487.3 ms -301.9 ms
vercel.service-prefix.resolve 1.7 ms 1.6 ms -0.1 ms
nitro.create 415.9 ms 448.0 ms +32.1 ms
sandbox.prewarm 668.7 ms 576.7 ms -92.0 ms
nitro.cache.prepare 0.2 ms 0.2 ms 0.0 ms
nitro.prepare 0.7 ms 0.7 ms 0.0 ms
nitro.public-assets 0.8 ms 0.8 ms 0.0 ms
nitro.prerender 0.5 ms 0.5 ms 0.0 ms
nitro.bundle 1.92 s 1.93 s +17.8 ms
nitro.cache.write 0.3 ms 0.3 ms 0.0 ms
vercel.workflow-function.materialize 49.8 ms 49.8 ms 0.0 ms
agent-summary.emit 0.6 ms 0.6 ms 0.0 ms
connect-manifest.emit 0.3 ms 0.3 ms 0.0 ms
nitro.close 0.1 ms 0.1 ms 0.0 ms
output.publish 3.2 ms 3.3 ms +0.1 ms
workspace.remove 2.4 ms 2.3 ms -0.1 ms

…ults

A requested compaction that fails settles as failed and the call goes on
unless the request is over the limit. A strategy result that drops a tool
result, keeps a result without its call, or does not start with a user
message fails. A result still over the limit fails; no fallback strategy.
Remove the unused strategy name. Fold the last model call's input tokens
into the view. Fix the compaction e2e fixtures for the v27 events.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
…test inputTokens across turns

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
…ategy

Signed-off-by: Rui Conti <ruiconti@gmail.com>
…ail({ model })

compaction.model and compaction.modelContextWindowTokens now throw an error that names fixedTail({ model }) in compaction.ts. Drops the compiled, resolved, session and runtime plumbing for a separate compaction model.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
…tegy docs

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
/**
* How a session's history is shortened. The core owns when compaction can
* run, the events around it, memory records, failure and billing; a strategy
* owns whether to compact and what the compacted history looks like.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This says the strategy owns "whether to compact", but the core decides that before calling it (needsSummary in step.ts), and a strategy can't skip: returning history unchanged still settles a compaction. The docs say both things. compaction-strategies.md:20 says "a strategy decides only how", but compaction-strategies.md:59 says wrapping fixedTail() lets a strategy "compact earlier than the threshold". A wrapper can't do that, because it only runs after the core has already decided to compact.

Since the code has the core own timing, suggest fixing this comment and dropping the "compact earlier" example from the docs (a hook calling ctx.compact() is the way to compact early). If strategies should own timing, that needs an explicit trigger/skip contract the core consults.

readonly threshold: number;
/** How many of the latest messages the session suggests keeping word for word. */
readonly recentWindowSize: number;
/** A user or a hook asked for this compaction; the context was not over its limit. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

forced is documented as "asked for, and the context was not over its limit" (same in compaction-strategies.md:49), but step.ts:409 sets it straight from input.force. A ctx.compact() request that lands on an over-limit call, or a manual compact on a full context, passes forced: true with an over-limit inputTokens, and context.started drops its trigger.

Either document forced as "explicitly requested, whatever the size", or compute it as force && !exceedsLimit(measured) and keep trigger when the request was also over. The second option also keeps fixedTail's model-free tool-result trimming on that path, since forced feeds forceSummary.

step.session = resolvedModel.session;
const summaryModel =
step.session.agent.compactionModelReference ?? requireSessionModelReference(step.session);
const summaryModel = requireSessionModelReference(step.session);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The summary model now comes from the strategy (fixedTail({ model })), but the manual compact path still publishes runStarted up front with the session's model id (lines 115-122), and passes change.announced: true, so compactOnce never re-announces with the model it actually resolves. That causes two problems:

  • With fixedTail({ model: "anthropic/claude-haiku-4.5" }), the manual compaction's summary run reports the session model while the call runs on haiku.
  • With a strategy that never calls summarize (like the e2e fixture), the stream reports a model run that never happened, settled abandoned. That contradicts the docs' "A strategy that never summarizes makes no model call".

The threshold path already handles this lazily via resolveSummaryCall. Suggest tracking "context change announced" separately from "summary run opened", so the manual path also opens the run, with the right modelId, on the first summarize.

CompactionMessage,
CompactionContext,
CompactionStrategy,
} from "#harness/compaction/strategy.js";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The public eve/compaction types come straight from #harness/compaction/strategy.js. CompactionMessage aliases the internal HarnessModelMessage, and CompactionContext uses ModelMessage/LanguageModel from ai. Other authoring surfaces avoid this. public/memory/index.ts:4 uses HistoryMessage from #shared/history-message.js, which exists so that "authoring types do not depend on the app's AI SDK version".

Suggest defining the author-facing strategy/context/message types on the public side with eve's HistoryMessage, and adapting to HarnessModelMessage at the harness boundary (validateCompactionResult already sits there). Otherwise an app on a different ai version gets type mismatches in compaction.ts, and harness internals become public API.

Comment thread docs/guides/hooks.md

## Compact the history from a hook

Call `ctx.compact()` to compact the history before the next model call: the running turn's next call, or the first call of the next turn when no turn is running. Asking twice before it runs compacts once. How the history is shortened is the agent's [compaction strategy](../concepts/compaction-strategies). `ctx.view.context.inputTokens` is the input tokens of the latest settled model call, for hooks that decide by size.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Further down, the cancel section (line 265) still says "Hook-side ctx.compact and ctx.once are not available." That now contradicts this section. Suggest dropping ctx.compact from that sentence.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
…measure results with the provider undercount, lazy summary run, effective-model limit, public message type

Signed-off-by: Rui Conti <ruiconti@gmail.com>
…nt the result keeps

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
…ing points at the top-level one

Signed-off-by: Rui Conti <ruiconti@gmail.com>
… keeps every message

Signed-off-by: Rui Conti <ruiconti@gmail.com>
…rink an over-limit history

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>

This branch was successfully deployed

2 active deployments
Preview – eve-docs — 1e2e6698 Deployed Oct 27, 2026 by vercel[bot]
Preview – eve-pkg — 1e2e6698 Deployed Oct 27, 2026 by vercel[bot]
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