summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-17 16:52:40 -0400
committerGitHub <[email protected]>2026-04-17 16:52:40 -0400
commit999d8651aab7f9531539f686f685375fcbb19437 (patch)
tree867fffafc556026c17b0cb87cd27299a7991a2e9
parented0f0225025336ad84c04210850fcd7bfb38f221 (diff)
downloadopencode-999d8651aab7f9531539f686f685375fcbb19437.tar.gz
opencode-999d8651aab7f9531539f686f685375fcbb19437.zip
feat(server): wrap remaining route handlers in request spans (#23169)
-rw-r--r--packages/opencode/src/server/routes/instance/config.ts13
-rw-r--r--packages/opencode/src/server/routes/instance/experimental.ts170
-rw-r--r--packages/opencode/src/server/routes/instance/file.ts88
-rw-r--r--packages/opencode/src/server/routes/instance/index.ts87
-rw-r--r--packages/opencode/src/server/routes/instance/mcp.ts80
-rw-r--r--packages/opencode/src/server/routes/instance/permission.ts37
-rw-r--r--packages/opencode/src/server/routes/instance/project.ts18
-rw-r--r--packages/opencode/src/server/routes/instance/provider.ts117
-rw-r--r--packages/opencode/src/server/routes/instance/pty.ts69
-rw-r--r--packages/opencode/src/server/routes/instance/question.ts47
-rw-r--r--packages/opencode/src/server/routes/instance/session.ts411
-rw-r--r--packages/opencode/src/server/routes/instance/tui.ts8
-rw-r--r--packages/opencode/src/server/workspace.ts5
13 files changed, 550 insertions, 600 deletions
diff --git a/packages/opencode/src/server/routes/instance/config.ts b/packages/opencode/src/server/routes/instance/config.ts
index 235f5682e..7f368cd31 100644
--- a/packages/opencode/src/server/routes/instance/config.ts
+++ b/packages/opencode/src/server/routes/instance/config.ts
@@ -5,7 +5,6 @@ import { Config } from "@/config"
import { Provider } from "@/provider"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
-import { AppRuntime } from "@/effect/app-runtime"
import { jsonRequest } from "./trace"
export const ConfigRoutes = lazy(() =>
@@ -52,11 +51,13 @@ export const ConfigRoutes = lazy(() =>
},
}),
validator("json", Config.Info),
- async (c) => {
- const config = c.req.valid("json")
- await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.update(config)))
- return c.json(config)
- },
+ async (c) =>
+ jsonRequest("ConfigRoutes.update", c, function* () {
+ const config = c.req.valid("json")
+ const cfg = yield* Config.Service
+ yield* cfg.update(config)
+ return config
+ }),
)
.get(
"/providers",
diff --git a/packages/opencode/src/server/routes/instance/experimental.ts b/packages/opencode/src/server/routes/instance/experimental.ts
index d5659346e..9c8649498 100644
--- a/packages/opencode/src/server/routes/instance/experimental.ts
+++ b/packages/opencode/src/server/routes/instance/experimental.ts
@@ -12,11 +12,11 @@ import { Config } from "@/config"
import { ConsoleState } from "@/config/console-state"
import { Account } from "@/account/account"
import { AccountID, OrgID } from "@/account/schema"
-import { AppRuntime } from "@/effect/app-runtime"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Effect, Option } from "effect"
import { Agent } from "@/agent/agent"
+import { jsonRequest, runRequest } from "./trace"
const ConsoleOrgOption = z.object({
accountID: z.string(),
@@ -55,22 +55,18 @@ export const ExperimentalRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const result = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const config = yield* Config.Service
- const account = yield* Account.Service
- const [state, groups] = yield* Effect.all([config.getConsoleState(), account.orgsByAccount()], {
- concurrency: "unbounded",
- })
- return {
- ...state,
- switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0),
- }
- }),
- )
- return c.json(result)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.console.get", c, function* () {
+ const config = yield* Config.Service
+ const account = yield* Account.Service
+ const [state, groups] = yield* Effect.all([config.getConsoleState(), account.orgsByAccount()], {
+ concurrency: "unbounded",
+ })
+ return {
+ ...state,
+ switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0),
+ }
+ }),
)
.get(
"/console/orgs",
@@ -89,28 +85,25 @@ export const ExperimentalRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const orgs = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const account = yield* Account.Service
- const [groups, active] = yield* Effect.all([account.orgsByAccount(), account.active()], {
- concurrency: "unbounded",
- })
- const info = Option.getOrUndefined(active)
- return groups.flatMap((group) =>
- group.orgs.map((org) => ({
- accountID: group.account.id,
- accountEmail: group.account.email,
- accountUrl: group.account.url,
- orgID: org.id,
- orgName: org.name,
- active: !!info && info.id === group.account.id && info.active_org_id === org.id,
- })),
- )
- }),
- )
- return c.json({ orgs })
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.console.listOrgs", c, function* () {
+ const account = yield* Account.Service
+ const [groups, active] = yield* Effect.all([account.orgsByAccount(), account.active()], {
+ concurrency: "unbounded",
+ })
+ const info = Option.getOrUndefined(active)
+ const orgs = groups.flatMap((group) =>
+ group.orgs.map((org) => ({
+ accountID: group.account.id,
+ accountEmail: group.account.email,
+ accountUrl: group.account.url,
+ orgID: org.id,
+ orgName: org.name,
+ active: !!info && info.id === group.account.id && info.active_org_id === org.id,
+ })),
+ )
+ return { orgs }
+ }),
)
.post(
"/console/switch",
@@ -130,16 +123,13 @@ export const ExperimentalRoutes = lazy(() =>
},
}),
validator("json", ConsoleSwitchBody),
- async (c) => {
- const body = c.req.valid("json")
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const account = yield* Account.Service
- yield* account.use(AccountID.make(body.accountID), Option.some(OrgID.make(body.orgID)))
- }),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.console.switchOrg", c, function* () {
+ const body = c.req.valid("json")
+ const account = yield* Account.Service
+ yield* account.use(AccountID.make(body.accountID), Option.some(OrgID.make(body.orgID)))
+ return true
+ }),
)
.get(
"/tool/ids",
@@ -160,15 +150,11 @@ export const ExperimentalRoutes = lazy(() =>
...errors(400),
},
}),
- async (c) => {
- const ids = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const registry = yield* ToolRegistry.Service
- return yield* registry.ids()
- }),
- )
- return c.json(ids)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.tool.ids", c, function* () {
+ const registry = yield* ToolRegistry.Service
+ return yield* registry.ids()
+ }),
)
.get(
"/tool",
@@ -210,7 +196,9 @@ export const ExperimentalRoutes = lazy(() =>
),
async (c) => {
const { provider, model } = c.req.valid("query")
- const tools = await AppRuntime.runPromise(
+ const tools = await runRequest(
+ "ExperimentalRoutes.tool.list",
+ c,
Effect.gen(function* () {
const agents = yield* Agent.Service
const registry = yield* ToolRegistry.Service
@@ -249,11 +237,12 @@ export const ExperimentalRoutes = lazy(() =>
},
}),
validator("json", Worktree.CreateInput.optional()),
- async (c) => {
- const body = c.req.valid("json")
- const worktree = await AppRuntime.runPromise(Worktree.Service.use((svc) => svc.create(body)))
- return c.json(worktree)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.worktree.create", c, function* () {
+ const body = c.req.valid("json")
+ const svc = yield* Worktree.Service
+ return yield* svc.create(body)
+ }),
)
.get(
"/worktree",
@@ -272,10 +261,11 @@ export const ExperimentalRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const sandboxes = await AppRuntime.runPromise(Project.Service.use((svc) => svc.sandboxes(Instance.project.id)))
- return c.json(sandboxes)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.worktree.list", c, function* () {
+ const svc = yield* Project.Service
+ return yield* svc.sandboxes(Instance.project.id)
+ }),
)
.delete(
"/worktree",
@@ -296,14 +286,15 @@ export const ExperimentalRoutes = lazy(() =>
},
}),
validator("json", Worktree.RemoveInput),
- async (c) => {
- const body = c.req.valid("json")
- await AppRuntime.runPromise(Worktree.Service.use((svc) => svc.remove(body)))
- await AppRuntime.runPromise(
- Project.Service.use((svc) => svc.removeSandbox(Instance.project.id, body.directory)),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.worktree.remove", c, function* () {
+ const body = c.req.valid("json")
+ const worktree = yield* Worktree.Service
+ const project = yield* Project.Service
+ yield* worktree.remove(body)
+ yield* project.removeSandbox(Instance.project.id, body.directory)
+ return true
+ }),
)
.post(
"/worktree/reset",
@@ -324,11 +315,13 @@ export const ExperimentalRoutes = lazy(() =>
},
}),
validator("json", Worktree.ResetInput),
- async (c) => {
- const body = c.req.valid("json")
- await AppRuntime.runPromise(Worktree.Service.use((svc) => svc.reset(body)))
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.worktree.reset", c, function* () {
+ const body = c.req.valid("json")
+ const svc = yield* Worktree.Service
+ yield* svc.reset(body)
+ return true
+ }),
)
.get(
"/session",
@@ -406,15 +399,10 @@ export const ExperimentalRoutes = lazy(() =>
},
},
}),
- async (c) => {
- return c.json(
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const mcp = yield* MCP.Service
- return yield* mcp.resources()
- }),
- ),
- )
- },
+ async (c) =>
+ jsonRequest("ExperimentalRoutes.resource.list", c, function* () {
+ const mcp = yield* MCP.Service
+ return yield* mcp.resources()
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/file.ts b/packages/opencode/src/server/routes/instance/file.ts
index a82e5687d..bbef679a8 100644
--- a/packages/opencode/src/server/routes/instance/file.ts
+++ b/packages/opencode/src/server/routes/instance/file.ts
@@ -1,13 +1,12 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
-import { Effect } from "effect"
import z from "zod"
-import { AppRuntime } from "@/effect/app-runtime"
import { File } from "@/file"
import { Ripgrep } from "@/file/ripgrep"
import { LSP } from "@/lsp"
import { Instance } from "@/project/instance"
import { lazy } from "@/util/lazy"
+import { jsonRequest } from "./trace"
export const FileRoutes = lazy(() =>
new Hono()
@@ -34,13 +33,13 @@ export const FileRoutes = lazy(() =>
pattern: z.string(),
}),
),
- async (c) => {
- const pattern = c.req.valid("query").pattern
- const result = await AppRuntime.runPromise(
- Ripgrep.Service.use((svc) => svc.search({ cwd: Instance.directory, pattern, limit: 10 })),
- )
- return c.json(result.items)
- },
+ async (c) =>
+ jsonRequest("FileRoutes.findText", c, function* () {
+ const pattern = c.req.valid("query").pattern
+ const svc = yield* Ripgrep.Service
+ const result = yield* svc.search({ cwd: Instance.directory, pattern, limit: 10 })
+ return result.items
+ }),
)
.get(
"/find/file",
@@ -68,25 +67,17 @@ export const FileRoutes = lazy(() =>
limit: z.coerce.number().int().min(1).max(200).optional(),
}),
),
- async (c) => {
- const query = c.req.valid("query").query
- const dirs = c.req.valid("query").dirs
- const type = c.req.valid("query").type
- const limit = c.req.valid("query").limit
- const results = await AppRuntime.runPromise(
- Effect.gen(function* () {
- return yield* File.Service.use((svc) =>
- svc.search({
- query,
- limit: limit ?? 10,
- dirs: dirs !== "false",
- type,
- }),
- )
- }),
- )
- return c.json(results)
- },
+ async (c) =>
+ jsonRequest("FileRoutes.findFile", c, function* () {
+ const query = c.req.valid("query")
+ const svc = yield* File.Service
+ return yield* svc.search({
+ query: query.query,
+ limit: query.limit ?? 10,
+ dirs: query.dirs !== "false",
+ type: query.type,
+ })
+ }),
)
.get(
"/find/symbol",
@@ -138,15 +129,11 @@ export const FileRoutes = lazy(() =>
path: z.string(),
}),
),
- async (c) => {
- const path = c.req.valid("query").path
- const content = await AppRuntime.runPromise(
- Effect.gen(function* () {
- return yield* File.Service.use((svc) => svc.list(path))
- }),
- )
- return c.json(content)
- },
+ async (c) =>
+ jsonRequest("FileRoutes.list", c, function* () {
+ const svc = yield* File.Service
+ return yield* svc.list(c.req.valid("query").path)
+ }),
)
.get(
"/file/content",
@@ -171,15 +158,11 @@ export const FileRoutes = lazy(() =>
path: z.string(),
}),
),
- async (c) => {
- const path = c.req.valid("query").path
- const content = await AppRuntime.runPromise(
- Effect.gen(function* () {
- return yield* File.Service.use((svc) => svc.read(path))
- }),
- )
- return c.json(content)
- },
+ async (c) =>
+ jsonRequest("FileRoutes.read", c, function* () {
+ const svc = yield* File.Service
+ return yield* svc.read(c.req.valid("query").path)
+ }),
)
.get(
"/file/status",
@@ -198,13 +181,10 @@ export const FileRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const content = await AppRuntime.runPromise(
- Effect.gen(function* () {
- return yield* File.Service.use((svc) => svc.status())
- }),
- )
- return c.json(content)
- },
+ async (c) =>
+ jsonRequest("FileRoutes.status", c, function* () {
+ const svc = yield* File.Service
+ return yield* svc.status()
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts
index ddb7e335a..5cc51d27a 100644
--- a/packages/opencode/src/server/routes/instance/index.ts
+++ b/packages/opencode/src/server/routes/instance/index.ts
@@ -26,8 +26,8 @@ import { ExperimentalRoutes } from "./experimental"
import { ProviderRoutes } from "./provider"
import { EventRoutes } from "./event"
import { SyncRoutes } from "./sync"
-import { AppRuntime } from "@/effect/app-runtime"
import { InstanceMiddleware } from "./middleware"
+import { jsonRequest } from "./trace"
export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
const app = new Hono()
@@ -141,19 +141,14 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
},
},
}),
- async (c) => {
- return c.json(
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const vcs = yield* Vcs.Service
- const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], {
- concurrency: 2,
- })
- return { branch, default_branch }
- }),
- ),
- )
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.vcs.get", c, function* () {
+ const vcs = yield* Vcs.Service
+ const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], {
+ concurrency: 2,
+ })
+ return { branch, default_branch }
+ }),
)
.get(
"/vcs/diff",
@@ -178,16 +173,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
mode: Vcs.Mode,
}),
),
- async (c) => {
- return c.json(
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const vcs = yield* Vcs.Service
- return yield* vcs.diff(c.req.valid("query").mode)
- }),
- ),
- )
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.vcs.diff", c, function* () {
+ const vcs = yield* Vcs.Service
+ return yield* vcs.diff(c.req.valid("query").mode)
+ }),
)
.get(
"/command",
@@ -206,10 +196,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
},
},
}),
- async (c) => {
- const commands = await AppRuntime.runPromise(Command.Service.use((svc) => svc.list()))
- return c.json(commands)
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.command.list", c, function* () {
+ const svc = yield* Command.Service
+ return yield* svc.list()
+ }),
)
.get(
"/agent",
@@ -228,10 +219,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
},
},
}),
- async (c) => {
- const modes = await AppRuntime.runPromise(Agent.Service.use((svc) => svc.list()))
- return c.json(modes)
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.agent.list", c, function* () {
+ const svc = yield* Agent.Service
+ return yield* svc.list()
+ }),
)
.get(
"/skill",
@@ -250,15 +242,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
},
},
}),
- async (c) => {
- const skills = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const skill = yield* Skill.Service
- return yield* skill.all()
- }),
- )
- return c.json(skills)
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.skill.list", c, function* () {
+ const skill = yield* Skill.Service
+ return yield* skill.all()
+ }),
)
.get(
"/lsp",
@@ -277,10 +265,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
},
},
}),
- async (c) => {
- const items = await AppRuntime.runPromise(LSP.Service.use((lsp) => lsp.status()))
- return c.json(items)
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.lsp.status", c, function* () {
+ const lsp = yield* LSP.Service
+ return yield* lsp.status()
+ }),
)
.get(
"/formatter",
@@ -299,8 +288,10 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
},
},
}),
- async (c) => {
- return c.json(await AppRuntime.runPromise(Format.Service.use((svc) => svc.status())))
- },
+ async (c) =>
+ jsonRequest("InstanceRoutes.formatter.status", c, function* () {
+ const svc = yield* Format.Service
+ return yield* svc.status()
+ }),
)
}
diff --git a/packages/opencode/src/server/routes/instance/mcp.ts b/packages/opencode/src/server/routes/instance/mcp.ts
index b42cfb531..ce4722933 100644
--- a/packages/opencode/src/server/routes/instance/mcp.ts
+++ b/packages/opencode/src/server/routes/instance/mcp.ts
@@ -2,12 +2,11 @@ import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { MCP } from "@/mcp"
-import { Config } from "@/config"
import { ConfigMCP } from "@/config/mcp"
-import { AppRuntime } from "@/effect/app-runtime"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Effect } from "effect"
+import { jsonRequest, runRequest } from "./trace"
export const McpRoutes = lazy(() =>
new Hono()
@@ -28,9 +27,11 @@ export const McpRoutes = lazy(() =>
},
},
}),
- async (c) => {
- return c.json(await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.status())))
- },
+ async (c) =>
+ jsonRequest("McpRoutes.status", c, function* () {
+ const mcp = yield* MCP.Service
+ return yield* mcp.status()
+ }),
)
.post(
"/",
@@ -57,11 +58,13 @@ export const McpRoutes = lazy(() =>
config: ConfigMCP.Info.zod,
}),
),
- async (c) => {
- const { name, config } = c.req.valid("json")
- const result = await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.add(name, config)))
- return c.json(result.status)
- },
+ async (c) =>
+ jsonRequest("McpRoutes.add", c, function* () {
+ const { name, config } = c.req.valid("json")
+ const mcp = yield* MCP.Service
+ const result = yield* mcp.add(name, config)
+ return result.status
+ }),
)
.post(
"/:name/auth",
@@ -87,7 +90,9 @@ export const McpRoutes = lazy(() =>
}),
async (c) => {
const name = c.req.param("name")
- const result = await AppRuntime.runPromise(
+ const result = await runRequest(
+ "McpRoutes.auth.start",
+ c,
Effect.gen(function* () {
const mcp = yield* MCP.Service
const supports = yield* mcp.supportsOAuth(name)
@@ -129,12 +134,13 @@ export const McpRoutes = lazy(() =>
code: z.string().describe("Authorization code from OAuth callback"),
}),
),
- async (c) => {
- const name = c.req.param("name")
- const { code } = c.req.valid("json")
- const status = await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.finishAuth(name, code)))
- return c.json(status)
- },
+ async (c) =>
+ jsonRequest("McpRoutes.auth.callback", c, function* () {
+ const name = c.req.param("name")
+ const { code } = c.req.valid("json")
+ const mcp = yield* MCP.Service
+ return yield* mcp.finishAuth(name, code)
+ }),
)
.post(
"/:name/auth/authenticate",
@@ -156,7 +162,9 @@ export const McpRoutes = lazy(() =>
}),
async (c) => {
const name = c.req.param("name")
- const result = await AppRuntime.runPromise(
+ const result = await runRequest(
+ "McpRoutes.auth.authenticate",
+ c,
Effect.gen(function* () {
const mcp = yield* MCP.Service
const supports = yield* mcp.supportsOAuth(name)
@@ -191,11 +199,13 @@ export const McpRoutes = lazy(() =>
...errors(404),
},
}),
- async (c) => {
- const name = c.req.param("name")
- await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.removeAuth(name)))
- return c.json({ success: true as const })
- },
+ async (c) =>
+ jsonRequest("McpRoutes.auth.remove", c, function* () {
+ const name = c.req.param("name")
+ const mcp = yield* MCP.Service
+ yield* mcp.removeAuth(name)
+ return { success: true as const }
+ }),
)
.post(
"/:name/connect",
@@ -214,11 +224,13 @@ export const McpRoutes = lazy(() =>
},
}),
validator("param", z.object({ name: z.string() })),
- async (c) => {
- const { name } = c.req.valid("param")
- await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.connect(name)))
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("McpRoutes.connect", c, function* () {
+ const { name } = c.req.valid("param")
+ const mcp = yield* MCP.Service
+ yield* mcp.connect(name)
+ return true
+ }),
)
.post(
"/:name/disconnect",
@@ -237,10 +249,12 @@ export const McpRoutes = lazy(() =>
},
}),
validator("param", z.object({ name: z.string() })),
- async (c) => {
- const { name } = c.req.valid("param")
- await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.disconnect(name)))
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("McpRoutes.disconnect", c, function* () {
+ const { name } = c.req.valid("param")
+ const mcp = yield* MCP.Service
+ yield* mcp.disconnect(name)
+ return true
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/permission.ts b/packages/opencode/src/server/routes/instance/permission.ts
index c3f9c8201..c18f4734b 100644
--- a/packages/opencode/src/server/routes/instance/permission.ts
+++ b/packages/opencode/src/server/routes/instance/permission.ts
@@ -1,11 +1,11 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
-import { AppRuntime } from "@/effect/app-runtime"
import { Permission } from "@/permission"
import { PermissionID } from "@/permission/schema"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
+import { jsonRequest } from "./trace"
export const PermissionRoutes = lazy(() =>
new Hono()
@@ -34,20 +34,18 @@ export const PermissionRoutes = lazy(() =>
}),
),
validator("json", z.object({ reply: Permission.Reply.zod, message: z.string().optional() })),
- async (c) => {
- const params = c.req.valid("param")
- const json = c.req.valid("json")
- await AppRuntime.runPromise(
- Permission.Service.use((svc) =>
- svc.reply({
- requestID: params.requestID,
- reply: json.reply,
- message: json.message,
- }),
- ),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("PermissionRoutes.reply", c, function* () {
+ const params = c.req.valid("param")
+ const json = c.req.valid("json")
+ const svc = yield* Permission.Service
+ yield* svc.reply({
+ requestID: params.requestID,
+ reply: json.reply,
+ message: json.message,
+ })
+ return true
+ }),
)
.get(
"/",
@@ -66,9 +64,10 @@ export const PermissionRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const permissions = await AppRuntime.runPromise(Permission.Service.use((svc) => svc.list()))
- return c.json(permissions)
- },
+ async (c) =>
+ jsonRequest("PermissionRoutes.list", c, function* () {
+ const svc = yield* Permission.Service
+ return yield* svc.list()
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/project.ts b/packages/opencode/src/server/routes/instance/project.ts
index 060542c4b..5acef6d78 100644
--- a/packages/opencode/src/server/routes/instance/project.ts
+++ b/packages/opencode/src/server/routes/instance/project.ts
@@ -9,6 +9,7 @@ import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { InstanceBootstrap } from "@/project/bootstrap"
import { AppRuntime } from "@/effect/app-runtime"
+import { jsonRequest, runRequest } from "./trace"
export const ProjectRoutes = lazy(() =>
new Hono()
@@ -75,7 +76,9 @@ export const ProjectRoutes = lazy(() =>
async (c) => {
const dir = Instance.directory
const prev = Instance.project
- const next = await AppRuntime.runPromise(
+ const next = await runRequest(
+ "ProjectRoutes.initGit",
+ c,
Project.Service.use((svc) => svc.initGit({ directory: dir, project: prev })),
)
if (next.id === prev.id && next.vcs === prev.vcs && next.worktree === prev.worktree) return c.json(next)
@@ -108,11 +111,12 @@ export const ProjectRoutes = lazy(() =>
}),
validator("param", z.object({ projectID: ProjectID.zod })),
validator("json", Project.UpdateInput.omit({ projectID: true })),
- async (c) => {
- const projectID = c.req.valid("param").projectID
- const body = c.req.valid("json")
- const project = await AppRuntime.runPromise(Project.Service.use((svc) => svc.update({ ...body, projectID })))
- return c.json(project)
- },
+ async (c) =>
+ jsonRequest("ProjectRoutes.update", c, function* () {
+ const projectID = c.req.valid("param").projectID
+ const body = c.req.valid("json")
+ const svc = yield* Project.Service
+ return yield* svc.update({ ...body, projectID })
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/provider.ts b/packages/opencode/src/server/routes/instance/provider.ts
index 57aa895e3..617980e39 100644
--- a/packages/opencode/src/server/routes/instance/provider.ts
+++ b/packages/opencode/src/server/routes/instance/provider.ts
@@ -6,11 +6,11 @@ import { Provider } from "@/provider"
import { ModelsDev } from "@/provider"
import { ProviderAuth } from "@/provider"
import { ProviderID } from "@/provider/schema"
-import { AppRuntime } from "@/effect/app-runtime"
import { mapValues } from "remeda"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Effect } from "effect"
+import { jsonRequest } from "./trace"
export const ProviderRoutes = lazy(() =>
new Hono()
@@ -31,39 +31,31 @@ export const ProviderRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const result = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const svc = yield* Provider.Service
- const cfg = yield* Config.Service
- const config = yield* cfg.get()
- const all = yield* Effect.promise(() => ModelsDev.get())
- const disabled = new Set(config.disabled_providers ?? [])
- const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
- const filtered: Record<string, (typeof all)[string]> = {}
- for (const [key, value] of Object.entries(all)) {
- if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) {
- filtered[key] = value
- }
+ async (c) =>
+ jsonRequest("ProviderRoutes.list", c, function* () {
+ const svc = yield* Provider.Service
+ const cfg = yield* Config.Service
+ const config = yield* cfg.get()
+ const all = yield* Effect.promise(() => ModelsDev.get())
+ const disabled = new Set(config.disabled_providers ?? [])
+ const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
+ const filtered: Record<string, (typeof all)[string]> = {}
+ for (const [key, value] of Object.entries(all)) {
+ if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) {
+ filtered[key] = value
}
- const connected = yield* svc.list()
- const providers = Object.assign(
- mapValues(filtered, (x) => Provider.fromModelsDevProvider(x)),
- connected,
- )
- return {
- all: Object.values(providers),
- default: Provider.defaultModelIDs(providers),
- connected: Object.keys(connected),
- }
- }),
- )
- return c.json({
- all: result.all,
- default: result.default,
- connected: result.connected,
- })
- },
+ }
+ const connected = yield* svc.list()
+ const providers = Object.assign(
+ mapValues(filtered, (x) => Provider.fromModelsDevProvider(x)),
+ connected,
+ )
+ return {
+ all: Object.values(providers),
+ default: Provider.defaultModelIDs(providers),
+ connected: Object.keys(connected),
+ }
+ }),
)
.get(
"/auth",
@@ -82,9 +74,11 @@ export const ProviderRoutes = lazy(() =>
},
},
}),
- async (c) => {
- return c.json(await AppRuntime.runPromise(ProviderAuth.Service.use((svc) => svc.methods())))
- },
+ async (c) =>
+ jsonRequest("ProviderRoutes.auth", c, function* () {
+ const svc = yield* ProviderAuth.Service
+ return yield* svc.methods()
+ }),
)
.post(
"/:providerID/oauth/authorize",
@@ -111,20 +105,17 @@ export const ProviderRoutes = lazy(() =>
}),
),
validator("json", ProviderAuth.AuthorizeInput.zod),
- async (c) => {
- const providerID = c.req.valid("param").providerID
- const { method, inputs } = c.req.valid("json")
- const result = await AppRuntime.runPromise(
- ProviderAuth.Service.use((svc) =>
- svc.authorize({
- providerID,
- method,
- inputs,
- }),
- ),
- )
- return c.json(result)
- },
+ async (c) =>
+ jsonRequest("ProviderRoutes.oauth.authorize", c, function* () {
+ const providerID = c.req.valid("param").providerID
+ const { method, inputs } = c.req.valid("json")
+ const svc = yield* ProviderAuth.Service
+ return yield* svc.authorize({
+ providerID,
+ method,
+ inputs,
+ })
+ }),
)
.post(
"/:providerID/oauth/callback",
@@ -151,19 +142,17 @@ export const ProviderRoutes = lazy(() =>
}),
),
validator("json", ProviderAuth.CallbackInput.zod),
- async (c) => {
- const providerID = c.req.valid("param").providerID
- const { method, code } = c.req.valid("json")
- await AppRuntime.runPromise(
- ProviderAuth.Service.use((svc) =>
- svc.callback({
- providerID,
- method,
- code,
- }),
- ),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("ProviderRoutes.oauth.callback", c, function* () {
+ const providerID = c.req.valid("param").providerID
+ const { method, code } = c.req.valid("json")
+ const svc = yield* ProviderAuth.Service
+ yield* svc.callback({
+ providerID,
+ method,
+ code,
+ })
+ return true
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/pty.ts b/packages/opencode/src/server/routes/instance/pty.ts
index b3f71c235..a25b66e9f 100644
--- a/packages/opencode/src/server/routes/instance/pty.ts
+++ b/packages/opencode/src/server/routes/instance/pty.ts
@@ -8,6 +8,7 @@ import { Pty } from "@/pty"
import { PtyID } from "@/pty/schema"
import { NotFoundError } from "@/storage"
import { errors } from "../../error"
+import { jsonRequest, runRequest } from "./trace"
export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
return new Hono()
@@ -28,16 +29,11 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
},
},
}),
- async (c) => {
- return c.json(
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const pty = yield* Pty.Service
- return yield* pty.list()
- }),
- ),
- )
- },
+ async (c) =>
+ jsonRequest("PtyRoutes.list", c, function* () {
+ const pty = yield* Pty.Service
+ return yield* pty.list()
+ }),
)
.post(
"/",
@@ -58,15 +54,11 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
},
}),
validator("json", Pty.CreateInput),
- async (c) => {
- const info = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const pty = yield* Pty.Service
- return yield* pty.create(c.req.valid("json"))
- }),
- )
- return c.json(info)
- },
+ async (c) =>
+ jsonRequest("PtyRoutes.create", c, function* () {
+ const pty = yield* Pty.Service
+ return yield* pty.create(c.req.valid("json"))
+ }),
)
.get(
"/:ptyID",
@@ -88,7 +80,9 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
}),
validator("param", z.object({ ptyID: PtyID.zod })),
async (c) => {
- const info = await AppRuntime.runPromise(
+ const info = await runRequest(
+ "PtyRoutes.get",
+ c,
Effect.gen(function* () {
const pty = yield* Pty.Service
return yield* pty.get(c.req.valid("param").ptyID)
@@ -120,15 +114,11 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
}),
validator("param", z.object({ ptyID: PtyID.zod })),
validator("json", Pty.UpdateInput),
- async (c) => {
- const info = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const pty = yield* Pty.Service
- return yield* pty.update(c.req.valid("param").ptyID, c.req.valid("json"))
- }),
- )
- return c.json(info)
- },
+ async (c) =>
+ jsonRequest("PtyRoutes.update", c, function* () {
+ const pty = yield* Pty.Service
+ return yield* pty.update(c.req.valid("param").ptyID, c.req.valid("json"))
+ }),
)
.delete(
"/:ptyID",
@@ -149,15 +139,12 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
},
}),
validator("param", z.object({ ptyID: PtyID.zod })),
- async (c) => {
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const pty = yield* Pty.Service
- yield* pty.remove(c.req.valid("param").ptyID)
- }),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("PtyRoutes.remove", c, function* () {
+ const pty = yield* Pty.Service
+ yield* pty.remove(c.req.valid("param").ptyID)
+ return true
+ }),
)
.get(
"/:ptyID/connect",
@@ -194,7 +181,9 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
})()
let handler: Handler | undefined
if (
- !(await AppRuntime.runPromise(
+ !(await runRequest(
+ "PtyRoutes.connect",
+ c,
Effect.gen(function* () {
const pty = yield* Pty.Service
return yield* pty.get(id)
@@ -232,7 +221,7 @@ export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
Effect.gen(function* () {
const pty = yield* Pty.Service
return yield* pty.connect(id, socket, cursor)
- }),
+ }).pipe(Effect.withSpan("PtyRoutes.connect.open")),
)
ready = true
for (const msg of pending) handler?.onMessage(msg)
diff --git a/packages/opencode/src/server/routes/instance/question.ts b/packages/opencode/src/server/routes/instance/question.ts
index 9b8f461e3..51ecb48cc 100644
--- a/packages/opencode/src/server/routes/instance/question.ts
+++ b/packages/opencode/src/server/routes/instance/question.ts
@@ -3,10 +3,10 @@ import { describeRoute, validator } from "hono-openapi"
import { resolver } from "hono-openapi"
import { QuestionID } from "@/question/schema"
import { Question } from "@/question"
-import { AppRuntime } from "@/effect/app-runtime"
import z from "zod"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
+import { jsonRequest } from "./trace"
const Reply = z.object({
answers: Question.Answer.zod
@@ -33,10 +33,11 @@ export const QuestionRoutes = lazy(() =>
},
},
}),
- async (c) => {
- const questions = await AppRuntime.runPromise(Question.Service.use((svc) => svc.list()))
- return c.json(questions)
- },
+ async (c) =>
+ jsonRequest("QuestionRoutes.list", c, function* () {
+ const svc = yield* Question.Service
+ return yield* svc.list()
+ }),
)
.post(
"/:requestID/reply",
@@ -63,19 +64,17 @@ export const QuestionRoutes = lazy(() =>
}),
),
validator("json", Reply),
- async (c) => {
- const params = c.req.valid("param")
- const json = c.req.valid("json")
- await AppRuntime.runPromise(
- Question.Service.use((svc) =>
- svc.reply({
- requestID: params.requestID,
- answers: json.answers,
- }),
- ),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("QuestionRoutes.reply", c, function* () {
+ const params = c.req.valid("param")
+ const json = c.req.valid("json")
+ const svc = yield* Question.Service
+ yield* svc.reply({
+ requestID: params.requestID,
+ answers: json.answers,
+ })
+ return true
+ }),
)
.post(
"/:requestID/reject",
@@ -101,10 +100,12 @@ export const QuestionRoutes = lazy(() =>
requestID: QuestionID.zod,
}),
),
- async (c) => {
- const params = c.req.valid("param")
- await AppRuntime.runPromise(Question.Service.use((svc) => svc.reject(params.requestID)))
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("QuestionRoutes.reject", c, function* () {
+ const params = c.req.valid("param")
+ const svc = yield* Question.Service
+ yield* svc.reject(params.requestID)
+ return true
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/session.ts b/packages/opencode/src/server/routes/instance/session.ts
index ae6185abb..bf713935b 100644
--- a/packages/opencode/src/server/routes/instance/session.ts
+++ b/packages/opencode/src/server/routes/instance/session.ts
@@ -14,7 +14,6 @@ import { SessionStatus } from "@/session/status"
import { SessionSummary } from "@/session/summary"
import { Todo } from "@/session/todo"
import { Effect } from "effect"
-import { AppRuntime } from "@/effect/app-runtime"
import { Agent } from "@/agent/agent"
import { Snapshot } from "@/snapshot"
import { Command } from "@/command"
@@ -26,7 +25,7 @@ import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Bus } from "@/bus"
import { NamedError } from "@opencode-ai/shared/util/error"
-import { jsonRequest } from "./trace"
+import { jsonRequest, runRequest } from "./trace"
const log = Log.create({ service: "server" })
@@ -218,11 +217,12 @@ export const SessionRoutes = lazy(() =>
},
}),
validator("json", Session.CreateInput),
- async (c) => {
- const body = c.req.valid("json") ?? {}
- const session = await AppRuntime.runPromise(SessionShare.Service.use((svc) => svc.create(body)))
- return c.json(session)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.create", c, function* () {
+ const body = c.req.valid("json") ?? {}
+ const svc = yield* SessionShare.Service
+ return yield* svc.create(body)
+ }),
)
.delete(
"/:sessionID",
@@ -248,11 +248,13 @@ export const SessionRoutes = lazy(() =>
sessionID: Session.RemoveInput,
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- await AppRuntime.runPromise(Session.Service.use((svc) => svc.remove(sessionID)))
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.delete", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const svc = yield* Session.Service
+ yield* svc.remove(sessionID)
+ return true
+ }),
)
.patch(
"/:sessionID",
@@ -290,32 +292,28 @@ export const SessionRoutes = lazy(() =>
.optional(),
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const updates = c.req.valid("json")
- const session = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const session = yield* Session.Service
- const current = yield* session.get(sessionID)
+ async (c) =>
+ jsonRequest("SessionRoutes.update", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const updates = c.req.valid("json")
+ const session = yield* Session.Service
+ const current = yield* session.get(sessionID)
- if (updates.title !== undefined) {
- yield* session.setTitle({ sessionID, title: updates.title })
- }
- if (updates.permission !== undefined) {
- yield* session.setPermission({
- sessionID,
- permission: Permission.merge(current.permission ?? [], updates.permission),
- })
- }
- if (updates.time?.archived !== undefined) {
- yield* session.setArchived({ sessionID, time: updates.time.archived })
- }
+ if (updates.title !== undefined) {
+ yield* session.setTitle({ sessionID, title: updates.title })
+ }
+ if (updates.permission !== undefined) {
+ yield* session.setPermission({
+ sessionID,
+ permission: Permission.merge(current.permission ?? [], updates.permission),
+ })
+ }
+ if (updates.time?.archived !== undefined) {
+ yield* session.setArchived({ sessionID, time: updates.time.archived })
+ }
- return yield* session.get(sessionID)
- }),
- )
- return c.json(session)
- },
+ return yield* session.get(sessionID)
+ }),
)
// TODO(v2): remove this dedicated route and rely on the normal `/init` command flow.
.post(
@@ -351,22 +349,20 @@ export const SessionRoutes = lazy(() =>
messageID: MessageID.zod,
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const body = c.req.valid("json")
- await AppRuntime.runPromise(
- SessionPrompt.Service.use((svc) =>
- svc.command({
- sessionID,
- messageID: body.messageID,
- model: body.providerID + "/" + body.modelID,
- command: Command.Default.INIT,
- arguments: "",
- }),
- ),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.init", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const body = c.req.valid("json")
+ const svc = yield* SessionPrompt.Service
+ yield* svc.command({
+ sessionID,
+ messageID: body.messageID,
+ model: body.providerID + "/" + body.modelID,
+ command: Command.Default.INIT,
+ arguments: "",
+ })
+ return true
+ }),
)
.post(
"/:sessionID/fork",
@@ -392,12 +388,13 @@ export const SessionRoutes = lazy(() =>
}),
),
validator("json", Session.ForkInput.omit({ sessionID: true })),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const body = c.req.valid("json")
- const result = await AppRuntime.runPromise(Session.Service.use((svc) => svc.fork({ ...body, sessionID })))
- return c.json(result)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.fork", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const body = c.req.valid("json")
+ const svc = yield* Session.Service
+ return yield* svc.fork({ ...body, sessionID })
+ }),
)
.post(
"/:sessionID/abort",
@@ -423,10 +420,12 @@ export const SessionRoutes = lazy(() =>
sessionID: SessionID.zod,
}),
),
- async (c) => {
- await AppRuntime.runPromise(SessionPrompt.Service.use((svc) => svc.cancel(c.req.valid("param").sessionID)))
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.abort", c, function* () {
+ const svc = yield* SessionPrompt.Service
+ yield* svc.cancel(c.req.valid("param").sessionID)
+ return true
+ }),
)
.post(
"/:sessionID/share",
@@ -452,18 +451,14 @@ export const SessionRoutes = lazy(() =>
sessionID: SessionID.zod,
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const session = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const share = yield* SessionShare.Service
- const session = yield* Session.Service
- yield* share.share(sessionID)
- return yield* session.get(sessionID)
- }),
- )
- return c.json(session)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.share", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const share = yield* SessionShare.Service
+ const session = yield* Session.Service
+ yield* share.share(sessionID)
+ return yield* session.get(sessionID)
+ }),
)
.get(
"/:sessionID/diff",
@@ -494,19 +489,16 @@ export const SessionRoutes = lazy(() =>
messageID: SessionSummary.DiffInput.shape.messageID,
}),
),
- async (c) => {
- const query = c.req.valid("query")
- const params = c.req.valid("param")
- const result = await AppRuntime.runPromise(
- SessionSummary.Service.use((summary) =>
- summary.diff({
- sessionID: params.sessionID,
- messageID: query.messageID,
- }),
- ),
- )
- return c.json(result)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.diff", c, function* () {
+ const query = c.req.valid("query")
+ const params = c.req.valid("param")
+ const summary = yield* SessionSummary.Service
+ return yield* summary.diff({
+ sessionID: params.sessionID,
+ messageID: query.messageID,
+ })
+ }),
)
.delete(
"/:sessionID/share",
@@ -532,18 +524,14 @@ export const SessionRoutes = lazy(() =>
sessionID: SessionID.zod,
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const session = await AppRuntime.runPromise(
- Effect.gen(function* () {
- const share = yield* SessionShare.Service
- const session = yield* Session.Service
- yield* share.unshare(sessionID)
- return yield* session.get(sessionID)
- }),
- )
- return c.json(session)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.unshare", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const share = yield* SessionShare.Service
+ const session = yield* Session.Service
+ yield* share.unshare(sessionID)
+ return yield* session.get(sessionID)
+ }),
)
.post(
"/:sessionID/summarize",
@@ -577,43 +565,40 @@ export const SessionRoutes = lazy(() =>
auto: z.boolean().optional().default(false),
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const body = c.req.valid("json")
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const session = yield* Session.Service
- const revert = yield* SessionRevert.Service
- const compact = yield* SessionCompaction.Service
- const prompt = yield* SessionPrompt.Service
- const agent = yield* Agent.Service
+ async (c) =>
+ jsonRequest("SessionRoutes.summarize", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const body = c.req.valid("json")
+ const session = yield* Session.Service
+ const revert = yield* SessionRevert.Service
+ const compact = yield* SessionCompaction.Service
+ const prompt = yield* SessionPrompt.Service
+ const agent = yield* Agent.Service
- yield* revert.cleanup(yield* session.get(sessionID))
- const msgs = yield* session.messages({ sessionID })
- const defaultAgent = yield* agent.defaultAgent()
- let currentAgent = defaultAgent
- for (let i = msgs.length - 1; i >= 0; i--) {
- const info = msgs[i].info
- if (info.role === "user") {
- currentAgent = info.agent || defaultAgent
- break
- }
+ yield* revert.cleanup(yield* session.get(sessionID))
+ const msgs = yield* session.messages({ sessionID })
+ const defaultAgent = yield* agent.defaultAgent()
+ let currentAgent = defaultAgent
+ for (let i = msgs.length - 1; i >= 0; i--) {
+ const info = msgs[i].info
+ if (info.role === "user") {
+ currentAgent = info.agent || defaultAgent
+ break
}
+ }
- yield* compact.create({
- sessionID,
- agent: currentAgent,
- model: {
- providerID: body.providerID,
- modelID: body.modelID,
- },
- auto: body.auto,
- })
- yield* prompt.loop({ sessionID })
- }),
- )
- return c.json(true)
- },
+ yield* compact.create({
+ sessionID,
+ agent: currentAgent,
+ model: {
+ providerID: body.providerID,
+ modelID: body.modelID,
+ },
+ auto: body.auto,
+ })
+ yield* prompt.loop({ sessionID })
+ return true
+ }),
)
.get(
"/:sessionID/message",
@@ -675,7 +660,9 @@ export const SessionRoutes = lazy(() =>
const query = c.req.valid("query")
const sessionID = c.req.valid("param").sessionID
if (query.limit === undefined || query.limit === 0) {
- const messages = await AppRuntime.runPromise(
+ const messages = await runRequest(
+ "SessionRoutes.messages",
+ c,
Effect.gen(function* () {
const session = yield* Session.Service
yield* session.get(sessionID)
@@ -766,21 +753,18 @@ export const SessionRoutes = lazy(() =>
messageID: MessageID.zod,
}),
),
- async (c) => {
- const params = c.req.valid("param")
- await AppRuntime.runPromise(
- Effect.gen(function* () {
- const state = yield* SessionRunState.Service
- const session = yield* Session.Service
- yield* state.assertNotBusy(params.sessionID)
- yield* session.removeMessage({
- sessionID: params.sessionID,
- messageID: params.messageID,
- })
- }),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.deleteMessage", c, function* () {
+ const params = c.req.valid("param")
+ const state = yield* SessionRunState.Service
+ const session = yield* Session.Service
+ yield* state.assertNotBusy(params.sessionID)
+ yield* session.removeMessage({
+ sessionID: params.sessionID,
+ messageID: params.messageID,
+ })
+ return true
+ }),
)
.delete(
"/:sessionID/message/:messageID/part/:partID",
@@ -807,19 +791,17 @@ export const SessionRoutes = lazy(() =>
partID: PartID.zod,
}),
),
- async (c) => {
- const params = c.req.valid("param")
- await AppRuntime.runPromise(
- Session.Service.use((svc) =>
- svc.removePart({
- sessionID: params.sessionID,
- messageID: params.messageID,
- partID: params.partID,
- }),
- ),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.deletePart", c, function* () {
+ const params = c.req.valid("param")
+ const svc = yield* Session.Service
+ yield* svc.removePart({
+ sessionID: params.sessionID,
+ messageID: params.messageID,
+ partID: params.partID,
+ })
+ return true
+ }),
)
.patch(
"/:sessionID/message/:messageID/part/:partID",
@@ -855,8 +837,10 @@ export const SessionRoutes = lazy(() =>
`Part mismatch: body.id='${body.id}' vs partID='${params.partID}', body.messageID='${body.messageID}' vs messageID='${params.messageID}', body.sessionID='${body.sessionID}' vs sessionID='${params.sessionID}'`,
)
}
- const part = await AppRuntime.runPromise(Session.Service.use((svc) => svc.updatePart(body)))
- return c.json(part)
+ return jsonRequest("SessionRoutes.updatePart", c, function* () {
+ const svc = yield* Session.Service
+ return yield* svc.updatePart(body)
+ })
},
)
.post(
@@ -895,7 +879,9 @@ export const SessionRoutes = lazy(() =>
return stream(c, async (stream) => {
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
- const msg = await AppRuntime.runPromise(
+ const msg = await runRequest(
+ "SessionRoutes.prompt",
+ c,
SessionPrompt.Service.use((svc) => svc.prompt({ ...body, sessionID })),
)
void stream.write(JSON.stringify(msg))
@@ -926,15 +912,17 @@ export const SessionRoutes = lazy(() =>
async (c) => {
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
- void AppRuntime.runPromise(SessionPrompt.Service.use((svc) => svc.prompt({ ...body, sessionID }))).catch(
- (err) => {
- log.error("prompt_async failed", { sessionID, error: err })
- void Bus.publish(Session.Event.Error, {
- sessionID,
- error: new NamedError.Unknown({ message: err instanceof Error ? err.message : String(err) }).toObject(),
- })
- },
- )
+ void runRequest(
+ "SessionRoutes.prompt_async",
+ c,
+ SessionPrompt.Service.use((svc) => svc.prompt({ ...body, sessionID })),
+ ).catch((err) => {
+ log.error("prompt_async failed", { sessionID, error: err })
+ void Bus.publish(Session.Event.Error, {
+ sessionID,
+ error: new NamedError.Unknown({ message: err instanceof Error ? err.message : String(err) }).toObject(),
+ })
+ })
return c.body(null, 204)
},
@@ -969,12 +957,13 @@ export const SessionRoutes = lazy(() =>
}),
),
validator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const body = c.req.valid("json")
- const msg = await AppRuntime.runPromise(SessionPrompt.Service.use((svc) => svc.command({ ...body, sessionID })))
- return c.json(msg)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.command", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const body = c.req.valid("json")
+ const svc = yield* SessionPrompt.Service
+ return yield* svc.command({ ...body, sessionID })
+ }),
)
.post(
"/:sessionID/shell",
@@ -1001,12 +990,13 @@ export const SessionRoutes = lazy(() =>
}),
),
validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const body = c.req.valid("json")
- const msg = await AppRuntime.runPromise(SessionPrompt.Service.use((svc) => svc.shell({ ...body, sessionID })))
- return c.json(msg)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.shell", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const body = c.req.valid("json")
+ const svc = yield* SessionPrompt.Service
+ return yield* svc.shell({ ...body, sessionID })
+ }),
)
.post(
"/:sessionID/revert",
@@ -1036,15 +1026,13 @@ export const SessionRoutes = lazy(() =>
async (c) => {
const sessionID = c.req.valid("param").sessionID
log.info("revert", c.req.valid("json"))
- const session = await AppRuntime.runPromise(
- SessionRevert.Service.use((svc) =>
- svc.revert({
- sessionID,
- ...c.req.valid("json"),
- }),
- ),
- )
- return c.json(session)
+ return jsonRequest("SessionRoutes.revert", c, function* () {
+ const svc = yield* SessionRevert.Service
+ return yield* svc.revert({
+ sessionID,
+ ...c.req.valid("json"),
+ })
+ })
},
)
.post(
@@ -1071,11 +1059,12 @@ export const SessionRoutes = lazy(() =>
sessionID: SessionID.zod,
}),
),
- async (c) => {
- const sessionID = c.req.valid("param").sessionID
- const session = await AppRuntime.runPromise(SessionRevert.Service.use((svc) => svc.unrevert({ sessionID })))
- return c.json(session)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.unrevert", c, function* () {
+ const sessionID = c.req.valid("param").sessionID
+ const svc = yield* SessionRevert.Service
+ return yield* svc.unrevert({ sessionID })
+ }),
)
.post(
"/:sessionID/permissions/:permissionID",
@@ -1104,17 +1093,15 @@ export const SessionRoutes = lazy(() =>
}),
),
validator("json", z.object({ response: Permission.Reply.zod })),
- async (c) => {
- const params = c.req.valid("param")
- await AppRuntime.runPromise(
- Permission.Service.use((svc) =>
- svc.reply({
- requestID: params.permissionID,
- reply: c.req.valid("json").response,
- }),
- ),
- )
- return c.json(true)
- },
+ async (c) =>
+ jsonRequest("SessionRoutes.permissionRespond", c, function* () {
+ const params = c.req.valid("param")
+ const svc = yield* Permission.Service
+ yield* svc.reply({
+ requestID: params.permissionID,
+ reply: c.req.valid("json").response,
+ })
+ return true
+ }),
),
)
diff --git a/packages/opencode/src/server/routes/instance/tui.ts b/packages/opencode/src/server/routes/instance/tui.ts
index 2f856c348..d6add67b9 100644
--- a/packages/opencode/src/server/routes/instance/tui.ts
+++ b/packages/opencode/src/server/routes/instance/tui.ts
@@ -4,10 +4,10 @@ import z from "zod"
import { Bus } from "@/bus"
import { Session } from "@/session"
import { TuiEvent } from "@/cli/cmd/tui/event"
-import { AppRuntime } from "@/effect/app-runtime"
import { AsyncQueue } from "@/util/queue"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
+import { runRequest } from "./trace"
const TuiRequest = z.object({
path: z.string(),
@@ -371,7 +371,11 @@ export const TuiRoutes = lazy(() =>
validator("json", TuiEvent.SessionSelect.properties),
async (c) => {
const { sessionID } = c.req.valid("json")
- await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(sessionID)))
+ await runRequest(
+ "TuiRoutes.sessionSelect",
+ c,
+ Session.Service.use((svc) => svc.get(sessionID)),
+ )
await Bus.publish(TuiEvent.SessionSelect, { sessionID })
return c.json(true)
},
diff --git a/packages/opencode/src/server/workspace.ts b/packages/opencode/src/server/workspace.ts
index c141d1095..d30a117d6 100644
--- a/packages/opencode/src/server/workspace.ts
+++ b/packages/opencode/src/server/workspace.ts
@@ -10,6 +10,7 @@ import { Instance } from "@/project/instance"
import { Session } from "@/session"
import { SessionID } from "@/session/schema"
import { AppRuntime } from "@/effect/app-runtime"
+import { Effect } from "effect"
import { Log } from "@/util"
import { ServerProxy } from "./proxy"
@@ -42,7 +43,9 @@ async function getSessionWorkspace(url: URL) {
const id = getSessionID(url)
if (!id) return null
- const session = await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(id))).catch(() => undefined)
+ const session = await AppRuntime.runPromise(
+ Session.Service.use((svc) => svc.get(id)).pipe(Effect.withSpan("WorkspaceRouter.lookup")),
+ ).catch(() => undefined)
return session?.workspaceID
}