From 8cb02ba9cf76abbda9fbf50d913b78f0cb67b776 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Thu, 4 Jun 2026 21:32:37 -0400 Subject: [PATCH] refactor(core): trim v2 context epoch scope --- packages/core/src/event.ts | 8 +- packages/core/src/location-layer.ts | 2 +- packages/core/src/plugin/boot.ts | 4 - packages/core/src/session/runner/llm.ts | 12 +- packages/core/test/database-migration.test.ts | 153 ++++++++++-------- .../openai-chat-streams-text.json | 2 +- .../core/test/session-runner-message.test.ts | 60 +++---- .../core/test/session-runner-recorded.test.ts | 24 +-- packages/core/test/session-runner.test.ts | 34 ---- ...haiku-4-5-chronological-system-update.json | 33 ---- ...2-5-flash-chronological-system-update.json | 32 ---- ...t-4o-mini-chronological-system-update.json | 32 ---- ...s-gpt-5-5-chronological-system-update.json | 39 ----- .../llm/test/provider/golden.recorded.test.ts | 6 +- packages/llm/test/recorded-scenarios.ts | 27 ---- packages/llm/test/schema.test.ts | 12 -- .../opencode/test/cli/tui/sync-v2.test.tsx | 48 ------ 17 files changed, 117 insertions(+), 411 deletions(-) delete mode 100644 packages/llm/test/fixtures/recordings/anthropic-messages/anthropic-haiku-4-5-chronological-system-update.json delete mode 100644 packages/llm/test/fixtures/recordings/gemini/gemini-2-5-flash-chronological-system-update.json delete mode 100644 packages/llm/test/fixtures/recordings/openai-chat/openai-chat-gpt-4o-mini-chronological-system-update.json delete mode 100644 packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-chronological-system-update.json diff --git a/packages/core/src/event.ts b/packages/core/src/event.ts index 1362bf1e417..0ad0714e98d 100644 --- a/packages/core/src/event.ts +++ b/packages/core/src/event.ts @@ -381,10 +381,10 @@ export const layerWith = (options?: LayerOptions) => }) } - function publishEvent(event: Payload, options?: PublishOptions) { + function publishEvent(event: Payload, commit?: PublishOptions["commit"]) { return Effect.gen(function* () { const durable = registry.get(event.type)?.sync !== undefined - if (!durable && options?.commit) + if (!durable && commit) return yield* Effect.die( new InvalidSyncEventError({ type: event.type, @@ -392,7 +392,7 @@ export const layerWith = (options?: LayerOptions) => }), ) if (durable) { - const committed = yield* commitSyncEvent(event as Payload, undefined, options?.commit) + const committed = yield* commitSyncEvent(event as Payload, undefined, commit) if (committed) { event = { ...event, seq: committed.seq } yield* Effect.forEach(syncHandlers, (sync) => observe(event as Payload, "sync", sync), { discard: true }) @@ -446,7 +446,7 @@ export const layerWith = (options?: LayerOptions) => ...(location ? { location } : {}), data, } as Payload, - options, + options?.commit, ) }) } diff --git a/packages/core/src/location-layer.ts b/packages/core/src/location-layer.ts index d6c01e1c869..c1621c404f6 100644 --- a/packages/core/src/location-layer.ts +++ b/packages/core/src/location-layer.ts @@ -57,7 +57,7 @@ export class LocationServiceMap extends LayerMap.Service()(" Catalog.locationLayer, CommandV2.locationLayer, AgentV2.locationLayer, - PluginBoot.locationLayer.pipe(Layer.provide(systemContext)), + PluginBoot.locationLayer, FileSystem.locationLayer, Watcher.locationLayer, Pty.locationLayer, diff --git a/packages/core/src/plugin/boot.ts b/packages/core/src/plugin/boot.ts index 7cdc54fcf2b..6a589a1ef0d 100644 --- a/packages/core/src/plugin/boot.ts +++ b/packages/core/src/plugin/boot.ts @@ -25,7 +25,6 @@ import { EnvPlugin } from "./env" import { ModelsDevPlugin } from "./models-dev" import { ProviderPlugins } from "./provider" import { SkillV2 } from "../skill" -import { SystemContextRegistry } from "../system-context-registry" type Plugin = { id: PluginV2.ID @@ -43,7 +42,6 @@ type Plugin = { | Config.Service | ModelsDev.Service | SkillV2.Service - | SystemContextRegistry.Service > } @@ -69,7 +67,6 @@ export const layer = Layer.effect( const fs = yield* FSUtil.Service const global = yield* Global.Service const skill = yield* SkillV2.Service - const systemContext = yield* SystemContextRegistry.Service const done = yield* Deferred.make() const add = Effect.fn("PluginBoot.add")(function* (input: Plugin) { @@ -89,7 +86,6 @@ export const layer = Layer.effect( Effect.provideService(Global.Service, global), Effect.provideService(SkillV2.Service, skill), Effect.provideService(PluginV2.Service, plugin), - Effect.provideService(SystemContextRegistry.Service, systemContext), ), }) }) diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts index 3eda133e4ce..b628861033a 100644 --- a/packages/core/src/session/runner/llm.ts +++ b/packages/core/src/session/runner/llm.ts @@ -98,13 +98,6 @@ export const layer = Layer.effect( const getContext = Effect.fn("SessionRunner.getContext")(function* (sessionID: SessionSchema.ID) { return yield* store.context(sessionID) }) - const getRunnerContext = Effect.fn("SessionRunner.getRunnerContext")(function* ( - sessionID: SessionSchema.ID, - baselineSeq: number, - ) { - return yield* store.runnerContext(sessionID, baselineSeq) - }) - const failInterruptedTools = Effect.fn("SessionRunner.failInterruptedTools")(function* ( sessionID: SessionSchema.ID, ) { @@ -152,7 +145,7 @@ export const layer = Layer.effect( } } const system = initialized ?? (yield* SessionContextEpoch.prepare(db, events, systemContext, session.id)) - const context = yield* getRunnerContext(session.id, system.baselineSeq) + const context = yield* store.runnerContext(session.id, system.baselineSeq) const request = LLM.request({ model, system: system.baseline.length > 0 ? [SystemPart.make(system.baseline)] : [], @@ -251,7 +244,6 @@ export const layer = Layer.effect( readonly sessionID: SessionSchema.ID readonly force?: boolean }) { - const session = yield* getSession(input.sessionID) const hasSteer = yield* SessionInput.hasPending(db, input.sessionID, "steer") const hasQueue = hasSteer ? false : yield* SessionInput.hasPending(db, input.sessionID, "queue") if (input.force !== true && !hasSteer && !hasQueue) return @@ -261,7 +253,7 @@ export const layer = Layer.effect( while (openActivity) { let needsContinuation = true for (let step = 0; step < MAX_STEPS; step++) { - needsContinuation = yield* runTurn(session.id, promotion) + needsContinuation = yield* runTurn(input.sessionID, promotion) promotion = "steer" if (!needsContinuation) needsContinuation = yield* SessionInput.hasPending(db, input.sessionID, "steer") if (!needsContinuation) break diff --git a/packages/core/test/database-migration.test.ts b/packages/core/test/database-migration.test.ts index 2cd5510be77..52c1e920ad4 100644 --- a/packages/core/test/database-migration.test.ts +++ b/packages/core/test/database-migration.test.ts @@ -1,4 +1,4 @@ -import { describe, expect } from "bun:test" +import { describe, expect, test } from "bun:test" import { $ } from "bun" import { fileURLToPath } from "url" import path from "path" @@ -18,45 +18,43 @@ import { AbsolutePath } from "@opencode-ai/core/schema" import { SessionSchema } from "@opencode-ai/core/session/schema" import { SessionTable } from "@opencode-ai/core/session/sql" import sessionMetadataMigration from "@opencode-ai/core/database/migration/20260511173437_session-metadata" +import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient" import { Database } from "@opencode-ai/core/database/database" import { tmpdir } from "./fixture/tmpdir" -import { testEffect } from "./lib/effect" + +const run = (effect: Effect.Effect) => + Effect.runPromise( + effect.pipe(Effect.provide(SqliteClient.layer({ filename: ":memory:", disableWAL: true })), Effect.scoped), + ) const makeDb = EffectDrizzleSqlite.makeWithDefaults() -const it = testEffect(SqliteClient.layer({ filename: ":memory:", disableWAL: true })) describe("DatabaseMigration", () => { - it.effect("serializes concurrent embedded initialization for one database path", () => - Effect.promise(async () => { - await using tmp = await tmpdir() - const filename = path.join(tmp.path, "embedded.sqlite") - const layers = [Database.layerFromPath(filename), Database.layerFromPath(filename)] + test("serializes concurrent embedded initialization for one database path", async () => { + await using tmp = await tmpdir() + const filename = path.join(tmp.path, "embedded.sqlite") + const layers = [Database.layerFromPath(filename), Database.layerFromPath(filename)] - await Effect.runPromise( - Effect.all( - layers.map((layer) => Effect.scoped(Layer.build(layer))), - { concurrency: "unbounded" }, - ), - ) - }), - ) - if (process.platform === "linux") { - it.effect( - "declared schema has no ungenerated migrations", - () => - Effect.promise(async () => { - const result = await $`bun ${fileURLToPath(new URL("../script/migration.ts", import.meta.url))} --check` - .quiet() - .nothrow() - expect(result.exitCode, result.stderr.toString()).toBe(0) - expect(result.stdout.toString()).toContain("No schema changes, nothing to migrate") - }), - 30_000, + await Effect.runPromise( + Effect.all( + layers.map((layer) => Effect.scoped(Layer.build(layer))), + { concurrency: "unbounded" }, + ), ) + }) + if (process.platform === "linux") { + test("declared schema has no ungenerated migrations", async () => { + const result = await $`bun ${fileURLToPath(new URL("../script/migration.ts", import.meta.url))} --check` + .quiet() + .nothrow() + expect(result.exitCode, result.stderr.toString()).toBe(0) + expect(result.stdout.toString()).toContain("No schema changes, nothing to migrate") + }, 30_000) } - it.effect("applies tracked migrations to an empty database", () => - Effect.gen(function* () { + test("applies tracked migrations to an empty database", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* DatabaseMigration.apply(db) @@ -84,11 +82,13 @@ describe("DatabaseMigration", () => { { name: "session_message_session_time_created_id_idx" }, { name: "session_message_session_type_seq_idx" }, ]) - }), - ) + }), + ) + }) - it.effect("resets beta history and rebuilds event-sourced Session input storage", () => - Effect.gen(function* () { + test("resets beta history and rebuilds event-sourced Session input storage", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, workspace_id text)`) yield* db.run(sql`CREATE TABLE workspace (id text PRIMARY KEY)`) @@ -158,11 +158,13 @@ describe("DatabaseMigration", () => { expect.objectContaining({ name: "session_input_session_promoted_seq_idx", unique: 1 }), expect.objectContaining({ name: "session_input_session_admitted_seq_idx", unique: 1 }), ]) - }), - ) + }), + ) + }) - it.effect("resets incompatible projected Session messages before adding sequence order", () => - Effect.gen(function* () { + test("resets incompatible projected Session messages before adding sequence order", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY)`) yield* db.run( @@ -211,11 +213,13 @@ describe("DatabaseMigration", () => { sql`INSERT INTO session_message (id, session_id, type, seq, time_created, time_updated, data) VALUES ('fresh_projection', 'session', 'user', 7, 2, 2, '{}')`, ) expect(yield* db.get(sql`SELECT id, seq FROM session_message`)).toEqual({ id: "fresh_projection", seq: 7 }) - }), - ) + }), + ) + }) - it.effect("runs session usage backfill in order with schema changes", () => - Effect.gen(function* () { + test("runs session usage backfill in order with schema changes", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, time_updated integer NOT NULL)`) yield* db.run(sql`CREATE TABLE message (id text PRIMARY KEY, session_id text NOT NULL, data text NOT NULL)`) @@ -238,11 +242,13 @@ describe("DatabaseMigration", () => { tokens_cache_read: 5, tokens_cache_write: 6, }) - }), - ) + }), + ) + }) - it.effect("normalizes Windows storage paths and leaves POSIX paths untouched", () => - Effect.gen(function* () { + test("normalizes Windows storage paths and leaves POSIX paths untouched", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE project (id text PRIMARY KEY, worktree text NOT NULL, sandboxes text NOT NULL)`) yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, directory text NOT NULL, path text)`) @@ -287,12 +293,14 @@ describe("DatabaseMigration", () => { directory: "/home/me/we\\ird", path: "src\\weird", }) - }), - ) + }), + ) + }) - it.effect("maps native Windows paths through database columns", () => { - if (process.platform !== "win32") return Effect.void - return Effect.gen(function* () { + test("maps native Windows paths through database columns", async () => { + if (process.platform !== "win32") return + await run( + Effect.gen(function* () { const db = yield* makeDb yield* DatabaseMigration.apply(db) const projectID = ProjectV2.ID.make("codec_project") @@ -395,11 +403,13 @@ describe("DatabaseMigration", () => { expect(() => Effect.runSync(db.select().from(ProjectTable).where(eq(ProjectTable.id, projectID)).get()), ).toThrow() - }) + }), + ) }) - it.effect("imports existing drizzle migration state", () => - Effect.gen(function* () { + test("imports existing drizzle migration state", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run( sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`, @@ -412,11 +422,13 @@ describe("DatabaseMigration", () => { yield* DatabaseMigration.applyOnly(db, []) expect(yield* db.get(sql`SELECT id FROM migration`)).toEqual({ id: "20260127222353_familiar_lady_ursula" }) - }), - ) + }), + ) + }) - it.effect("does not replay a migrated session metadata column", () => - Effect.gen(function* () { + test("does not replay a migrated session metadata column", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, metadata text)`) yield* db.run( @@ -430,11 +442,13 @@ describe("DatabaseMigration", () => { yield* DatabaseMigration.applyOnly(db, [sessionMetadataMigration]) expect(yield* db.all(sql`SELECT id FROM migration`)).toEqual([{ id: "20260511173437_session-metadata" }]) - }), - ) + }), + ) + }) - it.effect("accepts the temporary replacement session metadata migration id", () => - Effect.gen(function* () { + test("accepts the temporary replacement session metadata migration id", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, metadata text)`) yield* db.run(sql`CREATE TABLE migration (id TEXT PRIMARY KEY, time_completed INTEGER NOT NULL)`) @@ -446,11 +460,13 @@ describe("DatabaseMigration", () => { { id: "20260511173437_session-metadata" }, { id: "20260530232709_lovely_romulus" }, ]) - }), - ) + }), + ) + }) - it.effect("skips drizzle import when migration table already has state", () => - Effect.gen(function* () { + test("skips drizzle import when migration table already has state", async () => { + await run( + Effect.gen(function* () { const db = yield* makeDb yield* db.run(sql`CREATE TABLE migration (id TEXT PRIMARY KEY, time_completed INTEGER NOT NULL)`) yield* db.run(sql`INSERT INTO migration (id, time_completed) VALUES ('existing', 1)`) @@ -465,6 +481,7 @@ describe("DatabaseMigration", () => { yield* DatabaseMigration.applyOnly(db, []) expect(yield* db.all(sql`SELECT id FROM migration ORDER BY id`)).toEqual([{ id: "existing" }]) - }), - ) + }), + ) + }) }) diff --git a/packages/core/test/fixtures/recordings/session-runner/openai-chat-streams-text.json b/packages/core/test/fixtures/recordings/session-runner/openai-chat-streams-text.json index 0e452bb6183..bad659f93ef 100644 --- a/packages/core/test/fixtures/recordings/session-runner/openai-chat-streams-text.json +++ b/packages/core/test/fixtures/recordings/session-runner/openai-chat-streams-text.json @@ -13,7 +13,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"system\",\"content\":\"Recorded context\"},{\"role\":\"user\",\"content\":\"Say hello in one short sentence.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_tokens\":20,\"temperature\":0}" + "body": "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"user\",\"content\":\"Say hello in one short sentence.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_tokens\":20,\"temperature\":0}" }, "response": { "status": 200, diff --git a/packages/core/test/session-runner-message.test.ts b/packages/core/test/session-runner-message.test.ts index 9e0398c92a9..47b018fa122 100644 --- a/packages/core/test/session-runner-message.test.ts +++ b/packages/core/test/session-runner-message.test.ts @@ -1,4 +1,4 @@ -import { describe, expect } from "bun:test" +import { describe, expect, test } from "bun:test" import { Message, Model } from "@opencode-ai/llm" import * as OpenAIChat from "@opencode-ai/llm/protocols/openai-chat" import { ModelV2 } from "@opencode-ai/core/model" @@ -8,16 +8,14 @@ import { AgentAttachment, FileAttachment, ReferenceAttachment } from "@opencode- import { toLLMMessages } from "@opencode-ai/core/session/runner/to-llm-message" import { SessionV2 } from "@opencode-ai/core/session" import { ToolOutput } from "@opencode-ai/core/tool-output" -import { DateTime, Effect } from "effect" -import { it } from "./lib/effect" +import { DateTime } from "effect" const created = DateTime.makeUnsafe(0) const id = (value: string) => SessionMessage.ID.make(`msg_${value}`) const model = Model.make({ id: "model", provider: "provider", route: OpenAIChat.route }) describe("toLLMMessages", () => { - it.effect("maps every top-level V2 Session message type", () => - Effect.sync(() => { + test("maps every top-level V2 Session message type", () => { const file = new FileAttachment({ uri: "data:image/png;base64,aGVsbG8=", mime: "image/png", name: "hello.png" }) const reference = new ReferenceAttachment({ name: "docs", kind: "local", uri: "file:///docs" }) const messages = toLLMMessages( @@ -34,6 +32,12 @@ describe("toLLMMessages", () => { model: { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") }, time: { created }, }), + new SessionMessage.System({ + id: id("system"), + type: "system", + text: "Updated context\n\nOther context", + time: { created }, + }), new SessionMessage.User({ id: id("user"), type: "user", @@ -69,8 +73,9 @@ describe("toLLMMessages", () => { model, ) - expect(messages.map((message) => message.role)).toEqual(["user", "user", "user", "user"]) - expect(messages[0]).toEqual( + expect(messages.map((message) => message.role)).toEqual(["system", "user", "user", "user", "user"]) + expect(messages[0]).toEqual(Message.system("Updated context\n\nOther context")) + expect(messages[1]).toEqual( Message.make({ id: id("user"), role: "user", @@ -81,34 +86,14 @@ describe("toLLMMessages", () => { metadata: { agents: [{ name: "build" }], references: [reference] }, }), ) - expect(messages.slice(1).map((message) => message.content)).toEqual([ + expect(messages.slice(2).map((message) => message.content)).toEqual([ [{ type: "text", text: "Synthetic context" }], [{ type: "text", text: "Shell command: pwd\n\n/project" }], [{ type: "text", text: "Summary of earlier conversation:\nEarlier work" }], ]) - }), - ) + }) - it.effect("maps durable Session system messages into chronological system messages", () => - Effect.sync(() => { - expect( - toLLMMessages( - [ - new SessionMessage.System({ - id: id("system"), - type: "system", - text: "Updated context\n\nOther context", - time: { created }, - }), - ], - model, - ), - ).toEqual([Message.system("Updated context\n\nOther context")]) - }), - ) - - it.effect("expands assistant tool calls and settled outcomes into canonical tool messages", () => - Effect.sync(() => { + test("expands assistant tool calls and settled outcomes into canonical tool messages", () => { const messages = toLLMMessages( [ new SessionMessage.Assistant({ @@ -263,11 +248,9 @@ describe("toLLMMessages", () => { }, }, ]) - }), - ) + }) - it.effect("restores OpenAI encrypted reasoning metadata", () => - Effect.sync(() => { + test("restores OpenAI encrypted reasoning metadata", () => { const messages = toLLMMessages( [ new SessionMessage.Assistant({ @@ -296,11 +279,9 @@ describe("toLLMMessages", () => { providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: "encrypted-state" } }, }, ]) - }), - ) + }) - it.effect("drops provider-native continuation metadata after a model switch", () => - Effect.sync(() => { + test("drops provider-native continuation metadata after a model switch", () => { const messages = toLLMMessages( [ new SessionMessage.Assistant({ @@ -398,6 +379,5 @@ describe("toLLMMessages", () => { providerMetadata: undefined, }, ]) - }), - ) + }) }) diff --git a/packages/core/test/session-runner-recorded.test.ts b/packages/core/test/session-runner-recorded.test.ts index 99410e6efd9..376b2e3452f 100644 --- a/packages/core/test/session-runner-recorded.test.ts +++ b/packages/core/test/session-runner-recorded.test.ts @@ -19,11 +19,10 @@ import { SessionRunnerModel } from "@opencode-ai/core/session/runner/model" import { ToolRegistry } from "@opencode-ai/core/tool/registry" import { SessionTable } from "@opencode-ai/core/session/sql" import { SessionStore } from "@opencode-ai/core/session/store" -import { SystemContext } from "@opencode-ai/core/system-context" import { SystemContextRegistry } from "@opencode-ai/core/system-context-registry" import { describe, expect } from "bun:test" import { eq } from "drizzle-orm" -import { Effect, Layer, Schema } from "effect" +import { Effect, Layer } from "effect" import path from "node:path" import { testEffect } from "./lib/effect" @@ -57,26 +56,7 @@ const model = OpenAIChat.route }) .model({ id: "gpt-4o-mini" }) const models = SessionRunnerModel.layerWith(() => Effect.succeed(model)) -const systemContextKey = SystemContext.Key.make("test/context") -const systemContext = Layer.effectDiscard( - SystemContextRegistry.Service.pipe( - Effect.flatMap((registry) => - registry.contribute({ - key: systemContextKey, - load: Effect.succeed( - SystemContext.make({ - key: systemContextKey, - codec: Schema.toCodecJson(Schema.String), - load: Effect.succeed("Recorded context"), - baseline: String, - update: (_previous, current) => current, - removed: () => "Recorded context removed", - }), - ), - }), - ), - ), -).pipe(Layer.provideMerge(SystemContextRegistry.layer)) +const systemContext = SystemContextRegistry.layer const runner = SessionRunnerLLM.defaultLayer.pipe( Layer.provide(database), Layer.provide(store), diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index 10a87130bef..1457daecca9 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -823,40 +823,6 @@ describe("SessionRunnerLLM", () => { }), ) - it.effect("replays retained context projections after multiple replacements", () => - Effect.gen(function* () { - yield* setup - const session = yield* SessionV2.Service - const events = yield* EventV2.Service - yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false }) - - requests.length = 0 - response = [] - yield* session.resume(sessionID) - yield* events.publish(SessionEvent.ModelSwitched, { - sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), - model: { id: ModelV2.ID.make("replacement-1"), providerID: ProviderV2.ID.make("fake") }, - }) - systemBaseline = "Replacement context 1" - yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false }) - yield* session.resume(sessionID) - yield* events.publish(SessionEvent.ModelSwitched, { - sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(2), - model: { id: ModelV2.ID.make("replacement-2"), providerID: ProviderV2.ID.make("fake") }, - }) - systemBaseline = "Replacement context 2" - yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Third" }), resume: false }) - yield* session.resume(sessionID) - - yield* replaySessionProjection(sessionID) - expect(requests.at(-1)?.system.map((part) => part.text)).toEqual(["Replacement context 2"]) - }), - ) - it.effect("replaces the baseline lazily after completed compaction without reopening replacement on replay", () => Effect.gen(function* () { yield* setup diff --git a/packages/llm/test/fixtures/recordings/anthropic-messages/anthropic-haiku-4-5-chronological-system-update.json b/packages/llm/test/fixtures/recordings/anthropic-messages/anthropic-haiku-4-5-chronological-system-update.json deleted file mode 100644 index 72e4d7b21b5..00000000000 --- a/packages/llm/test/fixtures/recordings/anthropic-messages/anthropic-haiku-4-5-chronological-system-update.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": 1, - "metadata": { - "name": "anthropic-messages/anthropic-haiku-4-5-chronological-system-update", - "recordedAt": "2026-06-04T19:14:47.473Z", - "provider": "anthropic", - "route": "anthropic-messages", - "transport": "http", - "model": "claude-haiku-4-5-20251001", - "tags": ["prefix:anthropic-messages", "provider:anthropic", "system", "chronological-system-update", "golden"] - }, - "interactions": [ - { - "transport": "http", - "request": { - "method": "POST", - "url": "https://api.anthropic.com/v1/messages", - "headers": { - "anthropic-version": "2023-06-01", - "content-type": "application/json" - }, - "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"Follow the latest instruction exactly. Reply only with the requested word.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Use the latest instruction for your reply.\"},{\"type\":\"text\",\"text\":\"\\nFor this reply, respond exactly with: UPDATED\\n\"}]}],\"stream\":true,\"max_tokens\":80,\"temperature\":0}" - }, - "response": { - "status": 200, - "headers": { - "content-type": "text/event-stream; charset=utf-8" - }, - "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01QxsUPda7mfZ9zGSgw4JD54\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":49,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":5,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"UPDATED\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":49,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":5} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" - } - } - ] -} diff --git a/packages/llm/test/fixtures/recordings/gemini/gemini-2-5-flash-chronological-system-update.json b/packages/llm/test/fixtures/recordings/gemini/gemini-2-5-flash-chronological-system-update.json deleted file mode 100644 index c4fd142fc77..00000000000 --- a/packages/llm/test/fixtures/recordings/gemini/gemini-2-5-flash-chronological-system-update.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": 1, - "metadata": { - "name": "gemini/gemini-2-5-flash-chronological-system-update", - "recordedAt": "2026-06-04T19:14:48.065Z", - "provider": "google", - "route": "gemini", - "transport": "http", - "model": "gemini-2.5-flash", - "tags": ["prefix:gemini", "provider:google", "system", "chronological-system-update", "golden"] - }, - "interactions": [ - { - "transport": "http", - "request": { - "method": "POST", - "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse", - "headers": { - "content-type": "application/json" - }, - "body": "{\"contents\":[{\"role\":\"user\",\"parts\":[{\"text\":\"Use the latest instruction for your reply.\"},{\"text\":\"\\nFor this reply, respond exactly with: UPDATED\\n\"}]}],\"systemInstruction\":{\"parts\":[{\"text\":\"Follow the latest instruction exactly. Reply only with the requested word.\"}]},\"generationConfig\":{\"maxOutputTokens\":80,\"temperature\":0,\"thinkingConfig\":{\"thinkingBudget\":0}}}" - }, - "response": { - "status": 200, - "headers": { - "content-type": "text/event-stream" - }, - "body": "data: {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \"UPDATED\"}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 44,\"candidatesTokenCount\": 2,\"totalTokenCount\": 46,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 44}],\"serviceTier\": \"standard\"},\"modelVersion\": \"gemini-2.5-flash\",\"responseId\": \"p84hasmqJL2I-sAPtZnUkA0\"}\r\n\r\n" - } - } - ] -} diff --git a/packages/llm/test/fixtures/recordings/openai-chat/openai-chat-gpt-4o-mini-chronological-system-update.json b/packages/llm/test/fixtures/recordings/openai-chat/openai-chat-gpt-4o-mini-chronological-system-update.json deleted file mode 100644 index 4c01f328d7f..00000000000 --- a/packages/llm/test/fixtures/recordings/openai-chat/openai-chat-gpt-4o-mini-chronological-system-update.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": 1, - "metadata": { - "name": "openai-chat/openai-chat-gpt-4o-mini-chronological-system-update", - "recordedAt": "2026-06-04T19:14:43.750Z", - "provider": "openai", - "route": "openai-chat", - "transport": "http", - "model": "gpt-4o-mini", - "tags": ["prefix:openai-chat", "provider:openai", "system", "chronological-system-update", "golden"] - }, - "interactions": [ - { - "transport": "http", - "request": { - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions", - "headers": { - "content-type": "application/json" - }, - "body": "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"system\",\"content\":\"Follow the latest instruction exactly. Reply only with the requested word.\"},{\"role\":\"user\",\"content\":\"Use the latest instruction for your reply.\\n\\nFor this reply, respond exactly with: UPDATED\\n\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"store\":false,\"max_tokens\":80,\"temperature\":0}" - }, - "response": { - "status": 200, - "headers": { - "content-type": "text/event-stream; charset=utf-8" - }, - "body": "data: {\"id\":\"chatcmpl-Dn7oZjOtsXXzajgbNQ17HQXOPABbd\",\"object\":\"chat.completion.chunk\",\"created\":1780600483,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_e0de90b008\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"uIKJFKJXo\"}\n\ndata: {\"id\":\"chatcmpl-Dn7oZjOtsXXzajgbNQ17HQXOPABbd\",\"object\":\"chat.completion.chunk\",\"created\":1780600483,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_e0de90b008\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"UPDATED\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"6HB3\"}\n\ndata: {\"id\":\"chatcmpl-Dn7oZjOtsXXzajgbNQ17HQXOPABbd\",\"object\":\"chat.completion.chunk\",\"created\":1780600483,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_e0de90b008\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"UlWDE\"}\n\ndata: {\"id\":\"chatcmpl-Dn7oZjOtsXXzajgbNQ17HQXOPABbd\",\"object\":\"chat.completion.chunk\",\"created\":1780600483,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_e0de90b008\",\"choices\":[],\"usage\":{\"prompt_tokens\":50,\"completion_tokens\":1,\"total_tokens\":51,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"w348ZVmuZE6\"}\n\ndata: [DONE]\n\n" - } - } - ] -} diff --git a/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-chronological-system-update.json b/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-chronological-system-update.json deleted file mode 100644 index f434c08f21c..00000000000 --- a/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-chronological-system-update.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": 1, - "metadata": { - "name": "openai-responses/openai-responses-gpt-5-5-chronological-system-update", - "recordedAt": "2026-06-04T19:14:46.794Z", - "provider": "openai", - "route": "openai-responses", - "transport": "http", - "model": "gpt-5.5", - "tags": [ - "prefix:openai-responses", - "provider:openai", - "flagship", - "system", - "chronological-system-update", - "golden" - ] - }, - "interactions": [ - { - "transport": "http", - "request": { - "method": "POST", - "url": "https://api.openai.com/v1/responses", - "headers": { - "content-type": "application/json" - }, - "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"system\",\"content\":\"Follow the latest instruction exactly. Reply only with the requested word.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Use the latest instruction for your reply.\"},{\"type\":\"input_text\",\"text\":\"\\nFor this reply, respond exactly with: UPDATED\\n\"}]}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":80,\"stream\":true}" - }, - "response": { - "status": 200, - "headers": { - "content-type": "text/event-stream; charset=utf-8" - }, - "body": "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b058cc7f4ec9168016a21cea3d244819da4789560e119a7ac\",\"object\":\"response\",\"created_at\":1780600483,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":80,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b058cc7f4ec9168016a21cea3d244819da4789560e119a7ac\",\"object\":\"response\",\"created_at\":1780600483,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":80,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0b058cc7f4ec9168016a21cea4a180819dab9efa31556dff80\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqIc6kqld4jlRSpxnmJIcQxeZVqwQ9w073E8j7To6pXvbKYQTT2P_KD86yGR20IrGeredD67SvjdCe32pPRqNcDgit4XCJuSKWzvQt8EZOrs5ZbY7EjnRWOWv7WGLRFyzIJG2oQVbEwrJZzV-msWBXOj45GobymmHAJT1yj76ks33WcKtOSyGXWfmzIx1tfeVFULriBFnnWJ45iYFJS2VdwvkHyLci6uitNfwHWOALkA00aavmw9oCac8LSDrKz7aIOYMHekFI4IosCjJ0GgTrPvGm_D0EIzeAcNn_ofL31u5i1r_v--RNC9dog0Xc1DxoXeXDoLUj1YZ3JxWjFhhyAlraZ-t5S4kHsAqIo_21usyZf2u59DSdIvA1JnzDYQO24-LhsnMsHjqVvh0tYh9EZ75U7-7arr8KOupXzrFDY4SrNFmdTdyT4AdDF8CamDfHszti2P5a0q0E_0lBCehiMPBHlg8yKvVN8oSkf3svUIyJfaQ80g_K0tOgwkOW54rMbBalKNRKOCQnP7_eN16XldJbhcPY1K00XUBjfWOxLR-mXv1-20CINZKsy6a_lp-yrR7fcGaYykY7v75-svaj-6nEpVTelFiepcnHySP3vBzEzlP9n7eQf6CEcb8IUNh8sYQSnnvpEba9kWrTz7laA-EseH1tIN_GHxd1aTPyg_hW1mjN3IajA38Lvsd7RT1rcE644zSbz2-mqh5NTkQ9SGkvW3yhTLJWageRvT_qSqPVnEv18qtO0KgSOLgmsij43kfPvrcXFmnSnwtuf6kebY_T88il919MIRSuLEzI4vocXiL3sJ34pp4HPb1nfdu6N8IP_m28ti47LOQi5FZd9oFm8aYJgUaijlu5O7loDcI5Mz1uNsSqAd0V6Vs-mPFPLj00WqF4wFaYsp4qjmr9uNVaug==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0b058cc7f4ec9168016a21cea4a180819dab9efa31556dff80\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqIc6lpWyK-ZtXIPgHUsad6JFwBf2WpDu7lyu-ISjz0dAs-NPdkHl21o6eo84i9i-kuwPidonWRG35JNnedZ-4k17N5iJC81DwW8Gpb83rQ6VN1YqNQeWEY0Y56euNz4A13EzP84g-_X-6NDxS9WvQWTBwNrdkDKsRGn62CGxaz6EiO8Gkeb9ZGUxLc0TdLtM4fBK3Kpy1mvFOyp1kv4CSw1VE3g3iN2p8CskF1BaduprfT9WDPRfO4CHJs8_YlEFDN_THnIy-2vXsCJlqoMZ7xg0i1syW0osiISNXWimhRRg5wFME50ZQV0F6WjETCFcMsFrkEHAdjxGhhrWroPT_79niuE1iA74iiwUpeF1Bp5dTYBbwijJrDnhFG5CpOYtgUb5-5lPdwmUK0nbYDBL6dQC-PntXU6vi0u3pVRcil0dOqkzhzy56BtBM4tJagYnhuAaAkW86nbq31qq1eH8XmUSKItZ3Um2eOlGHwRdV_0iZVYYvqquoByy4Wptznw0y30XvMSeSDp2deLqzU4dCLYgxcYczlREEd1pVCah-HjpSipHNcUUMshP8h0wKBcy3C5ciBzT-cmgTIgCEEWkAatSx3ADDM9MOnk6fjogmkj-FETMIebgI02zN4Qh1GXJZahseuhouM8O5ei-17XqHWwlRvEZfolUfHSiBnHGH1Hk2mdnqLm3WfAlULQ1d0SEYrZlztdMuQW-4Fw619fixbatUvp_p0VvlOPGEKXmgKMy_ZOkJSVIS0O0KphVRCebnVWRsg5oGGezVormxKaZj5yKYtgSaODXM_q0GTgo5OxaaIz7RHX_c3e8URJ5auUwZGXh3mzNgsB7VDo9wkawCeHG0BVJ1akwF7J1uNcwfMD1tFNrO5zQwF1NfiwFo-uVots8AHK5pZqrFpY64LULoiNVmqD1EZpB_sUCwpQdVSvgox5ZvW4xzcdNEDsuYklCB25xruiIo9qufGrrSjSK8DNxB2w==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"UPDATED\",\"item_id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"logprobs\":[],\"obfuscation\":\"mgR1eK6bQ\",\"output_index\":1,\"sequence_number\":6}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":7,\"text\":\"UPDATED\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"UPDATED\"},\"sequence_number\":8}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"UPDATED\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":9}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b058cc7f4ec9168016a21cea3d244819da4789560e119a7ac\",\"object\":\"response\",\"created_at\":1780600483,\"status\":\"completed\",\"background\":false,\"completed_at\":1780600486,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":80,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"rs_0b058cc7f4ec9168016a21cea4a180819dab9efa31556dff80\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqIc6m-feiwQGp0dCrntYnYC-tRtrFG_e34B0XI6-cuPjU9c7PhWAjN7pyE8sIXfe8t1Nxi8aqXwfKbMgFX-lycUFo6Fyv209ZOpWwUf4gL5KIvCnHvISI13qmgiIzti2HHBO3HYSHNdFvrj8B3AxU1KEMjn7Pnv1-c23i0H9mYWp6TTO9K1vDCWkGZYpGe9OP0aFBZmxkdB5fWWwi2X6naLOpvHn2ssTesecwQ2lRyrdY_EV1rtL8YL0zG78azgQdJPNRL8gv_BQ1HLHS1xUDgvYnVgQb8U8Tu-42scVWP6qBXJk3-01d6sjUj8RYVgrsjLIsHZq1QlnBTSNnynGLoALtt7cC4g3LhbTCjSTP5kG-KxDQ8RiBd34CP8voN8oaM_2Ycs0t7mCtBZdmWjw92iViVlW7wVV5PNU8fPz0BqGQqKWnb6xawvEqPxhxiiNuaby26mGdMOpilDNJVyQV06NVAMtNW7givFKxT-cRuvWxecfFgGdSfbK3V03k1NlN5Pard8ZqZgqZSpOEk_MpV59outeqvKozOl1jX6E8SXPtPW4nNfHz_vUb57hKGWx6meJj2kZlQ1xpb3Tb4MEMLltKj_OPbfyv-GfXyhm_enzRCu0iepQAhU1fFHfns8z0tT4xndX_5mEMgiDkXDBg0bfXpBA8uOuiJIlV5s1wXi85zJQ6hpnqG6fKMNI2iGgQihIm4OiiG73eyePSVto8nAiq_Q4WnDMX_k1w6V1D5IIeTIc8MLZjDL5kw3pm6GnnoxlNoYoBnmdupW_ckMKMzNDg0dGLQ17TsaEiGr1dfAxStBiP1hIx_fFYebksXtFr-OVxCN_Z09hkEQ6BOvrDtE02VPI_7vjz9EroixIKYdJDWwmvXwiXYGLSzL4uyN1Z4aLS5MrfWeG3AQNSMjOBBQ_SsPUFFqskhMiy1keO5-htbgV1gbOl9BlnNO1VaACCAKdbweOdosCUiRojQ3KKhhlVhg==\",\"summary\":[]},{\"id\":\"msg_0b058cc7f4ec9168016a21cea52760819d927c7a1d6538a62a\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"UPDATED\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":48,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":17,\"output_tokens_details\":{\"reasoning_tokens\":10},\"total_tokens\":65},\"user\":null,\"metadata\":{}},\"sequence_number\":10}\n\n" - } - } - ] -} diff --git a/packages/llm/test/provider/golden.recorded.test.ts b/packages/llm/test/provider/golden.recorded.test.ts index f82df3b5eb9..9b95f678fd2 100644 --- a/packages/llm/test/provider/golden.recorded.test.ts +++ b/packages/llm/test/provider/golden.recorded.test.ts @@ -73,7 +73,7 @@ describeRecordedGoldenScenarios([ prefix: "openai-chat", model: openAIChat, requires: ["OPENAI_API_KEY"], - scenarios: ["text", "chronological-system-update", "tool-call", "tool-loop"], + scenarios: ["text", "tool-call", "tool-loop"], }, { name: "OpenAI Responses gpt-5.5", @@ -83,7 +83,6 @@ describeRecordedGoldenScenarios([ tags: ["flagship"], scenarios: [ { id: "text", temperature: false }, - { id: "chronological-system-update", temperature: false }, { id: "reasoning", temperature: false }, { id: "reasoning-continuation", temperature: false }, { id: "tool-call", temperature: false }, @@ -105,7 +104,7 @@ describeRecordedGoldenScenarios([ model: anthropicHaiku, requires: ["ANTHROPIC_API_KEY"], options: { redactor: Redactor.defaults({ requestHeaders: { allow: ["content-type", "anthropic-version"] } }) }, - scenarios: ["text", "chronological-system-update", "tool-call"], + scenarios: ["text", "tool-call"], }, { name: "Anthropic Opus 4.7", @@ -126,7 +125,6 @@ describeRecordedGoldenScenarios([ requires: ["GOOGLE_GENERATIVE_AI_API_KEY"], scenarios: [ { id: "text", maxTokens: 80 }, - "chronological-system-update", "tool-call", { id: "image", maxTokens: 160 }, ], diff --git a/packages/llm/test/recorded-scenarios.ts b/packages/llm/test/recorded-scenarios.ts index 1aa9998bfc5..545a3b983ab 100644 --- a/packages/llm/test/recorded-scenarios.ts +++ b/packages/llm/test/recorded-scenarios.ts @@ -332,28 +332,6 @@ const runTextScenario = (context: GoldenScenarioContext) => }), ]) -export const runChronologicalSystemUpdateScenario = (context: GoldenScenarioContext) => - Effect.gen(function* () { - const response = yield* generate( - LLM.request({ - id: `${context.id}_chronological_system_update`, - model: context.model, - system: "Follow the latest instruction exactly. Reply only with the requested word.", - cache: "none", - messages: [ - Message.user("Use the latest instruction for your reply."), - Message.system("For this reply, respond exactly with: UPDATED"), - ], - providerOptions: - context.model.route.id === "gemini" ? { gemini: { thinkingConfig: { thinkingBudget: 0 } } } : undefined, - generation: generation(context, context.maxTokens ?? 80), - }), - ) - - expectFinish(response.events, "stop") - expect(response.text.trim()).toMatch(/^UPDATED[.!]?$/i) - }) - const runToolCallScenario = (context: GoldenScenarioContext) => runGeneratedConversation(context, [ user("Call get_weather with city exactly Paris."), @@ -467,11 +445,6 @@ const runToolLoopScenario = (context: GoldenScenarioContext) => const goldenScenarios = { text: { title: "streams text", tags: ["text", "golden"], run: runTextScenario }, - "chronological-system-update": { - title: "uses chronological system update", - tags: ["system", "chronological-system-update", "golden"], - run: runChronologicalSystemUpdateScenario, - }, "tool-call": { title: "streams tool call", tags: ["tool", "tool-call", "golden"], run: runToolCallScenario }, "tool-loop": { title: "drives a tool loop", tags: ["tool", "tool-loop", "golden"], run: runToolLoopScenario }, image: { title: "reads image text", tags: ["media", "image", "vision", "golden"], run: runImageScenario }, diff --git a/packages/llm/test/schema.test.ts b/packages/llm/test/schema.test.ts index 949d0d06815..84ceb0e5d89 100644 --- a/packages/llm/test/schema.test.ts +++ b/packages/llm/test/schema.test.ts @@ -6,7 +6,6 @@ import { ContentPart, LLMEvent, LLMRequest, - Message, Model, ModelID, ProviderID, @@ -52,17 +51,6 @@ describe("llm schema", () => { expect(decoded.model.route.id).toBe("openai-responses") }) - test("decodes chronological system messages", () => { - const decoded = decodeLLMRequest({ - model, - system: [], - messages: [{ role: "system", content: [{ type: "text", text: "Operator update." }] }], - tools: [], - }) - - expect(decoded.messages[0]).toMatchObject({ role: "system", content: [{ type: "text", text: "Operator update." }] }) - }) - test("rejects invalid event type", () => { expect(() => decodeLLMEvent({ type: "bogus" })).toThrow() }) diff --git a/packages/opencode/test/cli/tui/sync-v2.test.tsx b/packages/opencode/test/cli/tui/sync-v2.test.tsx index a408e38e74a..b37d86a7037 100644 --- a/packages/opencode/test/cli/tui/sync-v2.test.tsx +++ b/packages/opencode/test/cli/tui/sync-v2.test.tsx @@ -358,54 +358,6 @@ test("sync v2 preserves live events while snapshot hydration is in flight", asyn } }) -test("sync v2 deduplicates a buffered context update already present in the hydrated snapshot", async () => { - const events = createEventSource() - const response = Promise.withResolvers() - const calls = createFetch((url) => { - if (url.pathname === "/api/session/session-1/message") return response.promise - return undefined - }) - let sync!: ReturnType - let ready!: () => void - const mounted = new Promise((resolve) => { - ready = resolve - }) - - function Probe() { - sync = useSyncV2() - onMount(ready) - return - } - - const app = await testRender(() => ( - - - - - - - - )) - - try { - await mounted - const hydration = sync.session.message.sync("session-1") - emitTwice(events, { - id: "evt_context_1", - type: "session.next.context.updated", - properties: { sessionID: "session-1", messageID: "msg_context_1", timestamp: 1, text: "Updated context" }, - }) - response.resolve( - json({ data: [{ id: "msg_context_1", type: "system", text: "Updated context", time: { created: 1 } }] }), - ) - await hydration - - expect(sync.session.message.fromSession("session-1")).toHaveLength(1) - } finally { - app.renderer.destroy() - } -}) - test("sync v2 replaces stale cached rows while preserving in-flight live rows", async () => { const events = createEventSource() const response = Promise.withResolvers()