From 940ad53971c6cc60b88180d629c5637934b0738a Mon Sep 17 00:00:00 2001 From: tris203 Date: Sun, 5 Jul 2026 21:00:57 +0100 Subject: [PATCH 1/3] Delegate OpenCode session titles to provider - Stop sending a generated title when creating OpenCode sessions - Emit thread metadata updates from OpenCode session title events - Cover the title sync flow in adapter tests --- .../provider/Layers/OpenCodeAdapter.test.ts | 47 ++++++++++++++++++- .../src/provider/Layers/OpenCodeAdapter.ts | 47 +++++++++++++++++-- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts index d0475e25284..73c6ea54917 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts @@ -54,6 +54,7 @@ const runtimeMock = { state: { startCalls: [] as string[], sessionCreateUrls: [] as string[], + sessionCreateInputs: [] as Array>, authHeaders: [] as Array, abortCalls: [] as string[], closeCalls: [] as string[], @@ -67,6 +68,7 @@ const runtimeMock = { reset() { this.state.startCalls.length = 0; this.state.sessionCreateUrls.length = 0; + this.state.sessionCreateInputs.length = 0; this.state.authHeaders.length = 0; this.state.abortCalls.length = 0; this.state.closeCalls.length = 0; @@ -122,8 +124,9 @@ const OpenCodeRuntimeTestDouble: OpenCodeRuntimeShape = { createOpenCodeSdkClient: ({ baseUrl, serverPassword }) => ({ session: { - create: async () => { + create: async (input: Record) => { runtimeMock.state.sessionCreateUrls.push(baseUrl); + runtimeMock.state.sessionCreateInputs.push(input); runtimeMock.state.authHeaders.push( serverPassword ? `Basic ${btoa(`opencode:${serverPassword}`)}` : null, ); @@ -733,6 +736,48 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { }), ); + it.effect("lets OpenCode own session title generation and emits title metadata updates", () => + Effect.gen(function* () { + const adapter = yield* OpenCodeAdapter; + const threadId = asThreadId("thread-opencode-title-sync"); + runtimeMock.state.subscribedEvents = [ + { + type: "session.updated", + properties: { + sessionID: "http://127.0.0.1:9999/session", + info: { + id: "http://127.0.0.1:9999/session", + title: "Investigate OpenCode title sync", + }, + }, + }, + ]; + + const eventsFiber = yield* adapter.streamEvents.pipe( + Stream.filter((event) => event.threadId === threadId), + Stream.take(3), + Stream.runCollect, + Effect.forkChild, + ); + + yield* adapter.startSession({ + provider: ProviderDriverKind.make("opencode"), + threadId, + runtimeMode: "full-access", + }); + + const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second"))); + NodeAssert.equal(runtimeMock.state.sessionCreateInputs.length, 1); + NodeAssert.equal("title" in (runtimeMock.state.sessionCreateInputs[0] ?? {}), false); + + const metadataUpdated = events.find((event) => event.type === "thread.metadata.updated"); + NodeAssert.ok(metadataUpdated); + if (metadataUpdated.type === "thread.metadata.updated") { + NodeAssert.equal(metadataUpdated.payload.name, "Investigate OpenCode title sync"); + } + }), + ); + it.effect("writes provider-native observability records using the session thread id", () => Effect.gen(function* () { const nativeEvents: Array<{ diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.ts index 1eb6e47bc19..fb62dd0c586 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.ts @@ -65,6 +65,29 @@ type OpenCodeSubscribedEvent = ? TEvent : never; +function trimText(value: string | undefined | null): string | undefined { + const trimmed = value?.trim(); + return trimmed && trimmed.length > 0 ? trimmed : undefined; +} + +function openCodeEventSessionId(event: OpenCodeSubscribedEvent): string | undefined { + const properties = "properties" in event ? event.properties : undefined; + if (!properties || typeof properties !== "object") { + return undefined; + } + + const sessionID = (properties as { readonly sessionID?: unknown }).sessionID; + return typeof sessionID === "string" ? sessionID : undefined; +} + +function openCodeEventSessionTitle(event: OpenCodeSubscribedEvent): string | undefined { + if (event.type !== "session.updated") { + return undefined; + } + + return trimText(event.properties.info.title); +} + interface OpenCodeSessionContext { session: ProviderSession; readonly client: OpencodeClient; @@ -646,8 +669,7 @@ export function makeOpenCodeAdapter( context: OpenCodeSessionContext, event: OpenCodeSubscribedEvent, ) { - const payloadSessionId = - "properties" in event ? (event.properties as { sessionID?: unknown }).sessionID : undefined; + const payloadSessionId = openCodeEventSessionId(event); if (payloadSessionId !== context.openCodeSessionId) { return; } @@ -666,6 +688,26 @@ export function makeOpenCodeAdapter( }); switch (event.type) { + case "session.updated": { + const title = openCodeEventSessionTitle(event); + if (title) { + yield* emit({ + ...(yield* buildEventBase({ + threadId: context.session.threadId, + raw: event, + })), + type: "thread.metadata.updated", + payload: { + name: title, + metadata: { + sessionID: context.openCodeSessionId, + }, + }, + }); + } + break; + } + case "message.updated": { context.messageRoleById.set(event.properties.info.id, event.properties.info.role); if (event.properties.info.role === "assistant") { @@ -1072,7 +1114,6 @@ export function makeOpenCodeAdapter( } const openCodeSession = yield* runOpenCodeSdk("session.create", () => client.session.create({ - title: `T3 Code ${input.threadId}`, permission: buildOpenCodePermissionRules(input.runtimeMode), }), ); From 3ee7084826d7be131bfbe5b21a95ac6c92a0f0e1 Mon Sep 17 00:00:00 2001 From: tris203 Date: Sun, 5 Jul 2026 21:32:23 +0100 Subject: [PATCH 2/3] fixup --- apps/server/src/provider/Layers/OpenCodeAdapter.test.ts | 1 - apps/server/src/provider/Layers/OpenCodeAdapter.ts | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts index 73c6ea54917..6b245701d70 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts @@ -744,7 +744,6 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { { type: "session.updated", properties: { - sessionID: "http://127.0.0.1:9999/session", info: { id: "http://127.0.0.1:9999/session", title: "Investigate OpenCode title sync", diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.ts index fb62dd0c586..2e0ffbe0ba6 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.ts @@ -76,6 +76,11 @@ function openCodeEventSessionId(event: OpenCodeSubscribedEvent): string | undefi return undefined; } + const info = (properties as { readonly info?: { readonly id?: unknown } }).info; + const sessionIDFromInfo = info && typeof info.id === "string" ? info.id : undefined; + if (sessionIDFromInfo) { + return sessionIDFromInfo; + } const sessionID = (properties as { readonly sessionID?: unknown }).sessionID; return typeof sessionID === "string" ? sessionID : undefined; } From 4bee25b2ece9eeb03a832fbdb7aeafb9ce031f3d Mon Sep 17 00:00:00 2001 From: tris203 Date: Sun, 5 Jul 2026 21:52:53 +0100 Subject: [PATCH 3/3] fixup2 --- apps/server/src/provider/Layers/OpenCodeAdapter.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.ts index 2e0ffbe0ba6..bdbf7524d00 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.ts @@ -76,13 +76,14 @@ function openCodeEventSessionId(event: OpenCodeSubscribedEvent): string | undefi return undefined; } - const info = (properties as { readonly info?: { readonly id?: unknown } }).info; - const sessionIDFromInfo = info && typeof info.id === "string" ? info.id : undefined; - if (sessionIDFromInfo) { - return sessionIDFromInfo; - } const sessionID = (properties as { readonly sessionID?: unknown }).sessionID; - return typeof sessionID === "string" ? sessionID : undefined; + const sessionIDFromProperties = typeof sessionID === "string" ? sessionID : undefined; + if (sessionIDFromProperties) { + return sessionIDFromProperties; + } + + const info = (properties as { readonly info?: { readonly id?: unknown } }).info; + return info && typeof info.id === "string" ? info.id : undefined; } function openCodeEventSessionTitle(event: OpenCodeSubscribedEvent): string | undefined {