Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171import { describe, expect } from "bun:test"import { LLMClient, LLMEvent, LanguageModel, type LLMRequest } from "@opencode/ai"import { OpenAIChat } from "@opencode/ai/protocols"import { Config } from "@opencode/core/config"import { Database } from "@opencode/core/database/database"import { AppNodeBuilder } from "@opencode/core/effect/app-node-builder"import { LayerNode } from "@opencode/util/effect/layer-node"import { Bus } from "@opencode/core/bus"import { Location } from "@opencode/core/location"import { LocationServiceMap } from "@opencode/core/location-service-map"import type { LocationServices } from "@opencode/core/location-services"import { Project } from "@opencode/core/project"import { AbsolutePath } from "@opencode/core/schema"import { Session } from "@opencode/core/session"import { SessionCompaction } from "@opencode/core/session/compaction"import { SessionEvent } from "@opencode/core/session/event"import { SessionMessage } from "@opencode/core/session/message"import { SessionProjector } from "@opencode/core/session/projector"import { SessionExecution } from "@opencode/core/session/execution"import { SessionRunnerModel } from "@opencode/core/session/runner/model"import { SessionStore } from "@opencode/core/session/store"import { Effect, Layer, LayerMap, Stream } from "effect"import { testEffect } from "./lib/effect"import { globalProjectNode } from "./lib/project"
const location = Location.Ref.make({ directory: AbsolutePath.make("/project") })const model = LanguageModel.make({ id: "summary-model", provider: "test", route: OpenAIChat.route,})let requests: LLMRequest[] = []const client = Layer.mock(LLMClient.Service)({ stream: (request: LLMRequest) => { requests.push(request) return Stream.make(LLMEvent.textDelta({ id: "summary", text: "manual session summary" })) }, generate: () => Effect.die("unused"),})const config = Layer.mock(Config.Service)({ entries: () => Effect.succeed([]) })const models = Layer.mock(SessionRunnerModel.Service)({ resolve: () => Effect.succeed( SessionRunnerModel.resolved(model, { capabilities: { tools: true, input: ["text", "image"], output: ["text"] }, cost: [], limit: { context: 10_000, output: 1_000 }, }), ),})const locations = Layer.effect( LocationServiceMap.Service, LayerMap.make( () => // The test only needs the compaction location service used by Session.compact. // oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion SessionCompaction.layer.pipe( Layer.provide(client), Layer.provide(config), Layer.provide(models), ) as unknown as Layer.Layer<LocationServices>, ),)const it = testEffect( AppNodeBuilder.build( LayerNode.group([Database.node, Bus.node, SessionProjector.node, SessionStore.node, Session.node]), [ LocationServiceMap.node.replace(locations), Project.node.replace(globalProjectNode), SessionExecution.node.replace(SessionExecution.noopLayer), ], ),)
describe("Session.compact", () => { it.effect("durably coalesces manual compaction", () => Effect.gen(function* () { requests = [] const session = yield* Session.Service const bus = yield* Bus.Service const created = yield* session.create({ location })
const messageID = SessionMessage.ID.create() yield* bus.publish(SessionEvent.InboxEnqueued, { sessionID: created.id, inboxID: messageID, item: { type: "user", payload: { text: "Please compact this session history." }, delivery: "steer", }, }) yield* bus.publish(SessionEvent.InboxDelivered, { sessionID: created.id, inboxID: messageID, })
expect(yield* session.compact({ id: messageID, sessionID: created.id }).pipe(Effect.flip)).toMatchObject({ _tag: "Session.CompactionConflictError", inputID: messageID, }) const first = yield* session.compact({ sessionID: created.id }) const second = yield* session.compact({ sessionID: created.id })
expect(second.id).toBe(first.id) expect(requests).toHaveLength(0) expect(yield* session.inbox(created.id)).toEqual([ expect.objectContaining({ id: first.id, type: "compaction", delivery: "steer" }), ]) expect((yield* session.context(created.id)).find((message) => message.id === first.id)).toBeUndefined()
const queued = yield* session.create({ location }) const queue = yield* session.compact({ sessionID: queued.id, delivery: "queue" }) expect(queue).toMatchObject({ type: "compaction", delivery: "queue" }) }), )
it.effect("coalesces concurrent manual compaction", () => Effect.gen(function* () { const session = yield* Session.Service const created = yield* session.create({ location }) const admitted = yield* Effect.all( [SessionMessage.ID.create(), SessionMessage.ID.create()].map((id) => session.compact({ id, sessionID: created.id }), ), { concurrency: "unbounded" }, )
expect(admitted[1]?.id).toBe(admitted[0]?.id) expect(yield* session.inbox(created.id)).toHaveLength(1) }), )
it.effect("commits a staged revert before admitting manual compaction", () => Effect.gen(function* () { const session = yield* Session.Service const bus = yield* Bus.Service const created = yield* session.create({ location }) const messageID = SessionMessage.ID.create()
yield* bus.publish(SessionEvent.InboxEnqueued, { sessionID: created.id, inboxID: messageID, item: { type: "user", payload: { text: "Undo this prompt before compacting." }, delivery: "steer", }, }) yield* bus.publish(SessionEvent.InboxDelivered, { sessionID: created.id, inboxID: messageID, }) yield* bus.publish(SessionEvent.RevertEvent.Staged, { sessionID: created.id, revert: { messageID, files: [] }, })
expect((yield* session.get(created.id)).revert?.messageID).toBe(messageID)
const compacted = yield* session.compact({ sessionID: created.id })
expect((yield* session.get(created.id)).revert).toBeUndefined() expect(yield* session.context(created.id)).toEqual([]) expect(yield* session.inbox(created.id)).toEqual([ expect.objectContaining({ id: compacted.id, type: "compaction" }), ]) }), )})