Skip to content

Commit 90bde18

Browse files
authored
feat(id): brand WorkspaceID through Drizzle and Zod schemas (anomalyco#16964)
1 parent 57d3dd2 commit 90bde18

49 files changed

Lines changed: 205 additions & 157 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/opencode/script/seed-e2e.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ const seed = async () => {
1212
const { InstanceBootstrap } = await import("../src/project/bootstrap")
1313
const { Session } = await import("../src/session")
1414
const { Identifier } = await import("../src/id/id")
15+
const { MessageID } = await import("../src/session/schema")
1516
const { Project } = await import("../src/project/project")
1617

1718
await Instance.provide({
1819
directory: dir,
1920
init: InstanceBootstrap,
2021
fn: async () => {
2122
const session = await Session.create({ title })
22-
const messageID = Identifier.descending("message")
23+
const messageID = MessageID.ascending()
2324
const partID = Identifier.descending("part")
2425
const message = {
2526
id: messageID,

packages/opencode/src/cli/cmd/debug/agent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Provider } from "../../../provider/provider"
55
import { Session } from "../../../session"
66
import type { MessageV2 } from "../../../session/message-v2"
77
import { Identifier } from "../../../id/id"
8+
import { MessageID } from "../../../session/schema"
89
import { ToolRegistry } from "../../../tool/registry"
910
import { Instance } from "../../../project/instance"
1011
import { PermissionNext } from "../../../permission/next"
@@ -113,7 +114,7 @@ function parseToolParams(input?: string) {
113114

114115
async function createToolContext(agent: Agent.Info) {
115116
const session = await Session.create({ title: `Debug tool run (${agent.name})` })
116-
const messageID = Identifier.ascending("message")
117+
const messageID = MessageID.ascending()
117118
const model = agent.model ?? (await Provider.defaultModel())
118119
const now = Date.now()
119120
const message: MessageV2.Assistant = {

packages/opencode/src/cli/cmd/github.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { bootstrap } from "../bootstrap"
2424
import { Session } from "../../session"
2525
import type { SessionID } from "../../session/schema"
2626
import { Identifier } from "../../id/id"
27+
import { MessageID } from "../../session/schema"
2728
import { Provider } from "../../provider/provider"
2829
import { Bus } from "../../bus"
2930
import { MessageV2 } from "../../session/message-v2"
@@ -935,7 +936,7 @@ export const GithubRunCommand = cmd({
935936

936937
const result = await SessionPrompt.prompt({
937938
sessionID: session.id,
938-
messageID: Identifier.ascending("message"),
939+
messageID: MessageID.ascending(),
939940
variant,
940941
model: {
941942
providerID,
@@ -989,7 +990,7 @@ export const GithubRunCommand = cmd({
989990
console.log("Requesting summary from agent...")
990991
const summary = await SessionPrompt.prompt({
991992
sessionID: session.id,
992-
messageID: Identifier.ascending("message"),
993+
messageID: MessageID.ascending(),
993994
variant,
994995
model: {
995996
providerID,

packages/opencode/src/cli/cmd/import.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { Argv } from "yargs"
22
import type { Session as SDKSession, Message, Part } from "@opencode-ai/sdk/v2"
33
import { Session } from "../../session"
4-
import { SessionID } from "../../session/schema"
4+
import { SessionID, MessageID } from "../../session/schema"
5+
import { WorkspaceID } from "../../control-plane/schema"
56
import { cmd } from "./cmd"
67
import { bootstrap } from "../bootstrap"
78
import { Database } from "../../storage/db"
@@ -157,7 +158,11 @@ export const ImportCommand = cmd({
157158
...exportData.info,
158159
id: SessionID.make(exportData.info.id),
159160
parentID: exportData.info.parentID ? SessionID.make(exportData.info.parentID) : undefined,
161+
workspaceID: exportData.info.workspaceID ? WorkspaceID.make(exportData.info.workspaceID) : undefined,
160162
projectID: Instance.project.id,
163+
revert: exportData.info.revert
164+
? { ...exportData.info.revert, messageID: MessageID.make(exportData.info.revert.messageID) }
165+
: undefined,
161166
})
162167
Database.use((db) =>
163168
db
@@ -168,28 +173,30 @@ export const ImportCommand = cmd({
168173
)
169174

170175
for (const msg of exportData.messages) {
176+
const { id: _mid, sessionID: _msid, ...msgData } = msg.info
171177
Database.use((db) =>
172178
db
173179
.insert(MessageTable)
174180
.values({
175-
id: msg.info.id,
181+
id: MessageID.make(msg.info.id),
176182
session_id: row.id,
177183
time_created: msg.info.time?.created ?? Date.now(),
178-
data: msg.info,
184+
data: msgData,
179185
})
180186
.onConflictDoNothing()
181187
.run(),
182188
)
183189

184190
for (const part of msg.parts) {
191+
const { id: _pid, sessionID: _psid, messageID: _pmid, ...partData } = part
185192
Database.use((db) =>
186193
db
187194
.insert(PartTable)
188195
.values({
189196
id: part.id,
190-
message_id: msg.info.id,
197+
message_id: MessageID.make(msg.info.id),
191198
session_id: row.id,
192-
data: part,
199+
data: partData,
193200
})
194201
.onConflictDoNothing()
195202
.run(),

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useSDK } from "@tui/context/sdk"
1010
import { useRoute } from "@tui/context/route"
1111
import { useSync } from "@tui/context/sync"
1212
import { Identifier } from "@/id/id"
13+
import { MessageID } from "@/session/schema"
1314
import { createStore, produce } from "solid-js/store"
1415
import { useKeybind } from "@tui/context/keybind"
1516
import { usePromptHistory, type PromptInfo } from "./history"
@@ -561,7 +562,7 @@ export function Prompt(props: PromptProps) {
561562
sessionID = res.data.id
562563
}
563564

564-
const messageID = Identifier.ascending("message")
565+
const messageID = MessageID.ascending()
565566
let inputText = store.prompt.input
566567

567568
// Expand pasted text inline before submitting

packages/opencode/src/command/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BusEvent } from "@/bus/bus-event"
2-
import { SessionID } from "@/session/schema"
2+
import { SessionID, MessageID } from "@/session/schema"
33
import z from "zod"
44
import { Config } from "../config/config"
55
import { Instance } from "../project/instance"
@@ -17,7 +17,7 @@ export namespace Command {
1717
name: z.string(),
1818
sessionID: SessionID.zod,
1919
arguments: z.string(),
20-
messageID: Identifier.schema("message"),
20+
messageID: MessageID.zod,
2121
}),
2222
),
2323
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Schema } from "effect"
2+
import z from "zod"
3+
4+
import { withStatics } from "@/util/schema"
5+
import { Identifier } from "@/id/id"
6+
7+
const workspaceIdSchema = Schema.String.pipe(Schema.brand("WorkspaceId"))
8+
9+
export type WorkspaceID = typeof workspaceIdSchema.Type
10+
11+
export const WorkspaceID = workspaceIdSchema.pipe(
12+
withStatics((schema: typeof workspaceIdSchema) => ({
13+
make: (id: string) => schema.makeUnsafe(id),
14+
ascending: (id?: string) => schema.makeUnsafe(Identifier.ascending("workspace", id)),
15+
zod: z.string().startsWith("wrk").pipe(z.custom<WorkspaceID>()),
16+
})),
17+
)

packages/opencode/src/control-plane/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import z from "zod"
2-
import { Identifier } from "@/id/id"
32
import { ProjectID } from "@/project/schema"
3+
import { WorkspaceID } from "./schema"
44

55
export const WorkspaceInfo = z.object({
6-
id: Identifier.schema("workspace"),
6+
id: WorkspaceID.zod,
77
type: z.string(),
88
branch: z.string().nullable(),
99
name: z.string().nullable(),

packages/opencode/src/control-plane/workspace-context.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Context } from "../util/context"
2+
import type { WorkspaceID } from "./schema"
23

34
interface Context {
4-
workspaceID?: string
5+
workspaceID?: WorkspaceID
56
}
67

78
const context = Context.create<Context>("workspace")
89

910
export const WorkspaceContext = {
10-
async provide<R>(input: { workspaceID?: string; fn: () => R }): Promise<R> {
11+
async provide<R>(input: { workspaceID?: WorkspaceID; fn: () => R }): Promise<R> {
1112
return context.provide({ workspaceID: input.workspaceID }, async () => {
1213
return input.fn()
1314
})

packages/opencode/src/control-plane/workspace-server/server.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InstanceBootstrap } from "../../project/bootstrap"
44
import { SessionRoutes } from "../../server/routes/session"
55
import { WorkspaceServerRoutes } from "./routes"
66
import { WorkspaceContext } from "../workspace-context"
7+
import { WorkspaceID } from "../schema"
78

89
export namespace WorkspaceServer {
910
export function App() {
@@ -20,9 +21,9 @@ export namespace WorkspaceServer {
2021

2122
return new Hono()
2223
.use(async (c, next) => {
23-
const workspaceID = c.req.query("workspace") || c.req.header("x-opencode-workspace")
24+
const rawWorkspaceID = c.req.query("workspace") || c.req.header("x-opencode-workspace")
2425
const raw = c.req.query("directory") || c.req.header("x-opencode-directory")
25-
if (workspaceID == null) {
26+
if (rawWorkspaceID == null) {
2627
throw new Error("workspaceID parameter is required")
2728
}
2829
if (raw == null) {
@@ -38,7 +39,7 @@ export namespace WorkspaceServer {
3839
})()
3940

4041
return WorkspaceContext.provide({
41-
workspaceID,
42+
workspaceID: WorkspaceID.make(rawWorkspaceID),
4243
async fn() {
4344
return Instance.provide({
4445
directory,

0 commit comments

Comments
 (0)