Skip to content

Commit 699cf79

Browse files
authored
refactor(session): migrate session domain to Effect Schema (anomalyco#24005)
1 parent eede888 commit 699cf79

18 files changed

Lines changed: 820 additions & 450 deletions

File tree

packages/opencode/specs/effect/schema.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,18 @@ Schema at source.
159159
These are the highest-priority next targets. Each is a small, self-contained
160160
schema module with a clear domain.
161161

162+
- [x] `src/account/schema.ts`
162163
- [x] `src/control-plane/schema.ts`
163164
- [x] `src/permission/schema.ts`
164165
- [x] `src/project/schema.ts`
165166
- [x] `src/provider/schema.ts`
166167
- [x] `src/pty/schema.ts`
167168
- [x] `src/question/schema.ts`
168169
- [x] `src/session/schema.ts`
170+
- [x] `src/storage/schema.ts`
169171
- [x] `src/sync/schema.ts`
170172
- [x] `src/tool/schema.ts`
173+
- [x] `src/util/schema.ts`
171174

172175
### Session domain
173176

@@ -248,15 +251,15 @@ Possible later tightening after the Schema-first migration is stable:
248251
- promote repeated opaque strings and timestamp numbers into branded/newtype
249252
leaf schemas where that adds domain value without changing the wire format
250253

251-
- [ ] `src/session/compaction.ts`
252-
- [ ] `src/session/message-v2.ts`
253-
- [ ] `src/session/message.ts`
254-
- [ ] `src/session/prompt.ts`
255-
- [ ] `src/session/revert.ts`
256-
- [ ] `src/session/session.ts`
257-
- [ ] `src/session/status.ts`
258-
- [ ] `src/session/summary.ts`
259-
- [ ] `src/session/todo.ts`
254+
- [x] `src/session/compaction.ts`
255+
- [x] `src/session/message-v2.ts`
256+
- [x] `src/session/message.ts`
257+
- [x] `src/session/prompt.ts`
258+
- [x] `src/session/revert.ts`
259+
- [x] `src/session/session.ts`
260+
- [x] `src/session/status.ts`
261+
- [x] `src/session/summary.ts`
262+
- [x] `src/session/todo.ts`
260263

261264
### Provider domain
262265

packages/opencode/src/acp/agent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ export class Agent implements ACPAgent {
372372
}
373373

374374
if (part.tool === "todowrite") {
375-
const parsedTodos = z.array(Todo.Info).safeParse(JSON.parse(part.state.output))
375+
const parsedTodos = z.array(Todo.Info.zod).safeParse(JSON.parse(part.state.output))
376376
if (parsedTodos.success) {
377377
await this.connection
378378
.sessionUpdate({
@@ -901,7 +901,7 @@ export class Agent implements ACPAgent {
901901
}
902902

903903
if (part.tool === "todowrite") {
904-
const parsedTodos = z.array(Todo.Info).safeParse(JSON.parse(part.state.output))
904+
const parsedTodos = z.array(Todo.Info.zod).safeParse(JSON.parse(part.state.output))
905905
if (parsedTodos.success) {
906906
await this.connection
907907
.sessionUpdate({

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ShareNext } from "../../share"
1111
import { EOL } from "os"
1212
import { Filesystem } from "../../util"
1313
import { AppRuntime } from "@/effect/app-runtime"
14+
import { Schema } from "effect"
1415

1516
/** Discriminated union returned by the ShareNext API (GET /api/shares/:id/data) */
1617
export type ShareData =
@@ -154,10 +155,10 @@ export const ImportCommand = cmd({
154155
return
155156
}
156157

157-
const info = Session.Info.parse({
158+
const info = Schema.decodeUnknownSync(Session.Info)({
158159
...exportData.info,
159160
projectID: Instance.project.id,
160-
})
161+
}) as Session.Info
161162
const row = Session.toRow(info)
162163
Database.use((db) =>
163164
db

packages/opencode/src/server/routes/instance/experimental.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ export const ExperimentalRoutes = lazy(() =>
335335
description: "List of sessions",
336336
content: {
337337
"application/json": {
338-
schema: resolver(Session.GlobalInfo.array()),
338+
schema: resolver(Session.GlobalInfo.zod.array()),
339339
},
340340
},
341341
},

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

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { PermissionID } from "@/permission/schema"
2323
import { ModelID, ProviderID } from "@/provider/schema"
2424
import { errors } from "../../error"
2525
import { lazy } from "@/util/lazy"
26+
import { zodObject } from "@/util/effect-zod"
2627
import { Bus } from "@/bus"
2728
import { NamedError } from "@opencode-ai/shared/util/error"
2829
import { jsonRequest, runRequest } from "./trace"
@@ -42,7 +43,7 @@ export const SessionRoutes = lazy(() =>
4243
description: "List of sessions",
4344
content: {
4445
"application/json": {
45-
schema: resolver(Session.Info.array()),
46+
schema: resolver(Session.Info.zod.array()),
4647
},
4748
},
4849
},
@@ -87,7 +88,7 @@ export const SessionRoutes = lazy(() =>
8788
description: "Get session status",
8889
content: {
8990
"application/json": {
90-
schema: resolver(z.record(z.string(), SessionStatus.Info)),
91+
schema: resolver(z.record(z.string(), SessionStatus.Info.zod)),
9192
},
9293
},
9394
},
@@ -112,7 +113,7 @@ export const SessionRoutes = lazy(() =>
112113
description: "Get session",
113114
content: {
114115
"application/json": {
115-
schema: resolver(Session.Info),
116+
schema: resolver(Session.Info.zod),
116117
},
117118
},
118119
},
@@ -122,7 +123,7 @@ export const SessionRoutes = lazy(() =>
122123
validator(
123124
"param",
124125
z.object({
125-
sessionID: Session.GetInput,
126+
sessionID: Session.GetInput.zod,
126127
}),
127128
),
128129
async (c) => {
@@ -145,7 +146,7 @@ export const SessionRoutes = lazy(() =>
145146
description: "List of children",
146147
content: {
147148
"application/json": {
148-
schema: resolver(Session.Info.array()),
149+
schema: resolver(Session.Info.zod.array()),
149150
},
150151
},
151152
},
@@ -155,7 +156,7 @@ export const SessionRoutes = lazy(() =>
155156
validator(
156157
"param",
157158
z.object({
158-
sessionID: Session.ChildrenInput,
159+
sessionID: Session.ChildrenInput.zod,
159160
}),
160161
),
161162
async (c) => {
@@ -177,7 +178,7 @@ export const SessionRoutes = lazy(() =>
177178
description: "Todo list",
178179
content: {
179180
"application/json": {
180-
schema: resolver(Todo.Info.array()),
181+
schema: resolver(Todo.Info.zod.array()),
181182
},
182183
},
183184
},
@@ -210,13 +211,13 @@ export const SessionRoutes = lazy(() =>
210211
description: "Successfully created session",
211212
content: {
212213
"application/json": {
213-
schema: resolver(Session.Info),
214+
schema: resolver(Session.Info.zod),
214215
},
215216
},
216217
},
217218
},
218219
}),
219-
validator("json", Session.CreateInput),
220+
validator("json", Session.CreateInput.zod),
220221
async (c) =>
221222
jsonRequest("SessionRoutes.create", c, function* () {
222223
const body = c.req.valid("json") ?? {}
@@ -245,7 +246,7 @@ export const SessionRoutes = lazy(() =>
245246
validator(
246247
"param",
247248
z.object({
248-
sessionID: Session.RemoveInput,
249+
sessionID: Session.RemoveInput.zod,
249250
}),
250251
),
251252
async (c) =>
@@ -267,7 +268,7 @@ export const SessionRoutes = lazy(() =>
267268
description: "Successfully updated session",
268269
content: {
269270
"application/json": {
270-
schema: resolver(Session.Info),
271+
schema: resolver(Session.Info.zod),
271272
},
272273
},
273274
},
@@ -375,7 +376,7 @@ export const SessionRoutes = lazy(() =>
375376
description: "200",
376377
content: {
377378
"application/json": {
378-
schema: resolver(Session.Info),
379+
schema: resolver(Session.Info.zod),
379380
},
380381
},
381382
},
@@ -384,14 +385,14 @@ export const SessionRoutes = lazy(() =>
384385
validator(
385386
"param",
386387
z.object({
387-
sessionID: Session.ForkInput.shape.sessionID,
388+
sessionID: SessionID.zod,
388389
}),
389390
),
390-
validator("json", Session.ForkInput.omit({ sessionID: true })),
391+
validator("json", zodObject(Session.ForkInput).omit({ sessionID: true })),
391392
async (c) =>
392393
jsonRequest("SessionRoutes.fork", c, function* () {
393394
const sessionID = c.req.valid("param").sessionID
394-
const body = c.req.valid("json")
395+
const body = c.req.valid("json") as { messageID?: MessageID }
395396
const svc = yield* Session.Service
396397
return yield* svc.fork({ ...body, sessionID })
397398
}),
@@ -438,7 +439,7 @@ export const SessionRoutes = lazy(() =>
438439
description: "Successfully shared session",
439440
content: {
440441
"application/json": {
441-
schema: resolver(Session.Info),
442+
schema: resolver(Session.Info.zod),
442443
},
443444
},
444445
},
@@ -480,18 +481,13 @@ export const SessionRoutes = lazy(() =>
480481
validator(
481482
"param",
482483
z.object({
483-
sessionID: SessionSummary.DiffInput.shape.sessionID,
484-
}),
485-
),
486-
validator(
487-
"query",
488-
z.object({
489-
messageID: SessionSummary.DiffInput.shape.messageID,
484+
sessionID: SessionID.zod,
490485
}),
491486
),
487+
validator("query", zodObject(SessionSummary.DiffInput).omit({ sessionID: true })),
492488
async (c) =>
493489
jsonRequest("SessionRoutes.diff", c, function* () {
494-
const query = c.req.valid("query")
490+
const query = c.req.valid("query") as Omit<SessionSummary.DiffInput, "sessionID">
495491
const params = c.req.valid("param")
496492
const summary = yield* SessionSummary.Service
497493
return yield* summary.diff({
@@ -511,7 +507,7 @@ export const SessionRoutes = lazy(() =>
511507
description: "Successfully unshared session",
512508
content: {
513509
"application/json": {
514-
schema: resolver(Session.Info),
510+
schema: resolver(Session.Info.zod),
515511
},
516512
},
517513
},
@@ -872,7 +868,7 @@ export const SessionRoutes = lazy(() =>
872868
sessionID: SessionID.zod,
873869
}),
874870
),
875-
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
871+
validator("json", zodObject(SessionPrompt.PromptInput).omit({ sessionID: true })),
876872
async (c) => {
877873
c.status(200)
878874
c.header("Content-Type", "application/json")
@@ -910,7 +906,7 @@ export const SessionRoutes = lazy(() =>
910906
sessionID: SessionID.zod,
911907
}),
912908
),
913-
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
909+
validator("json", zodObject(SessionPrompt.PromptInput).omit({ sessionID: true })),
914910
async (c) => {
915911
const sessionID = c.req.valid("param").sessionID
916912
const body = c.req.valid("json")
@@ -960,11 +956,11 @@ export const SessionRoutes = lazy(() =>
960956
sessionID: SessionID.zod,
961957
}),
962958
),
963-
validator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
959+
validator("json", zodObject(SessionPrompt.CommandInput).omit({ sessionID: true })),
964960
async (c) =>
965961
jsonRequest("SessionRoutes.command", c, function* () {
966962
const sessionID = c.req.valid("param").sessionID
967-
const body = c.req.valid("json")
963+
const body = c.req.valid("json") as Omit<SessionPrompt.CommandInput, "sessionID">
968964
const svc = yield* SessionPrompt.Service
969965
return yield* svc.command({ ...body, sessionID })
970966
}),
@@ -993,11 +989,11 @@ export const SessionRoutes = lazy(() =>
993989
sessionID: SessionID.zod,
994990
}),
995991
),
996-
validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
992+
validator("json", zodObject(SessionPrompt.ShellInput).omit({ sessionID: true })),
997993
async (c) =>
998994
jsonRequest("SessionRoutes.shell", c, function* () {
999995
const sessionID = c.req.valid("param").sessionID
1000-
const body = c.req.valid("json")
996+
const body = c.req.valid("json") as Omit<SessionPrompt.ShellInput, "sessionID">
1001997
const svc = yield* SessionPrompt.Service
1002998
return yield* svc.shell({ ...body, sessionID })
1003999
}),
@@ -1013,7 +1009,7 @@ export const SessionRoutes = lazy(() =>
10131009
description: "Updated session",
10141010
content: {
10151011
"application/json": {
1016-
schema: resolver(Session.Info),
1012+
schema: resolver(Session.Info.zod),
10171013
},
10181014
},
10191015
},
@@ -1026,16 +1022,14 @@ export const SessionRoutes = lazy(() =>
10261022
sessionID: SessionID.zod,
10271023
}),
10281024
),
1029-
validator("json", SessionRevert.RevertInput.omit({ sessionID: true })),
1025+
validator("json", zodObject(SessionRevert.RevertInput).omit({ sessionID: true })),
10301026
async (c) => {
10311027
const sessionID = c.req.valid("param").sessionID
1032-
log.info("revert", c.req.valid("json"))
1028+
const body = c.req.valid("json") as Omit<SessionRevert.RevertInput, "sessionID">
1029+
log.info("revert", body)
10331030
return jsonRequest("SessionRoutes.revert", c, function* () {
10341031
const svc = yield* SessionRevert.Service
1035-
return yield* svc.revert({
1036-
sessionID,
1037-
...c.req.valid("json"),
1038-
})
1032+
return yield* svc.revert({ sessionID, ...body })
10391033
})
10401034
},
10411035
)
@@ -1050,7 +1044,7 @@ export const SessionRoutes = lazy(() =>
10501044
description: "Updated session",
10511045
content: {
10521046
"application/json": {
1053-
schema: resolver(Session.Info),
1047+
schema: resolver(Session.Info.zod),
10541048
},
10551049
},
10561050
},

0 commit comments

Comments
 (0)