Skip to content

Commit 0cdc6f0

Browse files
committed
feat(session): brand PartID through Drizzle and Zod schemas
- Introduces Effect branded PartID type in session/schema.ts with .make(), .ascending(), and .zod statics - Flows PartID through Drizzle PartTable.id, PartBase.id zod schema, revert.partID, server routes, and all call sites - Replaces all Identifier.ascending("part") with PartID.ascending() - Updates tests to use proper branded IDs
1 parent d26c6f8 commit 0cdc6f0

File tree

21 files changed

+102
-97
lines changed

21 files changed

+102
-97
lines changed

packages/opencode/script/seed-e2e.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ const seed = async () => {
1111
const { Instance } = await import("../src/project/instance")
1212
const { InstanceBootstrap } = await import("../src/project/bootstrap")
1313
const { Session } = await import("../src/session")
14-
const { Identifier } = await import("../src/id/id")
15-
const { MessageID } = await import("../src/session/schema")
14+
const { MessageID, PartID } = await import("../src/session/schema")
1615
const { Project } = await import("../src/project/project")
1716

1817
await Instance.provide({
@@ -21,7 +20,7 @@ const seed = async () => {
2120
fn: async () => {
2221
const session = await Session.create({ title })
2322
const messageID = MessageID.ascending()
24-
const partID = Identifier.descending("part")
23+
const partID = PartID.ascending()
2524
const message = {
2625
id: messageID,
2726
sessionID: session.id,

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { Agent } from "../../../agent/agent"
44
import { Provider } from "../../../provider/provider"
55
import { Session } from "../../../session"
66
import type { MessageV2 } from "../../../session/message-v2"
7-
import { Identifier } from "../../../id/id"
8-
import { MessageID } from "../../../session/schema"
7+
import { MessageID, PartID } from "../../../session/schema"
98
import { ToolRegistry } from "../../../tool/registry"
109
import { Instance } from "../../../project/instance"
1110
import { PermissionNext } from "../../../permission/next"
@@ -151,7 +150,7 @@ async function createToolContext(agent: Agent.Info) {
151150
return {
152151
sessionID: session.id,
153152
messageID,
154-
callID: Identifier.ascending("part"),
153+
callID: PartID.ascending(),
155154
agent: agent.name,
156155
abort: new AbortController().signal,
157156
messages: [],

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ import { Instance } from "@/project/instance"
2323
import { bootstrap } from "../bootstrap"
2424
import { Session } from "../../session"
2525
import type { SessionID } from "../../session/schema"
26-
import { Identifier } from "../../id/id"
27-
import { MessageID } from "../../session/schema"
26+
import { MessageID, PartID } from "../../session/schema"
2827
import { Provider } from "../../provider/provider"
2928
import { Bus } from "../../bus"
3029
import { MessageV2 } from "../../session/message-v2"
@@ -945,13 +944,13 @@ export const GithubRunCommand = cmd({
945944
// agent is omitted - server will use default_agent from config or fall back to "build"
946945
parts: [
947946
{
948-
id: Identifier.ascending("part"),
947+
id: PartID.ascending(),
949948
type: "text",
950949
text: message,
951950
},
952951
...files.flatMap((f) => [
953952
{
954-
id: Identifier.ascending("part"),
953+
id: PartID.ascending(),
955954
type: "file" as const,
956955
mime: f.mime,
957956
url: `data:${f.mime};base64,${f.content}`,
@@ -999,7 +998,7 @@ export const GithubRunCommand = cmd({
999998
tools: { "*": false }, // Disable all tools to force text response
1000999
parts: [
10011000
{
1002-
id: Identifier.ascending("part"),
1001+
id: PartID.ascending(),
10031002
type: "text",
10041003
text: "Summarize the actions (tool calls & reasoning) you did for the user in 1-2 sentences.",
10051004
},

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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, MessageID } from "../../session/schema"
4+
import { SessionID, MessageID, PartID } from "../../session/schema"
55
import { WorkspaceID } from "../../control-plane/schema"
66
import { cmd } from "./cmd"
77
import { bootstrap } from "../bootstrap"
@@ -161,7 +161,11 @@ export const ImportCommand = cmd({
161161
workspaceID: exportData.info.workspaceID ? WorkspaceID.make(exportData.info.workspaceID) : undefined,
162162
projectID: Instance.project.id,
163163
revert: exportData.info.revert
164-
? { ...exportData.info.revert, messageID: MessageID.make(exportData.info.revert.messageID) }
164+
? {
165+
...exportData.info.revert,
166+
messageID: MessageID.make(exportData.info.revert.messageID),
167+
partID: exportData.info.revert.partID ? PartID.make(exportData.info.revert.partID) : undefined,
168+
}
165169
: undefined,
166170
})
167171
Database.use((db) =>
@@ -193,7 +197,7 @@ export const ImportCommand = cmd({
193197
db
194198
.insert(PartTable)
195199
.values({
196-
id: part.id,
200+
id: PartID.make(part.id),
197201
message_id: MessageID.make(msg.info.id),
198202
session_id: row.id,
199203
data: partData,

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import { EmptyBorder } from "@tui/component/border"
99
import { useSDK } from "@tui/context/sdk"
1010
import { useRoute } from "@tui/context/route"
1111
import { useSync } from "@tui/context/sync"
12-
import { Identifier } from "@/id/id"
13-
import { MessageID } from "@/session/schema"
12+
import { MessageID, PartID } from "@/session/schema"
1413
import { createStore, produce } from "solid-js/store"
1514
import { useKeybind } from "@tui/context/keybind"
1615
import { usePromptHistory, type PromptInfo } from "./history"
@@ -625,7 +624,7 @@ export function Prompt(props: PromptProps) {
625624
parts: nonTextParts
626625
.filter((x) => x.type === "file")
627626
.map((x) => ({
628-
id: Identifier.ascending("part"),
627+
id: PartID.ascending(),
629628
...x,
630629
})),
631630
})
@@ -640,12 +639,12 @@ export function Prompt(props: PromptProps) {
640639
variant,
641640
parts: [
642641
{
643-
id: Identifier.ascending("part"),
642+
id: PartID.ascending(),
644643
type: "text",
645644
text: inputText,
646645
},
647646
...nonTextParts.map((x) => ({
648-
id: Identifier.ascending("part"),
647+
id: PartID.ascending(),
649648
...x,
650649
})),
651650
],

packages/opencode/src/server/routes/session.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Hono } from "hono"
22
import { stream } from "hono/streaming"
33
import { describeRoute, validator, resolver } from "hono-openapi"
4-
import { SessionID, MessageID } from "@/session/schema"
4+
import { SessionID, MessageID, PartID } from "@/session/schema"
55
import z from "zod"
66
import { Session } from "../../session"
77
import { MessageV2 } from "../../session/message-v2"
@@ -677,7 +677,7 @@ export const SessionRoutes = lazy(() =>
677677
z.object({
678678
sessionID: SessionID.zod,
679679
messageID: MessageID.zod,
680-
partID: z.string(),
680+
partID: PartID.zod,
681681
}),
682682
),
683683
async (c) => {
@@ -712,7 +712,7 @@ export const SessionRoutes = lazy(() =>
712712
z.object({
713713
sessionID: SessionID.zod,
714714
messageID: MessageID.zod,
715-
partID: z.string(),
715+
partID: PartID.zod,
716716
}),
717717
),
718718
validator("json", MessageV2.Part),

packages/opencode/src/session/compaction.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { BusEvent } from "@/bus/bus-event"
22
import { Bus } from "@/bus"
33
import { Session } from "."
4-
import { Identifier } from "../id/id"
5-
import { SessionID, MessageID } from "./schema"
4+
import { SessionID, MessageID, PartID } from "./schema"
65
import { Instance } from "../project/instance"
76
import { Provider } from "../provider/provider"
87
import { MessageV2 } from "./message-v2"
@@ -256,7 +255,7 @@ When constructing the summary, try to stick to this template:
256255
: part
257256
await Session.updatePart({
258257
...replayPart,
259-
id: Identifier.ascending("part"),
258+
id: PartID.ascending(),
260259
messageID: replayMsg.id,
261260
sessionID: input.sessionID,
262261
})
@@ -276,7 +275,7 @@ When constructing the summary, try to stick to this template:
276275
: "") +
277276
"Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed."
278277
await Session.updatePart({
279-
id: Identifier.ascending("part"),
278+
id: PartID.ascending(),
280279
messageID: continueMsg.id,
281280
sessionID: input.sessionID,
282281
type: "text",
@@ -317,7 +316,7 @@ When constructing the summary, try to stick to this template:
317316
},
318317
})
319318
await Session.updatePart({
320-
id: Identifier.ascending("part"),
319+
id: PartID.ascending(),
321320
messageID: msg.id,
322321
sessionID: msg.sessionID,
323322
type: "compaction",

packages/opencode/src/session/index.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import z from "zod"
77
import { type ProviderMetadata } from "ai"
88
import { Config } from "../config/config"
99
import { Flag } from "../flag/flag"
10-
import { Identifier } from "../id/id"
1110
import { Installation } from "../installation"
1211

1312
import { Database, NotFoundError, eq, and, or, gte, isNull, desc, like, inArray, lt } from "../storage/db"
@@ -25,7 +24,7 @@ import { Snapshot } from "@/snapshot"
2524
import { WorkspaceContext } from "../control-plane/workspace-context"
2625
import { ProjectID } from "../project/schema"
2726
import { WorkspaceID } from "../control-plane/schema"
28-
import { SessionID, MessageID } from "./schema"
27+
import { SessionID, MessageID, PartID } from "./schema"
2928

3029
import type { Provider } from "@/provider/provider"
3130
import { PermissionNext } from "@/permission/next"
@@ -152,7 +151,7 @@ export namespace Session {
152151
revert: z
153152
.object({
154153
messageID: MessageID.zod,
155-
partID: z.string().optional(),
154+
partID: PartID.zod.optional(),
156155
snapshot: z.string().optional(),
157156
diff: z.string().optional(),
158157
})
@@ -269,7 +268,7 @@ export namespace Session {
269268
for (const part of msg.parts) {
270269
await updatePart({
271270
...part,
272-
id: Identifier.ascending("part"),
271+
id: PartID.ascending(),
273272
messageID: cloned.id,
274273
sessionID: session.id,
275274
})
@@ -731,7 +730,7 @@ export namespace Session {
731730
z.object({
732731
sessionID: SessionID.zod,
733732
messageID: MessageID.zod,
734-
partID: Identifier.schema("part"),
733+
partID: PartID.zod,
735734
}),
736735
async (input) => {
737736
Database.use((db) => {
@@ -779,7 +778,7 @@ export namespace Session {
779778
z.object({
780779
sessionID: SessionID.zod,
781780
messageID: MessageID.zod,
782-
partID: z.string(),
781+
partID: PartID.zod,
783782
field: z.string(),
784783
delta: z.string(),
785784
}),

packages/opencode/src/session/message-v2.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BusEvent } from "@/bus/bus-event"
2-
import { SessionID, MessageID } from "./schema"
2+
import { SessionID, MessageID, PartID } from "./schema"
33
import z from "zod"
44
import { NamedError } from "@opencode-ai/util/error"
55
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
@@ -78,7 +78,7 @@ export namespace MessageV2 {
7878
export type OutputFormat = z.infer<typeof Format>
7979

8080
const PartBase = z.object({
81-
id: z.string(),
81+
id: PartID.zod,
8282
sessionID: SessionID.zod,
8383
messageID: MessageID.zod,
8484
})
@@ -472,7 +472,7 @@ export namespace MessageV2 {
472472
z.object({
473473
sessionID: SessionID.zod,
474474
messageID: MessageID.zod,
475-
partID: z.string(),
475+
partID: PartID.zod,
476476
field: z.string(),
477477
delta: z.string(),
478478
}),
@@ -482,7 +482,7 @@ export namespace MessageV2 {
482482
z.object({
483483
sessionID: SessionID.zod,
484484
messageID: MessageID.zod,
485-
partID: z.string(),
485+
partID: PartID.zod,
486486
}),
487487
),
488488
}

packages/opencode/src/session/processor.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { MessageV2 } from "./message-v2"
22
import { Log } from "@/util/log"
3-
import { Identifier } from "@/id/id"
43
import { Session } from "."
54
import { Agent } from "@/agent/agent"
65
import { Snapshot } from "@/snapshot"
@@ -15,6 +14,7 @@ import { Config } from "@/config/config"
1514
import { SessionCompaction } from "./compaction"
1615
import { PermissionNext } from "@/permission/next"
1716
import { Question } from "@/question"
17+
import { PartID } from "./schema"
1818
import type { SessionID, MessageID } from "./schema"
1919

2020
export namespace SessionProcessor {
@@ -65,7 +65,7 @@ export namespace SessionProcessor {
6565
continue
6666
}
6767
const reasoningPart = {
68-
id: Identifier.ascending("part"),
68+
id: PartID.ascending(),
6969
messageID: input.assistantMessage.id,
7070
sessionID: input.assistantMessage.sessionID,
7171
type: "reasoning" as const,
@@ -111,7 +111,7 @@ export namespace SessionProcessor {
111111

112112
case "tool-input-start":
113113
const part = await Session.updatePart({
114-
id: toolcalls[value.id]?.id ?? Identifier.ascending("part"),
114+
id: toolcalls[value.id]?.id ?? PartID.ascending(),
115115
messageID: input.assistantMessage.id,
116116
sessionID: input.assistantMessage.sessionID,
117117
type: "tool",
@@ -234,7 +234,7 @@ export namespace SessionProcessor {
234234
case "start-step":
235235
snapshot = await Snapshot.track()
236236
await Session.updatePart({
237-
id: Identifier.ascending("part"),
237+
id: PartID.ascending(),
238238
messageID: input.assistantMessage.id,
239239
sessionID: input.sessionID,
240240
snapshot,
@@ -252,7 +252,7 @@ export namespace SessionProcessor {
252252
input.assistantMessage.cost += usage.cost
253253
input.assistantMessage.tokens = usage.tokens
254254
await Session.updatePart({
255-
id: Identifier.ascending("part"),
255+
id: PartID.ascending(),
256256
reason: value.finishReason,
257257
snapshot: await Snapshot.track(),
258258
messageID: input.assistantMessage.id,
@@ -266,7 +266,7 @@ export namespace SessionProcessor {
266266
const patch = await Snapshot.patch(snapshot)
267267
if (patch.files.length) {
268268
await Session.updatePart({
269-
id: Identifier.ascending("part"),
269+
id: PartID.ascending(),
270270
messageID: input.assistantMessage.id,
271271
sessionID: input.sessionID,
272272
type: "patch",
@@ -290,7 +290,7 @@ export namespace SessionProcessor {
290290

291291
case "text-start":
292292
currentText = {
293-
id: Identifier.ascending("part"),
293+
id: PartID.ascending(),
294294
messageID: input.assistantMessage.id,
295295
sessionID: input.assistantMessage.sessionID,
296296
type: "text",
@@ -389,7 +389,7 @@ export namespace SessionProcessor {
389389
const patch = await Snapshot.patch(snapshot)
390390
if (patch.files.length) {
391391
await Session.updatePart({
392-
id: Identifier.ascending("part"),
392+
id: PartID.ascending(),
393393
messageID: input.assistantMessage.id,
394394
sessionID: input.sessionID,
395395
type: "patch",

0 commit comments

Comments
 (0)