From 0bd61d2826e6827973f4806019d4fdedf44fc691 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 3 Jun 2026 17:47:38 -0400 Subject: [PATCH] feat(core): include resolved location info --- packages/core/src/event.ts | 12 ++++--- packages/core/src/location.ts | 21 +++++++---- packages/core/test/catalog.test.ts | 8 ++++- packages/core/test/event.test.ts | 7 +++- packages/opencode/src/event-v2-bridge.ts | 7 ++-- .../instance/httpapi/groups/v2/command.ts | 3 +- .../routes/instance/httpapi/groups/v2/fs.ts | 5 +-- .../instance/httpapi/groups/v2/location.ts | 30 ++++++++-------- .../instance/httpapi/groups/v2/model.ts | 3 +- .../instance/httpapi/groups/v2/permission.ts | 3 +- .../instance/httpapi/groups/v2/provider.ts | 5 +-- .../instance/httpapi/groups/v2/skill.ts | 3 +- .../instance/httpapi/handlers/v2/command.ts | 3 +- .../routes/instance/httpapi/handlers/v2/fs.ts | 5 +-- .../instance/httpapi/handlers/v2/model.ts | 3 +- .../httpapi/handlers/v2/permission.ts | 3 +- .../instance/httpapi/handlers/v2/provider.ts | 5 +-- .../instance/httpapi/handlers/v2/skill.ts | 3 +- packages/opencode/src/session/session.ts | 36 ++----------------- .../test/server/httpapi-v2-location.test.ts | 9 ++--- 20 files changed, 92 insertions(+), 82 deletions(-) diff --git a/packages/core/src/event.ts b/packages/core/src/event.ts index 0be8c64ef6..b9cba1ede4 100644 --- a/packages/core/src/event.ts +++ b/packages/core/src/event.ts @@ -30,7 +30,7 @@ export type Payload = { readonly type: D["type"] readonly data: Data readonly version?: number - readonly location?: Location.Ref + readonly location?: Location.Info readonly metadata?: Record } @@ -77,7 +77,7 @@ export function define - readonly location?: Location.Ref + readonly location?: Location.Info } export interface Interface { @@ -264,7 +264,11 @@ export const layer = Layer.effect( const location = options?.location ?? (serviceLocation - ? { directory: serviceLocation.directory, workspaceID: serviceLocation.workspaceID } + ? new Location.Info({ + directory: serviceLocation.directory, + workspaceID: serviceLocation.workspaceID, + project: serviceLocation.project, + }) : undefined) return yield* publishEvent({ id: options?.id ?? ID.create(), diff --git a/packages/core/src/location.ts b/packages/core/src/location.ts index 9613885c97..d8388452be 100644 --- a/packages/core/src/location.ts +++ b/packages/core/src/location.ts @@ -10,16 +10,23 @@ export const Ref = Schema.Struct({ }).annotate({ identifier: "Location.Ref" }) export type Ref = typeof Ref.Type -export interface Interface { - readonly directory: AbsolutePath - readonly workspaceID?: string - readonly project: { - readonly id: Project.ID - readonly directory: AbsolutePath - } +export class Info extends Schema.Class("Location.Info")({ + directory: AbsolutePath, + workspaceID: Schema.String.pipe(Schema.optional), + project: Schema.Struct({ + id: Project.ID, + directory: AbsolutePath, + }), +}) {} + +export interface Interface extends Info { readonly vcs?: Project.Vcs } +export function response(data: S) { + return Schema.Struct({ location: Info, data }) +} + export class Service extends Context.Service()("@opencode/Location") {} export const layer = (ref: Ref) => diff --git a/packages/core/test/catalog.test.ts b/packages/core/test/catalog.test.ts index 2f247ed0f8..14811d67ce 100644 --- a/packages/core/test/catalog.test.ts +++ b/packages/core/test/catalog.test.ts @@ -6,6 +6,7 @@ import { Location } from "@opencode-ai/core/location" import { ModelV2 } from "@opencode-ai/core/model" import { PluginV2 } from "@opencode-ai/core/plugin" import { Policy } from "@opencode-ai/core/policy" +import { Project } from "@opencode-ai/core/project" import { ProviderV2 } from "@opencode-ai/core/provider" import { AbsolutePath } from "@opencode-ai/core/schema" import { location } from "./fixture/location" @@ -187,7 +188,12 @@ describe("CatalogV2", () => { yield* events.publish( PluginV2.Event.Added, { id: PluginV2.ID.make("test-transform") }, - { location: { directory: AbsolutePath.make("other") } }, + { + location: new Location.Info({ + directory: AbsolutePath.make("other"), + project: { id: Project.ID.global, directory: AbsolutePath.make("other") }, + }), + }, ) yield* Effect.yieldNow diff --git a/packages/core/test/event.test.ts b/packages/core/test/event.test.ts index c3e5d2d75a..017ce95443 100644 --- a/packages/core/test/event.test.ts +++ b/packages/core/test/event.test.ts @@ -5,6 +5,7 @@ import { Database } from "@opencode-ai/core/database/database" import { EventSequenceTable, EventTable } from "@opencode-ai/core/event/sql" import { Location } from "@opencode-ai/core/location" import { AbsolutePath } from "@opencode-ai/core/schema" +import { Project } from "@opencode-ai/core/project" import { eq } from "drizzle-orm" import { location } from "./fixture/location" import { testEffect } from "./lib/effect" @@ -80,7 +81,11 @@ describe("EventV2", () => { expect(event.type).toBe("test.message") expect(event).not.toHaveProperty("version") expect(event.data).toEqual({ text: "hello" }) - expect(event.location).toEqual({ directory: AbsolutePath.make("project"), workspaceID: "workspace" }) + expect(event.location).toEqual({ + directory: AbsolutePath.make("project"), + workspaceID: "workspace", + project: { id: Project.ID.global, directory: AbsolutePath.make("project") }, + }) }), ) diff --git a/packages/opencode/src/event-v2-bridge.ts b/packages/opencode/src/event-v2-bridge.ts index 673bf1f15b..e7532a965a 100644 --- a/packages/opencode/src/event-v2-bridge.ts +++ b/packages/opencode/src/event-v2-bridge.ts @@ -3,6 +3,8 @@ import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref" import { GlobalBus } from "@/bus/global" import { EventV2 } from "@opencode-ai/core/event" +import { Location } from "@opencode-ai/core/location" +import { Project } from "@opencode-ai/core/project" import { AbsolutePath } from "@opencode-ai/core/schema" import "@opencode-ai/core/account" import "@opencode-ai/core/catalog" @@ -24,10 +26,11 @@ export const layer = Layer.effect( const workspaceID = yield* WorkspaceRef return yield* events.publish(definition, data, { ...options, - location: { + location: new Location.Info({ directory: AbsolutePath.make(ctx.directory), ...(workspaceID ? { workspaceID } : {}), - }, + project: { id: Project.ID.make(ctx.project.id), directory: AbsolutePath.make(ctx.worktree) }, + }), }) }) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/command.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/command.ts index b5edb2d4cf..98d84e1564 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/command.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/command.ts @@ -1,4 +1,5 @@ import { CommandV2 } from "@opencode-ai/core/command" +import { Location } from "@opencode-ai/core/location" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { V2Authorization } from "../../middleware/authorization" @@ -8,7 +9,7 @@ export const CommandGroup = HttpApiGroup.make("v2.command") .add( HttpApiEndpoint.get("commands", "/api/command", { query: LocationQuery, - success: Schema.Array(CommandV2.Info), + success: Location.response(Schema.Array(CommandV2.Info)), }) .annotateMerge(locationQueryOpenApi) .annotateMerge( diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/fs.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/fs.ts index b50d2466d4..81ea932f8e 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/fs.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/fs.ts @@ -1,4 +1,5 @@ import { FileSystem } from "@opencode-ai/core/filesystem" +import { Location } from "@opencode-ai/core/location" import { RelativePath } from "@opencode-ai/core/schema" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" @@ -21,7 +22,7 @@ export const FileSystemGroup = HttpApiGroup.make("v2.fs") .add( HttpApiEndpoint.get("read", "/api/fs/read", { query: ReadQuery, - success: FileSystem.Content, + success: Location.response(FileSystem.Content), }) .annotateMerge(locationQueryOpenApi) .annotateMerge( @@ -35,7 +36,7 @@ export const FileSystemGroup = HttpApiGroup.make("v2.fs") .add( HttpApiEndpoint.get("list", "/api/fs/list", { query: ListQuery, - success: Schema.Array(FileSystem.Entry), + success: Location.response(Schema.Array(FileSystem.Entry)), }) .annotateMerge(locationQueryOpenApi) .annotateMerge( diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/location.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/location.ts index c8389cd3f8..34967d6087 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/location.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/location.ts @@ -9,7 +9,7 @@ import { SkillV2 } from "@opencode-ai/core/skill" import { AbsolutePath } from "@opencode-ai/core/schema" import { PluginBoot } from "@opencode-ai/core/plugin/boot" import { Effect, Layer, Schema } from "effect" -import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http" +import { HttpServerRequest } from "effect/unstable/http" import { HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi" export const LocationQuery = Schema.Struct({ @@ -36,6 +36,20 @@ export const locationQueryOpenApi = OpenApi.annotations({ }, }) +export function response(data: Effect.Effect) { + return Effect.gen(function* () { + const location = yield* Location.Service + return { + location: new Location.Info({ + directory: location.directory, + workspaceID: location.workspaceID, + project: location.project, + }), + data: yield* data, + } + }) +} + export class V2LocationMiddleware extends HttpApiMiddleware.Service< V2LocationMiddleware, { @@ -67,19 +81,7 @@ export const layer = Layer.effect( return V2LocationMiddleware.of((effect) => Effect.gen(function* () { const request = yield* HttpServerRequest.HttpServerRequest - return yield* Effect.gen(function* () { - const location = yield* Location.Service - yield* HttpEffect.appendPreResponseHandler((_request, response) => - Effect.succeed( - HttpServerResponse.setHeaders(response, { - "x-opencode-directory": location.directory, - "x-opencode-project-id": location.project.id, - ...(location.workspaceID ? { "x-opencode-workspace": location.workspaceID } : {}), - }), - ), - ) - return yield* effect - }).pipe(Effect.provide(locations.get(ref(request)))) + return yield* effect.pipe(Effect.provide(locations.get(ref(request)))) }), ) }), diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/model.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/model.ts index 2f52ff23d4..bc210f1c61 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/model.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/model.ts @@ -1,4 +1,5 @@ import { ModelV2 } from "@opencode-ai/core/model" +import { Location } from "@opencode-ai/core/location" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { ServiceUnavailableError } from "../../errors" @@ -9,7 +10,7 @@ export const ModelGroup = HttpApiGroup.make("v2.model") .add( HttpApiEndpoint.get("models", "/api/model", { query: LocationQuery, - success: Schema.Array(ModelV2.Info), + success: Location.response(Schema.Array(ModelV2.Info)), error: ServiceUnavailableError, }) .annotateMerge(locationQueryOpenApi) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/permission.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/permission.ts index c1f78089a2..7a143d6828 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/permission.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/permission.ts @@ -1,4 +1,5 @@ import { PermissionV2 } from "@opencode-ai/core/permission" +import { Location } from "@opencode-ai/core/location" import { PermissionSaved } from "@opencode-ai/core/permission/saved" import { ProjectV2 } from "@opencode-ai/core/project" import { SessionV2 } from "@opencode-ai/core/session" @@ -12,7 +13,7 @@ export const PermissionGroup = HttpApiGroup.make("v2.permission") .add( HttpApiEndpoint.get("permissionRequests", "/api/permission/request", { query: LocationQuery, - success: Schema.Array(PermissionV2.Request), + success: Location.response(Schema.Array(PermissionV2.Request)), }) .annotateMerge(locationQueryOpenApi) .annotateMerge( diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/provider.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/provider.ts index 2038ddfedd..6498af016f 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/provider.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/provider.ts @@ -1,4 +1,5 @@ import { ProviderV2 } from "@opencode-ai/core/provider" +import { Location } from "@opencode-ai/core/location" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors" @@ -9,7 +10,7 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider") .add( HttpApiEndpoint.get("providers", "/api/provider", { query: LocationQuery, - success: Schema.Array(ProviderV2.Info), + success: Location.response(Schema.Array(ProviderV2.Info)), error: ServiceUnavailableError, }) .annotateMerge(locationQueryOpenApi) @@ -25,7 +26,7 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider") HttpApiEndpoint.get("provider", "/api/provider/:providerID", { params: { providerID: ProviderV2.ID }, query: LocationQuery, - success: ProviderV2.Info, + success: Location.response(ProviderV2.Info), error: [ProviderNotFoundError, ServiceUnavailableError], }) .annotateMerge(locationQueryOpenApi) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/skill.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/skill.ts index b9f036a861..0163c171cf 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/skill.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/skill.ts @@ -1,4 +1,5 @@ import { SkillV2 } from "@opencode-ai/core/skill" +import { Location } from "@opencode-ai/core/location" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { V2Authorization } from "../../middleware/authorization" @@ -8,7 +9,7 @@ export const SkillGroup = HttpApiGroup.make("v2.skill") .add( HttpApiEndpoint.get("skills", "/api/skill", { query: LocationQuery, - success: Schema.Array(SkillV2.Info), + success: Location.response(Schema.Array(SkillV2.Info)), }) .annotateMerge(locationQueryOpenApi) .annotateMerge( diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/command.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/command.ts index 9b251e6584..d9448e0a05 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/command.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/command.ts @@ -2,7 +2,8 @@ import { CommandV2 } from "@opencode-ai/core/command" import { Effect } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" +import { response } from "../../groups/v2/location" export const commandHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.command", (handlers) => - handlers.handle("commands", () => CommandV2.Service.use((command) => command.list())), + handlers.handle("commands", () => response(CommandV2.Service.use((command) => command.list()))), ) diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/fs.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/fs.ts index 67fd4d8c08..b407b21fd8 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/fs.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/fs.ts @@ -2,11 +2,12 @@ import { FileSystem } from "@opencode-ai/core/filesystem" import { Effect } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" +import { response } from "../../groups/v2/location" export const fileSystemHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.fs", (handlers) => Effect.gen(function* () { return handlers - .handle("read", (ctx) => FileSystem.Service.use((fs) => fs.read(ctx.query))) - .handle("list", (ctx) => FileSystem.Service.use((fs) => fs.list(ctx.query))) + .handle("read", (ctx) => response(FileSystem.Service.use((fs) => fs.read(ctx.query)))) + .handle("list", (ctx) => response(FileSystem.Service.use((fs) => fs.list(ctx.query)))) }), ) diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/model.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/model.ts index 4a748ef9b7..7df713d331 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/model.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/model.ts @@ -4,6 +4,7 @@ import { Effect } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" import { ServiceUnavailableError } from "../../errors" +import { response } from "../../groups/v2/location" const catalogUnavailable = new ServiceUnavailableError({ message: "Model catalog is unavailable", @@ -18,7 +19,7 @@ export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", ( const catalog = yield* Catalog.Service const pluginBoot = yield* PluginBoot.Service yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable))) - return yield* catalog.model.available() + return yield* response(catalog.model.available()) }), ) }), diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/permission.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/permission.ts index 8808042a11..e697ef314f 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/permission.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/permission.ts @@ -9,6 +9,7 @@ import { Effect } from "effect" import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" import { PermissionNotFoundError, SessionNotFoundError } from "../../errors" +import { response } from "../../groups/v2/location" function missingRequest(id: PermissionV2.ID) { return new PermissionNotFoundError({ requestID: id, message: `Permission request not found: ${id}` }) @@ -19,7 +20,7 @@ export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.perm return handlers.handle( "permissionRequests", Effect.fn(function* () { - return yield* (yield* PermissionV2.Service).list() + return yield* response((yield* PermissionV2.Service).list()) }), ) }), diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/provider.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/provider.ts index 2bc5cfbe82..37c9429517 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/provider.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/provider.ts @@ -4,6 +4,7 @@ import { Effect } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors" +import { response } from "../../groups/v2/location" const catalogUnavailable = new ServiceUnavailableError({ message: "Provider catalog is unavailable", @@ -19,7 +20,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provid const catalog = yield* Catalog.Service const pluginBoot = yield* PluginBoot.Service yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable))) - return yield* catalog.provider.available() + return yield* response(catalog.provider.available()) }), ) .handle( @@ -28,7 +29,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provid const catalog = yield* Catalog.Service const pluginBoot = yield* PluginBoot.Service yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable))) - return yield* catalog.provider.get(ctx.params.providerID).pipe( + return yield* response(catalog.provider.get(ctx.params.providerID)).pipe( Effect.catchTag("CatalogV2.ProviderNotFound", (error) => Effect.fail( new ProviderNotFoundError({ diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/skill.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/skill.ts index f382ac48fc..e10ae66ab2 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/skill.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/skill.ts @@ -1,7 +1,8 @@ import { SkillV2 } from "@opencode-ai/core/skill" import { HttpApiBuilder } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" +import { response } from "../../groups/v2/location" export const skillHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.skill", (handlers) => - handlers.handle("skills", () => SkillV2.Service.use((skill) => skill.list())), + handlers.handle("skills", () => response(SkillV2.Service.use((skill) => skill.list()))), ) diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts index 4b577bc381..2a295d1821 100644 --- a/packages/opencode/src/session/session.ts +++ b/packages/opencode/src/session/session.ts @@ -40,7 +40,7 @@ import type { Provider } from "@/provider/provider" import { Permission } from "@/permission" import { Global } from "@opencode-ai/core/global" import { Effect, Layer, Option, Context, Schema, Types } from "effect" -import { AbsolutePath, NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema" +import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema" import { RuntimeFlags } from "@/effect/runtime-flags" import { ProviderV2 } from "@opencode-ai/core/provider" @@ -112,13 +112,6 @@ export function fromRow(row: SessionRow): Info { } } -function eventLocation(info: Pick) { - return { - directory: AbsolutePath.make(info.directory), - workspaceID: info.workspaceID, - } -} - export function toRow(info: Info) { return { id: info.id, @@ -544,20 +537,6 @@ export const layer: Layer.Layer< const events = yield* EventV2Bridge.Service const flags = yield* RuntimeFlags.Service - const locationForSession = Effect.fnUntraced(function* (sessionID: SessionID) { - const row = yield* db - .select({ directory: SessionTable.directory, workspaceID: SessionTable.workspace_id }) - .from(SessionTable) - .where(eq(SessionTable.id, sessionID)) - .get() - .pipe(Effect.orDie) - if (!row) return - return { - directory: AbsolutePath.make(row.directory), - workspaceID: row.workspaceID ?? undefined, - } - }) - const createNext = Effect.fn("Session.createNext")(function* (input: { id?: SessionID title?: string @@ -597,7 +576,6 @@ export const layer: Layer.Layer< yield* events.publish( SessionV1.Event.Created, { sessionID: result.id, info: result }, - { location: eventLocation(result) }, ) return result @@ -688,7 +666,6 @@ export const layer: Layer.Layer< yield* events.publish( SessionV1.Event.Deleted, { sessionID, info: session }, - { location: eventLocation(session) }, ) yield* events.remove(sessionID) } catch (e) { @@ -698,14 +675,12 @@ export const layer: Layer.Layer< const updateMessage = (msg: T): Effect.Effect => Effect.gen(function* () { - const location = yield* locationForSession(msg.sessionID) - yield* events.publish(SessionV1.Event.MessageUpdated, { sessionID: msg.sessionID, info: msg }, { location }) + yield* events.publish(SessionV1.Event.MessageUpdated, { sessionID: msg.sessionID, info: msg }) return msg }).pipe(Effect.withSpan("Session.updateMessage")) const updatePart = (part: T): Effect.Effect => Effect.gen(function* () { - const location = yield* locationForSession(part.sessionID) yield* events.publish( SessionV1.Event.PartUpdated, { @@ -713,7 +688,6 @@ export const layer: Layer.Layer< part: structuredClone(part), time: Date.now(), }, - { location }, ) return part }).pipe(Effect.withSpan("Session.updatePart")) @@ -819,7 +793,7 @@ export const layer: Layer.Layer< revert: info.revert === null ? undefined : (info.revert ?? current.revert), permission: info.permission === null ? undefined : (info.permission ?? current.permission), } as Info - yield* events.publish(SessionV1.Event.Updated, { sessionID, info: next }, { location: eventLocation(next) }) + yield* events.publish(SessionV1.Event.Updated, { sessionID, info: next }) }) const touch = Effect.fn("Session.touch")(function* (sessionID: SessionID) { @@ -917,14 +891,12 @@ export const layer: Layer.Layer< sessionID: SessionID messageID: MessageID }) { - const location = yield* locationForSession(input.sessionID) yield* events.publish( SessionV1.Event.MessageRemoved, { sessionID: input.sessionID, messageID: input.messageID, }, - { location }, ) return input.messageID }) @@ -934,7 +906,6 @@ export const layer: Layer.Layer< messageID: MessageID partID: PartID }) { - const location = yield* locationForSession(input.sessionID) yield* events.publish( SessionV1.Event.PartRemoved, { @@ -942,7 +913,6 @@ export const layer: Layer.Layer< messageID: input.messageID, partID: input.partID, }, - { location }, ) return input.partID }) diff --git a/packages/opencode/test/server/httpapi-v2-location.test.ts b/packages/opencode/test/server/httpapi-v2-location.test.ts index eeedace7f6..6cb98952ba 100644 --- a/packages/opencode/test/server/httpapi-v2-location.test.ts +++ b/packages/opencode/test/server/httpapi-v2-location.test.ts @@ -26,15 +26,16 @@ afterEach(async () => { }) describe("v2 location HttpApi", () => { - test("returns command and skill snapshots with resolved location headers", async () => { + test("returns command and skill snapshots with resolved locations", async () => { await using tmp = await tmpdir({ git: true }) for (const route of ["/api/command", "/api/skill"]) { const response = await request(route, tmp.path) expect(response.status).toBe(200) - expect(await response.json()).toBeArray() - expect(response.headers.get("x-opencode-directory")).toBe(tmp.path) - expect(response.headers.get("x-opencode-project-id")).toBeTruthy() + const body = (await response.json()) as { location: { directory: string; project: { id: string } }; data: unknown } + expect(body.data).toBeArray() + expect(body.location.directory).toBe(tmp.path) + expect(body.location.project.id).toBeTruthy() } }) })