refactor(opencode): migrate compaction and workspace tests to layer nodes (#34478)

This commit is contained in:
James Long
2026-06-29 15:42:52 -04:00
committed by GitHub
parent d8f9388610
commit b9dae8593c
3 changed files with 90 additions and 144 deletions

View File

@@ -1,63 +1,35 @@
import { afterEach, describe, expect } from "bun:test" import { afterEach, describe, expect } from "bun:test"
import { Effect, Layer } from "effect" import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http" import { Npm } from "@opencode-ai/core/npm"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Database } from "@opencode-ai/core/database/database"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Ripgrep } from "@opencode-ai/core/ripgrep" import { Ripgrep } from "@opencode-ai/core/ripgrep"
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
import path from "path" import path from "path"
import { pathToFileURL } from "url" import { pathToFileURL } from "url"
import { Auth } from "../../src/auth" import { Auth } from "../../src/auth"
import { EventV2Bridge } from "../../src/event-v2-bridge" import { Account } from "../../src/account/account"
import { Config } from "../../src/config/config"
import { Env } from "../../src/env"
import { RuntimeFlags } from "../../src/effect/runtime-flags" import { RuntimeFlags } from "../../src/effect/runtime-flags"
import { Workspace } from "../../src/control-plane/workspace" import { Workspace } from "../../src/control-plane/workspace"
import { Plugin } from "../../src/plugin/index" import { Plugin } from "../../src/plugin/index"
import { InstanceBootstrap } from "../../src/project/bootstrap-service" import { InstanceBootstrap } from "../../src/project/bootstrap"
import { InstanceStore } from "../../src/project/instance-store" import { InstanceStore } from "../../src/project/instance-store"
import { Project } from "../../src/project/project"
import { Vcs } from "../../src/project/vcs"
import { InstanceState } from "../../src/effect/instance-state" import { InstanceState } from "../../src/effect/instance-state"
import { Session } from "../../src/session/session"
import { SessionPrompt } from "../../src/session/prompt"
import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { disposeAllInstances, TestInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect" import { testEffect } from "../lib/effect"
import { AccountTest } from "../fake/account" import { AccountTest } from "../fake/account"
import { AuthTest } from "../fake/auth" import { AuthTest } from "../fake/auth"
import { NpmTest } from "../fake/npm" import { NpmTest } from "../fake/npm"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
const configLayer = Config.layer.pipe(
Layer.provide(EffectFlock.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(AuthTest.empty),
Layer.provide(AccountTest.empty),
Layer.provide(NpmTest.noop),
Layer.provide(FetchHttpClient.layer),
)
const pluginLayer = Plugin.layer.pipe(
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(configLayer),
Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })),
)
const noopBootstrapLayer = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) const noopBootstrapLayer = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
const workspaceLayer = Workspace.layer.pipe(
Layer.provide(Auth.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(SessionPrompt.defaultLayer),
Layer.provide(Project.defaultLayer),
Layer.provide(Vcs.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(Database.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrapLayer))),
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: true })),
)
const it = testEffect( const it = testEffect(
Layer.mergeAll(pluginLayer, workspaceLayer, CrossSpawnSpawner.defaultLayer).pipe(Layer.provide(Ripgrep.defaultLayer)), AppNodeBuilder.build(LayerNode.group([Plugin.node, Workspace.node, InstanceStore.node, Ripgrep.node]), [
[Auth.node, AuthTest.empty],
[Account.node, AccountTest.empty],
[Npm.node, NpmTest.noop],
[InstanceBootstrap.node, noopBootstrapLayer],
[RuntimeFlags.node, RuntimeFlags.layer({ disableDefaultPlugins: true, experimentalWorkspaces: true })],
]),
) )
afterEach(async () => { afterEach(async () => {

View File

@@ -1,5 +1,4 @@
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { EventV2Bridge } from "@/event-v2-bridge"
import { Project } from "@/project/project" import { Project } from "@/project/project"
import { $ } from "bun" import { $ } from "bun"
import path from "path" import path from "path"
@@ -15,19 +14,17 @@ import { SessionID } from "@/session/schema"
import { WorkspaceV2 } from "@opencode-ai/core/workspace" import { WorkspaceV2 } from "@opencode-ai/core/workspace"
import { Cause, Effect, Exit, Layer, Stream } from "effect" import { Cause, Effect, Exit, Layer, Stream } from "effect"
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
import { NodePath } from "@effect/platform-node"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { AppProcess } from "@opencode-ai/core/process"
import { ProjectV2 } from "@opencode-ai/core/project" import { ProjectV2 } from "@opencode-ai/core/project"
import { ProjectDirectories } from "@opencode-ai/core/project/directories"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { testEffect } from "../lib/effect" import { testEffect } from "../lib/effect"
import { RuntimeFlags } from "@/effect/runtime-flags" import { RuntimeFlags } from "@/effect/runtime-flags"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
const encoder = new TextEncoder() const encoder = new TextEncoder()
const layer = Layer.mergeAll(Project.defaultLayer, Database.defaultLayer, CrossSpawnSpawner.defaultLayer) const projectTestNode = LayerNode.group([Project.node, Database.node, CrossSpawnSpawner.node])
const it = testEffect(layer) const it = testEffect(AppNodeBuilder.build(projectTestNode))
function remoteProjectID(remote: string) { function remoteProjectID(remote: string) {
return ProjectV2.ID.make(Hash.fast(`git-remote:${remote}`)) return ProjectV2.ID.make(Hash.fast(`git-remote:${remote}`))
@@ -65,41 +62,37 @@ function mockGitFailure(failArg: string) {
}), }),
) )
}), }),
).pipe(Layer.provide(CrossSpawnSpawner.defaultLayer)) ).pipe(Layer.provide(AppNodeBuilder.build(CrossSpawnSpawner.node)))
} }
function projectLayerWithFailure(failArg: string) { function projectLayerWithFailure(failArg: string) {
return Project.layer.pipe( return AppNodeBuilder.build(Project.node, [
Layer.provide(AppProcess.layer.pipe(Layer.provide(mockGitFailure(failArg)))), [ProjectV2.node, projectV2FailureLayer()],
Layer.provide(mockGitFailure(failArg)), [CrossSpawnSpawner.node, mockGitFailure(failArg)],
Layer.provide(ProjectV2.defaultLayer), ])
Layer.provide(ProjectDirectories.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(NodePath.layer),
Layer.provide(Database.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
)
} }
function projectLayerWithRuntimeFlags(flags: Parameters<typeof RuntimeFlags.layer>[0]) { function projectV2FailureLayer() {
return Project.layer.pipe( return Layer.succeed(
Layer.provide(EventV2Bridge.defaultLayer), ProjectV2.Service,
Layer.provide(ProjectV2.defaultLayer), ProjectV2.Service.of({
Layer.provide(ProjectDirectories.defaultLayer), directories: () => Effect.succeed([]),
Layer.provide(AppProcess.defaultLayer), resolve: (input) =>
Layer.provide(FSUtil.defaultLayer), Effect.succeed({
Layer.provide(NodePath.layer), id: ProjectV2.ID.global,
Layer.provide(Database.defaultLayer), directory: input,
Layer.provide(RuntimeFlags.layer(flags)), vcs: { type: "git" as const, store: input },
}),
commit: () => Effect.void,
}),
) )
} }
const failureIt = (failArg: string) => const failureIt = (failArg: string) =>
testEffect(Layer.mergeAll(projectLayerWithFailure(failArg), CrossSpawnSpawner.defaultLayer)) testEffect(AppNodeBuilder.build(projectTestNode, [[Project.node, projectLayerWithFailure(failArg)]]))
const iconDiscoveryIt = testEffect( const iconDiscoveryIt = testEffect(
Layer.provideMerge(projectLayerWithRuntimeFlags({ experimentalIconDiscovery: true }), CrossSpawnSpawner.defaultLayer), AppNodeBuilder.build(projectTestNode, [[RuntimeFlags.node, RuntimeFlags.layer({ experimentalIconDiscovery: true })]]),
) )
function waitForProjectIcon(id: ProjectV2.ID, attempts = 50): Effect.Effect<Project.Info, never, Project.Service> { function waitForProjectIcon(id: ProjectV2.ID, attempts = 50): Effect.Effect<Project.Info, never, Project.Service> {

View File

@@ -7,12 +7,9 @@ import { APICallError } from "ai"
import { Cause, Deferred, Effect, Exit, Fiber, Layer, Schema } from "effect" import { Cause, Deferred, Effect, Exit, Fiber, Layer, Schema } from "effect"
import * as Stream from "effect/Stream" import * as Stream from "effect/Stream"
import { Config } from "@/config/config" import { Config } from "@/config/config"
import { Image } from "@/image/image"
import { Agent } from "../../src/agent/agent"
import { LLM } from "../../src/session/llm" import { LLM } from "../../src/session/llm"
import { SessionCompaction } from "../../src/session/compaction" import { SessionCompaction } from "../../src/session/compaction"
import { Token } from "@/util/token" import { Token } from "@/util/token"
import { Permission } from "../../src/permission"
import { Plugin } from "../../src/plugin" import { Plugin } from "../../src/plugin"
import { provideTmpdirInstance, TestInstance } from "../fixture/fixture" import { provideTmpdirInstance, TestInstance } from "../fixture/fixture"
import { Session as SessionNs } from "@/session/session" import { Session as SessionNs } from "@/session/session"
@@ -21,12 +18,11 @@ import { MessageID, PartID, SessionID } from "../../src/session/schema"
import { SessionStatus } from "../../src/session/status" import { SessionStatus } from "../../src/session/status"
import { SessionSummary } from "../../src/session/summary" import { SessionSummary } from "../../src/session/summary"
import { SessionV2 } from "@opencode-ai/core/session" import { SessionV2 } from "@opencode-ai/core/session"
import { locationServiceMapLayer } from "@opencode-ai/core/location-services"
import { SessionExecution } from "@opencode-ai/core/session/execution" import { SessionExecution } from "@opencode-ai/core/session/execution"
import { SessionProjector } from "@opencode-ai/core/session/projector"
import type { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import * as SessionProcessorModule from "../../src/session/processor" import * as SessionProcessorModule from "../../src/session/processor"
import { Snapshot } from "../../src/snapshot"
import { ProviderTest } from "../fake/provider" import { ProviderTest } from "../fake/provider"
import { testEffect } from "../lib/effect" import { testEffect } from "../lib/effect"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
@@ -35,6 +31,8 @@ import { RuntimeFlags } from "@/effect/runtime-flags"
import { LLMEvent, Usage } from "@opencode-ai/llm" import { LLMEvent, Usage } from "@opencode-ai/llm"
import { ProviderV2 } from "@opencode-ai/core/provider" import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
const summary = Layer.succeed( const summary = Layer.succeed(
SessionSummary.Service, SessionSummary.Service,
@@ -210,7 +208,7 @@ function fake(
} satisfies SessionProcessorModule.SessionProcessor.Handle } satisfies SessionProcessorModule.SessionProcessor.Handle
} }
function layer(result: "continue" | "compact") { function processorLayer(result: "continue" | "compact") {
return Layer.succeed( return Layer.succeed(
SessionProcessorModule.SessionProcessor.Service, SessionProcessorModule.SessionProcessor.Service,
SessionProcessorModule.SessionProcessor.Service.of({ SessionProcessorModule.SessionProcessor.Service.of({
@@ -221,38 +219,28 @@ function layer(result: "continue" | "compact") {
function cfg(compaction?: ConfigV1.Info["compaction"]) { function cfg(compaction?: ConfigV1.Info["compaction"]) {
const base = Schema.decodeUnknownSync(ConfigV1.Info)({}) as ConfigV1.Info const base = Schema.decodeUnknownSync(ConfigV1.Info)({}) as ConfigV1.Info
return TestConfig.layer({ return Layer.succeed(Config.Service, TestConfig.make({ get: () => Effect.succeed({ ...base, compaction }) }))
get: () => Effect.succeed({ ...base, compaction }),
})
} }
const deps = Layer.mergeAll( const defaultProvider = wide()
wide().layer, const compactionTestNode = LayerNode.group([
layer("continue"), SessionCompaction.node,
Agent.defaultLayer, SessionNs.node,
Plugin.defaultLayer, SessionProjector.node,
EventV2Bridge.defaultLayer, Database.node,
Config.defaultLayer, EventV2Bridge.node,
RuntimeFlags.layer({ experimentalEventSystem: true }), CrossSpawnSpawner.node,
Database.defaultLayer, ])
EventV2Bridge.defaultLayer, const env = AppNodeBuilder.build(compactionTestNode, [
) [Provider.node, defaultProvider.layer],
[SessionProcessorModule.SessionProcessor.node, processorLayer("continue")],
const env = Layer.mergeAll( [RuntimeFlags.node, RuntimeFlags.layer({ experimentalEventSystem: true })],
SessionNs.defaultLayer, ])
Database.defaultLayer,
EventV2Bridge.defaultLayer,
CrossSpawnSpawner.defaultLayer,
SessionCompaction.layer.pipe(Layer.provide(SessionNs.defaultLayer), Layer.provideMerge(deps)),
)
const it = testEffect(env) const it = testEffect(env)
const compactionEnv = Layer.mergeAll( const compactionEnv = AppNodeBuilder.build(
SessionNs.defaultLayer, LayerNode.group([SessionNs.node, SessionProjector.node, Database.node, EventV2Bridge.node, CrossSpawnSpawner.node]),
Database.defaultLayer,
EventV2Bridge.defaultLayer,
CrossSpawnSpawner.defaultLayer,
) )
const itCompaction = testEffect(compactionEnv) const itCompaction = testEffect(compactionEnv)
@@ -260,7 +248,7 @@ type CompactionProcessOptions = {
result?: "continue" | "compact" result?: "continue" | "compact"
llm?: Layer.Layer<LLM.Service> llm?: Layer.Layer<LLM.Service>
plugin?: Layer.Layer<Plugin.Service> plugin?: Layer.Layer<Plugin.Service>
provider?: ReturnType<typeof ProviderTest.fake> provider?: ReturnType<typeof wide>
config?: Layer.Layer<Config.Service> config?: Layer.Layer<Config.Service>
} }
@@ -269,30 +257,25 @@ function withCompaction(options?: CompactionProcessOptions) {
} }
function compactionProcessLayer(options?: CompactionProcessOptions) { function compactionProcessLayer(options?: CompactionProcessOptions) {
const events = EventV2Bridge.defaultLayer const replacements: LayerNode.Replacements = [
const status = SessionStatus.layer.pipe(Layer.provide(events)) [Provider.node, (options?.provider ?? wide()).layer],
const processor = options?.llm [RuntimeFlags.node, RuntimeFlags.layer({ experimentalEventSystem: true })],
? SessionProcessorModule.SessionProcessor.layer.pipe( [SessionSummary.node, summary],
Layer.provide(summary), ]
Layer.provide(Image.defaultLayer), if (!options?.llm) {
Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })), return AppNodeBuilder.build(compactionTestNode, [
Layer.provide(status), ...replacements,
) [SessionProcessorModule.SessionProcessor.node, processorLayer(options?.result ?? "continue")],
: layer(options?.result ?? "continue") ...(options?.plugin ? ([[Plugin.node, options.plugin]] as const) : []),
return Layer.mergeAll(SessionCompaction.layer.pipe(Layer.provide(processor)), processor, events, status).pipe( ...(options?.config ? ([[Config.node, options.config]] as const) : []),
Layer.provide(SessionNs.defaultLayer), ])
Layer.provide((options?.provider ?? wide()).layer), }
Layer.provide(Snapshot.defaultLayer), return AppNodeBuilder.build(compactionTestNode, [
Layer.provide(options?.llm ?? LLM.defaultLayer), ...replacements,
Layer.provide(Permission.defaultLayer), [LLM.node, options.llm],
Layer.provide(Agent.defaultLayer), ...(options?.plugin ? ([[Plugin.node, options.plugin]] as const) : []),
Layer.provide(options?.plugin ?? Plugin.defaultLayer), ...(options?.config ? ([[Config.node, options.config]] as const) : []),
Layer.provide(status), ])
Layer.provide(events),
Layer.provide(options?.config ?? Config.defaultLayer),
Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })),
Layer.provide(EventV2Bridge.defaultLayer),
)
} }
function createSummaryCompaction(sessionID: SessionID) { function createSummaryCompaction(sessionID: SessionID) {
@@ -318,7 +301,7 @@ function llm() {
push(stream: Stream.Stream<LLMEvent, unknown> | ((input: LLM.StreamInput) => Stream.Stream<LLMEvent, unknown>)) { push(stream: Stream.Stream<LLMEvent, unknown> | ((input: LLM.StreamInput) => Stream.Stream<LLMEvent, unknown>)) {
queue.push(stream) queue.push(stream)
}, },
layer: Layer.succeed( llmLayer: Layer.succeed(
LLM.Service, LLM.Service,
LLM.Service.of({ LLM.Service.of({
stream: (input) => { stream: (input) => {
@@ -614,9 +597,7 @@ describe("session.compaction.create", () => {
}) })
const v2 = yield* SessionV2.Service.use((svc) => svc.messages({ sessionID: info.id })).pipe( const v2 = yield* SessionV2.Service.use((svc) => svc.messages({ sessionID: info.id })).pipe(
Effect.provide(SessionV2.defaultLayer), Effect.provide(AppNodeBuilder.build(SessionV2.node, [[SessionExecution.node, SessionExecution.noopLayer]])),
Effect.provide(SessionExecution.noopLayer),
Effect.provide(locationServiceMapLayer),
) )
expect(v2.at(-1)).toMatchObject({ expect(v2.at(-1)).toMatchObject({
type: "compaction", type: "compaction",
@@ -1014,7 +995,7 @@ describe("session.compaction.process", () => {
expect(part?.type).toBe("compaction") expect(part?.type).toBe("compaction")
expect(part?.tail_start_id).toBeUndefined() expect(part?.tail_start_id).toBeUndefined()
expect(captured).toContain("yyyy") expect(captured).toContain("yyyy")
}).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 20 }) })) }).pipe(withCompaction({ llm: stub.llmLayer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 20 }) }))
}, },
{ git: true }, { git: true },
) )
@@ -1051,7 +1032,7 @@ describe("session.compaction.process", () => {
expect(part?.tail_start_id).toBeUndefined() expect(part?.tail_start_id).toBeUndefined()
expect(captured).toContain("recent image turn") expect(captured).toContain("recent image turn")
expect(captured).toContain("Attached image/png: big.png") expect(captured).toContain("Attached image/png: big.png")
}).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) })) }).pipe(withCompaction({ llm: stub.llmLayer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) }))
}, },
{ git: true }, { git: true },
) )
@@ -1102,7 +1083,7 @@ describe("session.compaction.process", () => {
expect(filtered[1]?.info.role).toBe("assistant") expect(filtered[1]?.info.role).toBe("assistant")
expect(filtered[1]?.info.role === "assistant" ? filtered[1].info.summary : false).toBe(true) expect(filtered[1]?.info.role === "assistant" ? filtered[1].info.summary : false).toBe(true)
expect(filtered.map((msg) => msg.info.id)).not.toContain(large.id) expect(filtered.map((msg) => msg.info.id)).not.toContain(large.id)
}).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) })) }).pipe(withCompaction({ llm: stub.llmLayer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) }))
}, },
{ git: true }, { git: true },
) )
@@ -1263,7 +1244,7 @@ describe("session.compaction.process", () => {
expect(Cause.hasInterrupts(exit.cause)).toBe(true) expect(Cause.hasInterrupts(exit.cause)).toBe(true)
expect(Date.now() - start).toBeLessThan(250) expect(Date.now() - start).toBeLessThan(250)
} }
}).pipe(withCompaction({ llm: stub.layer })) }).pipe(withCompaction({ llm: stub.llmLayer }))
}, },
{ git: true }, { git: true },
{ timeout: 10_000 }, { timeout: 10_000 },
@@ -1336,7 +1317,7 @@ describe("session.compaction.process", () => {
expect(summary?.parts.some((part) => part.type === "reasoning")).toBe(false) expect(summary?.parts.some((part) => part.type === "reasoning")).toBe(false)
// Sanity: the text part still got through. // Sanity: the text part still got through.
expect(summary?.parts.some((part) => part.type === "text" && part.text === "summary")).toBe(true) expect(summary?.parts.some((part) => part.type === "text" && part.text === "summary")).toBe(true)
}).pipe(withCompaction({ llm: stub.layer })) }).pipe(withCompaction({ llm: stub.llmLayer }))
}, },
{ git: true }, { git: true },
) )
@@ -1372,7 +1353,7 @@ describe("session.compaction.process", () => {
expect(summary?.info.role).toBe("assistant") expect(summary?.info.role).toBe("assistant")
expect(summary?.parts.some((part) => part.type === "tool")).toBe(false) expect(summary?.parts.some((part) => part.type === "tool")).toBe(false)
}).pipe(withCompaction({ llm: stub.layer })) }).pipe(withCompaction({ llm: stub.llmLayer }))
}, },
{ git: true }, { git: true },
) )
@@ -1409,7 +1390,7 @@ describe("session.compaction.process", () => {
expect(captured).not.toContain("keep this turn") expect(captured).not.toContain("keep this turn")
expect(captured).not.toContain("and this one too") expect(captured).not.toContain("and this one too")
expect(captured).not.toContain("What did we do so far?") expect(captured).not.toContain("What did we do so far?")
}).pipe(withCompaction({ llm: stub.layer })) }).pipe(withCompaction({ llm: stub.llmLayer }))
}, },
{ git: true }, { git: true },
) )
@@ -1451,7 +1432,7 @@ describe("session.compaction.process", () => {
expect(captured.match(/summary one/g)?.length).toBe(1) expect(captured.match(/summary one/g)?.length).toBe(1)
expect(captured).toContain("## Constraints & Preferences") expect(captured).toContain("## Constraints & Preferences")
expect(captured).toContain("## Progress") expect(captured).toContain("## Progress")
}).pipe(withCompaction({ llm: stub.layer })) }).pipe(withCompaction({ llm: stub.llmLayer }))
}, },
{ git: true }, { git: true },
) )
@@ -1493,7 +1474,7 @@ describe("session.compaction.process", () => {
expect( expect(
filtered.some((msg) => msg.info.role === "user" && msg.parts.some((part) => part.type === "compaction")), filtered.some((msg) => msg.info.role === "user" && msg.parts.some((part) => part.type === "compaction")),
).toBe(true) ).toBe(true)
}).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) })) }).pipe(withCompaction({ llm: stub.llmLayer, config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) }))
}) })
itCompaction.instance( itCompaction.instance(