Compare commits

..

4 Commits

Author SHA1 Message Date
Kit Langton 94f1a8b128 refactor: delete unreachable code and artifacts 2026-08-06 11:13:02 -04:00
Kit Langton 66b3f5965e refactor(enterprise): use canonical session ids 2026-08-06 11:13:02 -04:00
Kit Langton 2a3242771a refactor(core): remove obsolete migration relics 2026-08-06 11:13:02 -04:00
Kit Langton 51cef27579 refactor(core): deduplicate Copilot endpoint routing (#40765) 2026-08-06 11:10:47 -04:00
34 changed files with 266 additions and 2428 deletions
+1
View File
@@ -513,6 +513,7 @@
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@opencode-ai/schema": "workspace:*",
"@tailwindcss/vite": "catalog:",
"@types/bun": "catalog:",
"@types/luxon": "catalog:",
-26
View File
@@ -137,32 +137,6 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
description: "List all available models",
params: ServerParams,
}),
Spec.make("export", {
description: "Export session data as JSON",
params: {
...ServerParams,
session: Flag.string("session").pipe(
Flag.withAlias("s"),
Flag.withDescription("Session ID to export to stdout"),
Flag.optional,
),
sanitize: Flag.boolean("sanitize").pipe(
Flag.withDescription("Redact sensitive transcript and file data"),
Flag.withDefault(false),
),
},
}),
Spec.make("import", {
description: "Import session data from a JSON file or URL",
params: {
...ServerParams,
file: Argument.string("file").pipe(Argument.withDescription("JSON file or URL to import")),
directory: Flag.string("directory").pipe(
Flag.withDescription("Directory in which to import the session"),
Flag.optional,
),
},
}),
Spec.make("mini", {
description: "Start the minimal interactive interface",
params: {
@@ -1,140 +0,0 @@
import { OpenCode, type SessionInfo } from "@opencode-ai/client"
import { Service } from "@opencode-ai/client/effect/service"
import { Effect, Option } from "effect"
import { EOL, tmpdir } from "node:os"
import path from "node:path"
import { emitKeypressEvents, type Key } from "node:readline"
import { Commands } from "../commands"
import { Runtime } from "../../framework/runtime"
import { ServerConnection } from "../../services/server-connection"
export default Runtime.handler(
Commands.commands.export,
Effect.fn("cli.export")(function* (input) {
const server = yield* ServerConnection.resolve({
server: Option.getOrUndefined(input.server),
standalone: input.standalone,
})
const client = OpenCode.make({
baseUrl: server.endpoint.url,
headers: Service.headers(server.endpoint),
})
const requested = Option.getOrUndefined(input.session)
const selected = requested
? undefined
: yield* Effect.promise(async () => {
const location = await client.location.get({ location: { directory: process.cwd() } })
const page = await client.session.list({
directory: location.directory,
workspace: location.workspaceID,
parentID: null,
order: "desc",
limit: 50,
})
if (page.data.length === 0) {
process.stderr.write(`No sessions found${EOL}`)
return undefined
}
return selectSession(page.data, input.sanitize)
})
const sessionID = requested ?? selected?.session.id
if (!sessionID) return
const data = yield* Effect.promise(() =>
client.session.export({ sessionID, sanitize: selected?.sanitize ?? input.sanitize }),
)
process.stdout.write(yield* Effect.promise(() => writeExport(data, sessionID, requested !== undefined)))
}),
)
type Selection = { session: SessionInfo; sanitize: boolean }
function selectSession(sessions: SessionInfo[], initialSanitize: boolean) {
if (!process.stdin.isTTY) return Promise.reject(new Error("Session ID is required when stdin is not interactive"))
const input = process.stdin
const output = process.stderr
const wasRaw = input.isRaw
const wasPaused = input.isPaused()
const date = new Intl.DateTimeFormat(undefined, {
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
})
const columns = output.columns ?? 100
const titleWidth = Math.max(8, Math.min(48, columns - 34))
let selected = 0
let offset = 0
let sanitize = initialSanitize
let height = 0
const render = () => {
const visible = sessions.slice(offset, offset + 10)
const lines = [" \x1b[36mExport session\x1b[0m", ""]
lines.push(
...visible.map((session) => {
const index = sessions.indexOf(session)
const title = (session.title ?? "Untitled session").slice(0, titleWidth).padEnd(titleWidth)
const updated = date.format(session.time.updated).slice(0, 18).padEnd(18)
const row = `${index === selected ? ">" : " "} ${title} ${updated} ${session.id.slice(-8)}`
return index === selected ? `\x1b[1m${row}\x1b[0m` : row
}),
"",
` [${sanitize ? "x" : " "}] sanitize sensitive data`,
"",
" navigate \x1b[2mup/down\x1b[0m sanitize \x1b[2mspace\x1b[0m export \x1b[2menter\x1b[0m cancel \x1b[2mesc\x1b[0m",
)
if (height > 0) output.write(`\x1b[${height}F\x1b[J`)
output.write(lines.join(EOL) + EOL)
height = lines.length
}
const clear = () => {
if (height > 0) output.write(`\x1b[${height}F\x1b[J`)
output.write("\x1b[?25h")
input.removeListener("keypress", onKeypress)
input.setRawMode(wasRaw ?? false)
if (wasPaused) input.pause()
}
const onKeypress = (value: string | undefined, key: Key) => {
if (key.name === "up") {
selected = (selected - 1 + sessions.length) % sessions.length
if (selected === sessions.length - 1) offset = Math.max(0, sessions.length - 10)
if (selected < offset) offset = selected
}
if (key.name === "down") {
selected = (selected + 1) % sessions.length
if (selected === 0) offset = 0
if (selected >= offset + 10) offset = selected - 9
}
if (key.name === "space" || value === " ") sanitize = !sanitize
if (key.name === "return") return finish(sessions[selected])
if (key.name === "escape" || (key.ctrl && key.name === "c")) return cancel()
render()
}
const finish = (session: SessionInfo) => {
clear()
resolveSelection?.({ session, sanitize })
}
const cancel = () => {
clear()
resolveSelection?.()
}
let resolveSelection: ((selection?: Selection) => void) | undefined
emitKeypressEvents(input)
input.setRawMode(true)
input.resume()
input.on("keypress", onKeypress)
output.write("\x1b[?25l")
render()
return new Promise<Selection | undefined>((resolve) => {
resolveSelection = resolve
})
}
export async function writeExport(data: unknown, sessionID: string, stdout: boolean) {
const json = JSON.stringify(data, null, 2) + EOL
if (stdout) return json
const file = path.join(tmpdir(), `opencode-session-${sessionID}-${crypto.randomUUID().slice(0, 8)}.json`)
await Bun.write(file, json)
return file + EOL
}
@@ -1,60 +0,0 @@
import { OpenCode } from "@opencode-ai/client"
import { Service } from "@opencode-ai/client/effect/service"
import { Session } from "@opencode-ai/schema/session"
import { SessionTransfer } from "@opencode-ai/schema/session-transfer"
import { Effect, Option, Schema } from "effect"
import { EOL } from "node:os"
import path from "node:path"
import { Commands } from "../commands"
import { Runtime } from "../../framework/runtime"
import { ServerConnection } from "../../services/server-connection"
export default Runtime.handler(
Commands.commands.import,
Effect.fn("cli.import")(function* (input) {
const text = yield* Effect.tryPromise({
try: () =>
input.file.startsWith("http://") || input.file.startsWith("https://")
? fetch(input.file).then((response) => {
if (!response.ok) throw new Error(`Failed to fetch session data: ${response.statusText}`)
return response.text()
})
: Bun.file(input.file).text(),
catch: (cause) => new Error(`Failed to read session data: ${cause instanceof Error ? cause.message : String(cause)}`),
})
const data = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(SessionTransfer.Data))(text)
const encoded = Schema.encodeSync(SessionTransfer.Data)(data)
const server = yield* ServerConnection.resolve({
server: Option.getOrUndefined(input.server),
standalone: input.standalone,
})
const client = OpenCode.make({
baseUrl: server.endpoint.url,
headers: Service.headers(server.endpoint),
})
const location = yield* Effect.promise(() =>
client.location.get({
location: { directory: path.resolve(Option.getOrElse(input.directory, () => process.cwd())) },
}),
)
const response = yield* Effect.promise(() =>
fetch(new URL("/api/session/import", server.endpoint.url), {
method: "POST",
headers: { ...Service.headers(server.endpoint), "content-type": "application/json" },
body: JSON.stringify({
...encoded,
location: { directory: location.directory, workspaceID: location.workspaceID },
}),
}),
)
if (response.status === 409) {
process.stderr.write(`Session already exists${EOL}`)
return
}
if (!response.ok) yield* Effect.fail(new Error(`Failed to import session: ${response.statusText}`))
const imported = yield* Schema.decodeUnknownEffect(
Schema.fromJsonString(Schema.Struct({ data: Session.Info })),
)(yield* Effect.promise(() => response.text()))
process.stdout.write(`Imported session: ${imported.data.id}${EOL}`)
}),
)
-2
View File
@@ -36,8 +36,6 @@ const Handlers = Runtime.handlers(Commands, {
list: () => import("./commands/handlers/plugin/list"),
},
models: () => import("./commands/handlers/models"),
export: () => import("./commands/handlers/export"),
import: () => import("./commands/handlers/import"),
mini: () => import("./commands/handlers/mini"),
run: () => import("./commands/handlers/run"),
pair: () => import("./commands/handlers/pair"),
-210
View File
@@ -1,210 +0,0 @@
import { expect, test } from "bun:test"
import fs from "node:fs/promises"
import os from "node:os"
import path from "node:path"
import { OPENCODE_VERSION } from "../src/version"
import { writeExport } from "../src/commands/handlers/export"
const info = {
id: "ses_export_test",
projectID: "global",
cost: 0,
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
time: { created: 1, updated: 2 },
title: "Exported session",
location: { directory: "/project" },
}
const transfer = {
info,
messages: [
{ id: "msg_first", type: "user", text: "First", time: { created: 1 } },
{ id: "msg_second", type: "user", text: "Second", time: { created: 2 } },
],
}
const sanitizedTransfer = {
info: {
...info,
title: "[redacted:session-title:ses_export_test]",
location: { directory: "/[redacted:session-directory:ses_export_test]" },
},
messages: [
{
id: "msg_first",
type: "user",
text: "[redacted:text:msg_first]",
time: { created: 1 },
},
{
id: "msg_second",
type: "user",
text: "[redacted:text:msg_second]",
time: { created: 2 },
},
],
}
const health = () => Response.json({ healthy: true, version: OPENCODE_VERSION, pid: process.pid })
function run(args: string[], stdin?: string) {
const child = Bun.spawn([process.execPath, "run", "src/index.ts", ...args], {
cwd: path.join(import.meta.dir, ".."),
stdin: stdin === undefined ? undefined : new Blob([stdin]),
stdout: "pipe",
stderr: "pipe",
})
return Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited])
}
test("export is raw by default and supports explicit sanitization", async () => {
const sanitization: string[] = []
const server = Bun.serve({
port: 0,
fetch(request) {
const url = new URL(request.url)
if (url.pathname === "/api/health") return health()
if (url.pathname === `/api/session/${info.id}`) return Response.json({ data: info })
if (url.pathname === `/api/session/${info.id}/export`) {
sanitization.push(url.searchParams.get("sanitize") ?? "")
return Response.json({ data: url.searchParams.get("sanitize") === "true" ? sanitizedTransfer : transfer })
}
return new Response("Not found", { status: 404 })
},
})
try {
const [stdout, , exitCode] = await run(["export", "-s", info.id, "--server", server.url.toString()])
const exported = JSON.parse(stdout)
expect(exitCode).toBe(0)
expect(exported).toEqual(transfer)
const [sanitized, , sanitizedExitCode] = await run([
"export",
"-s",
info.id,
"--sanitize",
"--server",
server.url.toString(),
])
expect(sanitizedExitCode).toBe(0)
expect(JSON.parse(sanitized)).toEqual(sanitizedTransfer)
expect(sanitization).toEqual(["false", "true"])
} finally {
await server.stop(true)
}
}, 15_000)
test("export reports an empty session list without a stack trace", async () => {
const server = Bun.serve({
port: 0,
fetch(request) {
const url = new URL(request.url)
if (url.pathname === "/api/health") return health()
if (url.pathname === "/api/location") {
return Response.json({
directory: "/project",
project: { id: "global", directory: "/project", canonical: "/project" },
})
}
if (url.pathname === "/api/session") return Response.json({ data: [], cursor: {} })
return new Response("Not found", { status: 404 })
},
})
try {
const [stdout, stderr, exitCode] = await run(["export", "--server", server.url.toString()])
expect(exitCode).toBe(0)
expect(stdout).toBe("")
expect(stderr).toBe(`No sessions found${os.EOL}`)
} finally {
await server.stop(true)
}
})
test("interactive export writes a temporary JSON file", async () => {
const output = await writeExport(transfer, info.id, false)
const file = output.trim()
try {
expect(path.dirname(file)).toBe(os.tmpdir())
expect(await Bun.file(file).json()).toEqual(transfer)
} finally {
await fs.rm(file, { force: true })
}
})
test("import validates a file and sends it to the resolved location", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-import-"))
const file = path.join(root, "session.json")
await fs.writeFile(file, JSON.stringify(transfer))
let imported: unknown
const server = Bun.serve({
port: 0,
async fetch(request) {
const url = new URL(request.url)
if (url.pathname === "/api/health") return health()
if (url.pathname === "/api/location") {
return Response.json({
directory: root,
project: { id: "global", directory: root, canonical: root },
})
}
if (url.pathname === "/api/session/import") {
imported = await request.json()
return Response.json({ data: { ...info, location: { directory: root } } })
}
return new Response("Not found", { status: 404 })
},
})
try {
const [stdout, , exitCode] = await run([
"import",
file,
"--directory",
root,
"--server",
server.url.toString(),
])
expect(exitCode).toBe(0)
expect(stdout).toBe(`Imported session: ${info.id}${os.EOL}`)
expect(imported).toEqual({ ...transfer, location: { directory: root } })
} finally {
await server.stop(true)
await fs.rm(root, { recursive: true, force: true })
}
})
test("import reports an existing session without a stack trace", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-import-conflict-"))
const file = path.join(root, "session.json")
await fs.writeFile(file, JSON.stringify(transfer))
const server = Bun.serve({
port: 0,
fetch(request) {
const url = new URL(request.url)
if (url.pathname === "/api/health") return health()
if (url.pathname === "/api/location") {
return Response.json({
directory: root,
project: { id: "global", directory: root, canonical: root },
})
}
if (url.pathname === "/api/session/import") return new Response("Conflict", { status: 409 })
return new Response("Not found", { status: 404 })
},
})
try {
const [stdout, stderr, exitCode] = await run(["import", file, "--server", server.url.toString()])
expect(exitCode).toBe(0)
expect(stdout).toBe("")
expect(stderr).toBe(`Session already exists${os.EOL}`)
} finally {
await server.stop(true)
await fs.rm(root, { recursive: true, force: true })
}
})
+83 -97
View File
@@ -126,54 +126,42 @@ export type Endpoint5_1Input = {
export type Endpoint5_1Output = Session.Info
export type SessionCreateOperation<E = never> = (input?: Endpoint5_1Input) => Effect.Effect<Endpoint5_1Output, E>
export type Endpoint5_2Input = {
readonly info: Session.Info
readonly messages: ReadonlyArray<SessionMessage.Info>
readonly location?: Location.Ref | undefined
}
export type Endpoint5_2Output = Session.Info
export type SessionImportOperation<E = never> = (input: Endpoint5_2Input) => Effect.Effect<Endpoint5_2Output, E>
export type Endpoint5_2Output = { readonly [x: Session.ID]: { readonly type: "running" } }
export type SessionActiveOperation<E = never> = () => Effect.Effect<Endpoint5_2Output, E>
export type Endpoint5_3Input = { readonly sessionID: Session.ID; readonly sanitize?: boolean | undefined }
export type Endpoint5_3Output = { readonly info: Session.Info; readonly messages: ReadonlyArray<SessionMessage.Info> }
export type SessionExportOperation<E = never> = (input: Endpoint5_3Input) => Effect.Effect<Endpoint5_3Output, E>
export type Endpoint5_3Input = { readonly sessionID: Session.ID }
export type Endpoint5_3Output = Session.Info
export type SessionGetOperation<E = never> = (input: Endpoint5_3Input) => Effect.Effect<Endpoint5_3Output, E>
export type Endpoint5_4Output = { readonly [x: Session.ID]: { readonly type: "running" } }
export type SessionActiveOperation<E = never> = () => Effect.Effect<Endpoint5_4Output, E>
export type Endpoint5_4Input = { readonly sessionID: Session.ID }
export type Endpoint5_4Output = void
export type SessionRemoveOperation<E = never> = (input: Endpoint5_4Input) => Effect.Effect<Endpoint5_4Output, E>
export type Endpoint5_5Input = { readonly sessionID: Session.ID }
export type Endpoint5_5Input = { readonly sessionID: Session.ID; readonly boundary: Session.ForkRequestBoundary }
export type Endpoint5_5Output = Session.Info
export type SessionGetOperation<E = never> = (input: Endpoint5_5Input) => Effect.Effect<Endpoint5_5Output, E>
export type SessionForkOperation<E = never> = (input: Endpoint5_5Input) => Effect.Effect<Endpoint5_5Output, E>
export type Endpoint5_6Input = { readonly sessionID: Session.ID }
export type Endpoint5_6Input = { readonly sessionID: Session.ID; readonly agent: Agent.ID }
export type Endpoint5_6Output = void
export type SessionRemoveOperation<E = never> = (input: Endpoint5_6Input) => Effect.Effect<Endpoint5_6Output, E>
export type SessionSwitchAgentOperation<E = never> = (input: Endpoint5_6Input) => Effect.Effect<Endpoint5_6Output, E>
export type Endpoint5_7Input = { readonly sessionID: Session.ID; readonly boundary: Session.ForkRequestBoundary }
export type Endpoint5_7Output = Session.Info
export type SessionForkOperation<E = never> = (input: Endpoint5_7Input) => Effect.Effect<Endpoint5_7Output, E>
export type Endpoint5_7Input = { readonly sessionID: Session.ID; readonly model: Model.Ref }
export type Endpoint5_7Output = void
export type SessionSwitchModelOperation<E = never> = (input: Endpoint5_7Input) => Effect.Effect<Endpoint5_7Output, E>
export type Endpoint5_8Input = { readonly sessionID: Session.ID; readonly agent: Agent.ID }
export type Endpoint5_8Input = { readonly sessionID: Session.ID; readonly title: string }
export type Endpoint5_8Output = void
export type SessionSwitchAgentOperation<E = never> = (input: Endpoint5_8Input) => Effect.Effect<Endpoint5_8Output, E>
export type SessionRenameOperation<E = never> = (input: Endpoint5_8Input) => Effect.Effect<Endpoint5_8Output, E>
export type Endpoint5_9Input = { readonly sessionID: Session.ID; readonly model: Model.Ref }
export type Endpoint5_9Output = void
export type SessionSwitchModelOperation<E = never> = (input: Endpoint5_9Input) => Effect.Effect<Endpoint5_9Output, E>
export type Endpoint5_10Input = { readonly sessionID: Session.ID; readonly title: string }
export type Endpoint5_10Output = void
export type SessionRenameOperation<E = never> = (input: Endpoint5_10Input) => Effect.Effect<Endpoint5_10Output, E>
export type Endpoint5_11Input = {
export type Endpoint5_9Input = {
readonly sessionID: Session.ID
readonly directory: AbsolutePath
readonly workspaceID?: Workspace.ID | undefined
}
export type Endpoint5_11Output = void
export type SessionMoveOperation<E = never> = (input: Endpoint5_11Input) => Effect.Effect<Endpoint5_11Output, E>
export type Endpoint5_9Output = void
export type SessionMoveOperation<E = never> = (input: Endpoint5_9Input) => Effect.Effect<Endpoint5_9Output, E>
export type Endpoint5_12Input = {
export type Endpoint5_10Input = {
readonly sessionID: Session.ID
readonly id?: SessionMessage.ID | undefined
readonly text: string
@@ -183,10 +171,10 @@ export type Endpoint5_12Input = {
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
}
export type Endpoint5_12Output = SessionPending.User
export type SessionPromptOperation<E = never> = (input: Endpoint5_12Input) => Effect.Effect<Endpoint5_12Output, E>
export type Endpoint5_10Output = SessionPending.User
export type SessionPromptOperation<E = never> = (input: Endpoint5_10Input) => Effect.Effect<Endpoint5_10Output, E>
export type Endpoint5_13Input = {
export type Endpoint5_11Input = {
readonly sessionID: Session.ID
readonly id?: SessionMessage.ID | undefined
readonly command: string
@@ -198,19 +186,19 @@ export type Endpoint5_13Input = {
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
}
export type Endpoint5_13Output = SessionPending.User
export type SessionCommandOperation<E = never> = (input: Endpoint5_13Input) => Effect.Effect<Endpoint5_13Output, E>
export type Endpoint5_11Output = SessionPending.User
export type SessionCommandOperation<E = never> = (input: Endpoint5_11Input) => Effect.Effect<Endpoint5_11Output, E>
export type Endpoint5_14Input = {
export type Endpoint5_12Input = {
readonly sessionID: Session.ID
readonly id?: SessionMessage.ID | undefined
readonly skill: Skill.ID
readonly resume?: boolean | undefined
}
export type Endpoint5_14Output = void
export type SessionSkillOperation<E = never> = (input: Endpoint5_14Input) => Effect.Effect<Endpoint5_14Output, E>
export type Endpoint5_12Output = void
export type SessionSkillOperation<E = never> = (input: Endpoint5_12Input) => Effect.Effect<Endpoint5_12Output, E>
export type Endpoint5_15Input = {
export type Endpoint5_13Input = {
readonly sessionID: Session.ID
readonly id?: SessionMessage.ID | undefined
readonly text: string
@@ -219,81 +207,81 @@ export type Endpoint5_15Input = {
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
}
export type Endpoint5_15Output = SessionPending.Synthetic
export type SessionSyntheticOperation<E = never> = (input: Endpoint5_15Input) => Effect.Effect<Endpoint5_15Output, E>
export type Endpoint5_13Output = SessionPending.Synthetic
export type SessionSyntheticOperation<E = never> = (input: Endpoint5_13Input) => Effect.Effect<Endpoint5_13Output, E>
export type Endpoint5_16Input = {
export type Endpoint5_14Input = {
readonly sessionID: Session.ID
readonly id?: Event.ID | undefined
readonly command: string
}
export type Endpoint5_14Output = void
export type SessionShellOperation<E = never> = (input: Endpoint5_14Input) => Effect.Effect<Endpoint5_14Output, E>
export type Endpoint5_15Input = { readonly sessionID: Session.ID; readonly id?: SessionMessage.ID | undefined }
export type Endpoint5_15Output = SessionPending.Compaction
export type SessionCompactOperation<E = never> = (input: Endpoint5_15Input) => Effect.Effect<Endpoint5_15Output, E>
export type Endpoint5_16Input = { readonly sessionID: Session.ID }
export type Endpoint5_16Output = void
export type SessionShellOperation<E = never> = (input: Endpoint5_16Input) => Effect.Effect<Endpoint5_16Output, E>
export type SessionWaitOperation<E = never> = (input: Endpoint5_16Input) => Effect.Effect<Endpoint5_16Output, E>
export type Endpoint5_17Input = { readonly sessionID: Session.ID; readonly id?: SessionMessage.ID | undefined }
export type Endpoint5_17Output = SessionPending.Compaction
export type SessionCompactOperation<E = never> = (input: Endpoint5_17Input) => Effect.Effect<Endpoint5_17Output, E>
export type Endpoint5_18Input = { readonly sessionID: Session.ID }
export type Endpoint5_18Output = void
export type SessionWaitOperation<E = never> = (input: Endpoint5_18Input) => Effect.Effect<Endpoint5_18Output, E>
export type Endpoint5_19Input = {
export type Endpoint5_17Input = {
readonly sessionID: Session.ID
readonly messageID: SessionMessage.ID
readonly files?: boolean | undefined
}
export type Endpoint5_19Output = Session.Revert
export type SessionRevertStageOperation<E = never> = (input: Endpoint5_19Input) => Effect.Effect<Endpoint5_19Output, E>
export type Endpoint5_17Output = Session.Revert
export type SessionRevertStageOperation<E = never> = (input: Endpoint5_17Input) => Effect.Effect<Endpoint5_17Output, E>
export type Endpoint5_18Input = { readonly sessionID: Session.ID }
export type Endpoint5_18Output = void
export type SessionRevertClearOperation<E = never> = (input: Endpoint5_18Input) => Effect.Effect<Endpoint5_18Output, E>
export type Endpoint5_19Input = { readonly sessionID: Session.ID }
export type Endpoint5_19Output = void
export type SessionRevertCommitOperation<E = never> = (input: Endpoint5_19Input) => Effect.Effect<Endpoint5_19Output, E>
export type Endpoint5_20Input = { readonly sessionID: Session.ID }
export type Endpoint5_20Output = void
export type SessionRevertClearOperation<E = never> = (input: Endpoint5_20Input) => Effect.Effect<Endpoint5_20Output, E>
export type Endpoint5_20Output = ReadonlyArray<SessionMessage.Info>
export type SessionContextOperation<E = never> = (input: Endpoint5_20Input) => Effect.Effect<Endpoint5_20Output, E>
export type Endpoint5_21Input = { readonly sessionID: Session.ID }
export type Endpoint5_21Output = void
export type SessionRevertCommitOperation<E = never> = (input: Endpoint5_21Input) => Effect.Effect<Endpoint5_21Output, E>
export type Endpoint5_21Output = ReadonlyArray<SessionPending.Info>
export type SessionPendingListOperation<E = never> = (input: Endpoint5_21Input) => Effect.Effect<Endpoint5_21Output, E>
export type Endpoint5_22Input = { readonly sessionID: Session.ID }
export type Endpoint5_22Output = ReadonlyArray<SessionMessage.Info>
export type SessionContextOperation<E = never> = (input: Endpoint5_22Input) => Effect.Effect<Endpoint5_22Output, E>
export type Endpoint5_23Input = { readonly sessionID: Session.ID }
export type Endpoint5_23Output = ReadonlyArray<SessionPending.Info>
export type SessionPendingListOperation<E = never> = (input: Endpoint5_23Input) => Effect.Effect<Endpoint5_23Output, E>
export type Endpoint5_24Input = { readonly sessionID: Session.ID }
export type Endpoint5_24Output = ReadonlyArray<InstructionEntry.Info>
export type Endpoint5_22Output = ReadonlyArray<InstructionEntry.Info>
export type SessionInstructionsEntryListOperation<E = never> = (
input: Endpoint5_24Input,
) => Effect.Effect<Endpoint5_24Output, E>
input: Endpoint5_22Input,
) => Effect.Effect<Endpoint5_22Output, E>
export type Endpoint5_25Input = {
export type Endpoint5_23Input = {
readonly sessionID: Session.ID
readonly key: InstructionEntry.Key
readonly value: Schema.Json
}
export type Endpoint5_25Output = void
export type Endpoint5_23Output = void
export type SessionInstructionsEntryPutOperation<E = never> = (
input: Endpoint5_25Input,
) => Effect.Effect<Endpoint5_25Output, E>
input: Endpoint5_23Input,
) => Effect.Effect<Endpoint5_23Output, E>
export type Endpoint5_26Input = { readonly sessionID: Session.ID; readonly key: InstructionEntry.Key }
export type Endpoint5_26Output = void
export type Endpoint5_24Input = { readonly sessionID: Session.ID; readonly key: InstructionEntry.Key }
export type Endpoint5_24Output = void
export type SessionInstructionsEntryRemoveOperation<E = never> = (
input: Endpoint5_26Input,
) => Effect.Effect<Endpoint5_26Output, E>
input: Endpoint5_24Input,
) => Effect.Effect<Endpoint5_24Output, E>
export type Endpoint5_27Input = { readonly sessionID: Session.ID; readonly prompt: string }
export type Endpoint5_27Output = { readonly text: string }
export type SessionGenerateOperation<E = never> = (input: Endpoint5_27Input) => Effect.Effect<Endpoint5_27Output, E>
export type Endpoint5_25Input = { readonly sessionID: Session.ID; readonly prompt: string }
export type Endpoint5_25Output = { readonly text: string }
export type SessionGenerateOperation<E = never> = (input: Endpoint5_25Input) => Effect.Effect<Endpoint5_25Output, E>
export type Endpoint5_28Input = {
export type Endpoint5_26Input = {
readonly sessionID: Session.ID
readonly after?: Event.Seq | undefined
readonly follow?: boolean | undefined
}
export type Endpoint5_28Output =
export type Endpoint5_26Output =
| (
| {
readonly id: Event.ID
@@ -861,25 +849,23 @@ export type Endpoint5_28Output =
}
)
| EventLog.Synced
export type SessionLogOperation<E = never> = (input: Endpoint5_28Input) => Stream.Stream<Endpoint5_28Output, E>
export type SessionLogOperation<E = never> = (input: Endpoint5_26Input) => Stream.Stream<Endpoint5_26Output, E>
export type Endpoint5_29Input = { readonly sessionID: Session.ID }
export type Endpoint5_29Output = void
export type SessionInterruptOperation<E = never> = (input: Endpoint5_29Input) => Effect.Effect<Endpoint5_29Output, E>
export type Endpoint5_27Input = { readonly sessionID: Session.ID }
export type Endpoint5_27Output = void
export type SessionInterruptOperation<E = never> = (input: Endpoint5_27Input) => Effect.Effect<Endpoint5_27Output, E>
export type Endpoint5_30Input = { readonly sessionID: Session.ID }
export type Endpoint5_30Output = void
export type SessionBackgroundOperation<E = never> = (input: Endpoint5_30Input) => Effect.Effect<Endpoint5_30Output, E>
export type Endpoint5_28Input = { readonly sessionID: Session.ID }
export type Endpoint5_28Output = void
export type SessionBackgroundOperation<E = never> = (input: Endpoint5_28Input) => Effect.Effect<Endpoint5_28Output, E>
export type Endpoint5_31Input = { readonly sessionID: Session.ID; readonly messageID: SessionMessage.ID }
export type Endpoint5_31Output = SessionMessage.Info
export type SessionMessageOperation<E = never> = (input: Endpoint5_31Input) => Effect.Effect<Endpoint5_31Output, E>
export type Endpoint5_29Input = { readonly sessionID: Session.ID; readonly messageID: SessionMessage.ID }
export type Endpoint5_29Output = SessionMessage.Info
export type SessionMessageOperation<E = never> = (input: Endpoint5_29Input) => Effect.Effect<Endpoint5_29Output, E>
export interface SessionApi<E = never> {
readonly list: SessionListOperation<E>
readonly create: SessionCreateOperation<E>
readonly import: SessionImportOperation<E>
readonly export: SessionExportOperation<E>
readonly active: SessionActiveOperation<E>
readonly get: SessionGetOperation<E>
readonly remove: SessionRemoveOperation<E>
+83 -107
View File
@@ -21,10 +21,10 @@ import type {
Endpoint5_0Output,
Endpoint5_1Input,
Endpoint5_1Output,
Endpoint5_2Input,
Endpoint5_2Output,
Endpoint5_3Input,
Endpoint5_3Output,
Endpoint5_4Input,
Endpoint5_4Output,
Endpoint5_5Input,
Endpoint5_5Output,
@@ -76,10 +76,6 @@ import type {
Endpoint5_28Output,
Endpoint5_29Input,
Endpoint5_29Output,
Endpoint5_30Input,
Endpoint5_30Output,
Endpoint5_31Input,
Endpoint5_31Output,
Endpoint6_0Input,
Endpoint6_0Output,
Endpoint7_0Input,
@@ -319,11 +315,9 @@ const Endpoint5_1 = (raw: RawClient["server.session"]) => (input?: Endpoint5_1In
),
)
const Endpoint5_2 = (raw: RawClient["server.session"]) => (input: Endpoint5_2Input) =>
const Endpoint5_2 = (raw: RawClient["server.session"]) => () =>
preserveEffect<Endpoint5_2Output>()(
raw["session.import"]({
payload: { info: input["info"], messages: input["messages"], location: input["location"] },
}).pipe(
raw["session.active"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
@@ -331,23 +325,20 @@ const Endpoint5_2 = (raw: RawClient["server.session"]) => (input: Endpoint5_2Inp
const Endpoint5_3 = (raw: RawClient["server.session"]) => (input: Endpoint5_3Input) =>
preserveEffect<Endpoint5_3Output>()(
raw["session.export"]({ params: { sessionID: input["sessionID"] }, query: { sanitize: input["sanitize"] } }).pipe(
raw["session.get"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_4 = (raw: RawClient["server.session"]) => () =>
const Endpoint5_4 = (raw: RawClient["server.session"]) => (input: Endpoint5_4Input) =>
preserveEffect<Endpoint5_4Output>()(
raw["session.active"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
raw["session.remove"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_5 = (raw: RawClient["server.session"]) => (input: Endpoint5_5Input) =>
preserveEffect<Endpoint5_5Output>()(
raw["session.get"]({ params: { sessionID: input["sessionID"] } }).pipe(
raw["session.fork"]({ params: { sessionID: input["sessionID"] }, payload: { boundary: input["boundary"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
@@ -355,48 +346,35 @@ const Endpoint5_5 = (raw: RawClient["server.session"]) => (input: Endpoint5_5Inp
const Endpoint5_6 = (raw: RawClient["server.session"]) => (input: Endpoint5_6Input) =>
preserveEffect<Endpoint5_6Output>()(
raw["session.remove"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
raw["session.switchAgent"]({ params: { sessionID: input["sessionID"] }, payload: { agent: input["agent"] } }).pipe(
Effect.mapError(mapClientError),
),
)
const Endpoint5_7 = (raw: RawClient["server.session"]) => (input: Endpoint5_7Input) =>
preserveEffect<Endpoint5_7Output>()(
raw["session.fork"]({ params: { sessionID: input["sessionID"] }, payload: { boundary: input["boundary"] } }).pipe(
raw["session.switchModel"]({ params: { sessionID: input["sessionID"] }, payload: { model: input["model"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_8 = (raw: RawClient["server.session"]) => (input: Endpoint5_8Input) =>
preserveEffect<Endpoint5_8Output>()(
raw["session.switchAgent"]({ params: { sessionID: input["sessionID"] }, payload: { agent: input["agent"] } }).pipe(
raw["session.rename"]({ params: { sessionID: input["sessionID"] }, payload: { title: input["title"] } }).pipe(
Effect.mapError(mapClientError),
),
)
const Endpoint5_9 = (raw: RawClient["server.session"]) => (input: Endpoint5_9Input) =>
preserveEffect<Endpoint5_9Output>()(
raw["session.switchModel"]({ params: { sessionID: input["sessionID"] }, payload: { model: input["model"] } }).pipe(
Effect.mapError(mapClientError),
),
)
const Endpoint5_10 = (raw: RawClient["server.session"]) => (input: Endpoint5_10Input) =>
preserveEffect<Endpoint5_10Output>()(
raw["session.rename"]({ params: { sessionID: input["sessionID"] }, payload: { title: input["title"] } }).pipe(
Effect.mapError(mapClientError),
),
)
const Endpoint5_11 = (raw: RawClient["server.session"]) => (input: Endpoint5_11Input) =>
preserveEffect<Endpoint5_11Output>()(
raw["session.move"]({
params: { sessionID: input["sessionID"] },
payload: { directory: input["directory"], workspaceID: input["workspaceID"] },
}).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_12 = (raw: RawClient["server.session"]) => (input: Endpoint5_12Input) =>
preserveEffect<Endpoint5_12Output>()(
const Endpoint5_10 = (raw: RawClient["server.session"]) => (input: Endpoint5_10Input) =>
preserveEffect<Endpoint5_10Output>()(
raw["session.prompt"]({
params: { sessionID: input["sessionID"] },
payload: {
@@ -414,8 +392,8 @@ const Endpoint5_12 = (raw: RawClient["server.session"]) => (input: Endpoint5_12I
),
)
const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13Input) =>
preserveEffect<Endpoint5_13Output>()(
const Endpoint5_11 = (raw: RawClient["server.session"]) => (input: Endpoint5_11Input) =>
preserveEffect<Endpoint5_11Output>()(
raw["session.command"]({
params: { sessionID: input["sessionID"] },
payload: {
@@ -435,16 +413,16 @@ const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13I
),
)
const Endpoint5_14 = (raw: RawClient["server.session"]) => (input: Endpoint5_14Input) =>
preserveEffect<Endpoint5_14Output>()(
const Endpoint5_12 = (raw: RawClient["server.session"]) => (input: Endpoint5_12Input) =>
preserveEffect<Endpoint5_12Output>()(
raw["session.skill"]({
params: { sessionID: input["sessionID"] },
payload: { id: input["id"], skill: input["skill"], resume: input["resume"] },
}).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_15 = (raw: RawClient["server.session"]) => (input: Endpoint5_15Input) =>
preserveEffect<Endpoint5_15Output>()(
const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13Input) =>
preserveEffect<Endpoint5_13Output>()(
raw["session.synthetic"]({
params: { sessionID: input["sessionID"] },
payload: {
@@ -461,29 +439,29 @@ const Endpoint5_15 = (raw: RawClient["server.session"]) => (input: Endpoint5_15I
),
)
const Endpoint5_16 = (raw: RawClient["server.session"]) => (input: Endpoint5_16Input) =>
preserveEffect<Endpoint5_16Output>()(
const Endpoint5_14 = (raw: RawClient["server.session"]) => (input: Endpoint5_14Input) =>
preserveEffect<Endpoint5_14Output>()(
raw["session.shell"]({
params: { sessionID: input["sessionID"] },
payload: { id: input["id"], command: input["command"] },
}).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_17 = (raw: RawClient["server.session"]) => (input: Endpoint5_17Input) =>
preserveEffect<Endpoint5_17Output>()(
const Endpoint5_15 = (raw: RawClient["server.session"]) => (input: Endpoint5_15Input) =>
preserveEffect<Endpoint5_15Output>()(
raw["session.compact"]({ params: { sessionID: input["sessionID"] }, payload: { id: input["id"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_18 = (raw: RawClient["server.session"]) => (input: Endpoint5_18Input) =>
preserveEffect<Endpoint5_18Output>()(
const Endpoint5_16 = (raw: RawClient["server.session"]) => (input: Endpoint5_16Input) =>
preserveEffect<Endpoint5_16Output>()(
raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_19 = (raw: RawClient["server.session"]) => (input: Endpoint5_19Input) =>
preserveEffect<Endpoint5_19Output>()(
const Endpoint5_17 = (raw: RawClient["server.session"]) => (input: Endpoint5_17Input) =>
preserveEffect<Endpoint5_17Output>()(
raw["session.revert.stage"]({
params: { sessionID: input["sessionID"] },
payload: { messageID: input["messageID"], files: input["files"] },
@@ -493,19 +471,35 @@ const Endpoint5_19 = (raw: RawClient["server.session"]) => (input: Endpoint5_19I
),
)
const Endpoint5_18 = (raw: RawClient["server.session"]) => (input: Endpoint5_18Input) =>
preserveEffect<Endpoint5_18Output>()(
raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_19 = (raw: RawClient["server.session"]) => (input: Endpoint5_19Input) =>
preserveEffect<Endpoint5_19Output>()(
raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_20 = (raw: RawClient["server.session"]) => (input: Endpoint5_20Input) =>
preserveEffect<Endpoint5_20Output>()(
raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_21 = (raw: RawClient["server.session"]) => (input: Endpoint5_21Input) =>
preserveEffect<Endpoint5_21Output>()(
raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
raw["session.pending.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_22 = (raw: RawClient["server.session"]) => (input: Endpoint5_22Input) =>
preserveEffect<Endpoint5_22Output>()(
raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
raw["session.instructions.entry.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
@@ -513,45 +507,29 @@ const Endpoint5_22 = (raw: RawClient["server.session"]) => (input: Endpoint5_22I
const Endpoint5_23 = (raw: RawClient["server.session"]) => (input: Endpoint5_23Input) =>
preserveEffect<Endpoint5_23Output>()(
raw["session.pending.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_24 = (raw: RawClient["server.session"]) => (input: Endpoint5_24Input) =>
preserveEffect<Endpoint5_24Output>()(
raw["session.instructions.entry.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_25 = (raw: RawClient["server.session"]) => (input: Endpoint5_25Input) =>
preserveEffect<Endpoint5_25Output>()(
raw["session.instructions.entry.put"]({
params: { sessionID: input["sessionID"], key: input["key"] },
payload: { value: input["value"] },
}).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_26 = (raw: RawClient["server.session"]) => (input: Endpoint5_26Input) =>
preserveEffect<Endpoint5_26Output>()(
const Endpoint5_24 = (raw: RawClient["server.session"]) => (input: Endpoint5_24Input) =>
preserveEffect<Endpoint5_24Output>()(
raw["session.instructions.entry.remove"]({ params: { sessionID: input["sessionID"], key: input["key"] } }).pipe(
Effect.mapError(mapClientError),
),
)
const Endpoint5_27 = (raw: RawClient["server.session"]) => (input: Endpoint5_27Input) =>
preserveEffect<Endpoint5_27Output>()(
const Endpoint5_25 = (raw: RawClient["server.session"]) => (input: Endpoint5_25Input) =>
preserveEffect<Endpoint5_25Output>()(
raw["session.generate"]({ params: { sessionID: input["sessionID"] }, payload: { prompt: input["prompt"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
),
)
const Endpoint5_28 = (raw: RawClient["server.session"]) => (input: Endpoint5_28Input) =>
preserveStream<Endpoint5_28Output>()(
const Endpoint5_26 = (raw: RawClient["server.session"]) => (input: Endpoint5_26Input) =>
preserveStream<Endpoint5_26Output>()(
Stream.unwrap(
raw["session.log"]({
params: { sessionID: input["sessionID"] },
@@ -563,18 +541,18 @@ const Endpoint5_28 = (raw: RawClient["server.session"]) => (input: Endpoint5_28I
),
)
const Endpoint5_29 = (raw: RawClient["server.session"]) => (input: Endpoint5_29Input) =>
preserveEffect<Endpoint5_29Output>()(
const Endpoint5_27 = (raw: RawClient["server.session"]) => (input: Endpoint5_27Input) =>
preserveEffect<Endpoint5_27Output>()(
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_30 = (raw: RawClient["server.session"]) => (input: Endpoint5_30Input) =>
preserveEffect<Endpoint5_30Output>()(
const Endpoint5_28 = (raw: RawClient["server.session"]) => (input: Endpoint5_28Input) =>
preserveEffect<Endpoint5_28Output>()(
raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const Endpoint5_31 = (raw: RawClient["server.session"]) => (input: Endpoint5_31Input) =>
preserveEffect<Endpoint5_31Output>()(
const Endpoint5_29 = (raw: RawClient["server.session"]) => (input: Endpoint5_29Input) =>
preserveEffect<Endpoint5_29Output>()(
raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
@@ -584,32 +562,30 @@ const Endpoint5_31 = (raw: RawClient["server.session"]) => (input: Endpoint5_31I
const adaptGroup5 = (raw: RawClient["server.session"]) => ({
list: Endpoint5_0(raw),
create: Endpoint5_1(raw),
import: Endpoint5_2(raw),
export: Endpoint5_3(raw),
active: Endpoint5_4(raw),
get: Endpoint5_5(raw),
remove: Endpoint5_6(raw),
fork: Endpoint5_7(raw),
switchAgent: Endpoint5_8(raw),
switchModel: Endpoint5_9(raw),
rename: Endpoint5_10(raw),
move: Endpoint5_11(raw),
prompt: Endpoint5_12(raw),
command: Endpoint5_13(raw),
skill: Endpoint5_14(raw),
synthetic: Endpoint5_15(raw),
shell: Endpoint5_16(raw),
compact: Endpoint5_17(raw),
wait: Endpoint5_18(raw),
revert: { stage: Endpoint5_19(raw), clear: Endpoint5_20(raw), commit: Endpoint5_21(raw) },
context: Endpoint5_22(raw),
pending: { list: Endpoint5_23(raw) },
instructions: { entry: { list: Endpoint5_24(raw), put: Endpoint5_25(raw), remove: Endpoint5_26(raw) } },
generate: Endpoint5_27(raw),
log: Endpoint5_28(raw),
interrupt: Endpoint5_29(raw),
background: Endpoint5_30(raw),
message: Endpoint5_31(raw),
active: Endpoint5_2(raw),
get: Endpoint5_3(raw),
remove: Endpoint5_4(raw),
fork: Endpoint5_5(raw),
switchAgent: Endpoint5_6(raw),
switchModel: Endpoint5_7(raw),
rename: Endpoint5_8(raw),
move: Endpoint5_9(raw),
prompt: Endpoint5_10(raw),
command: Endpoint5_11(raw),
skill: Endpoint5_12(raw),
synthetic: Endpoint5_13(raw),
shell: Endpoint5_14(raw),
compact: Endpoint5_15(raw),
wait: Endpoint5_16(raw),
revert: { stage: Endpoint5_17(raw), clear: Endpoint5_18(raw), commit: Endpoint5_19(raw) },
context: Endpoint5_20(raw),
pending: { list: Endpoint5_21(raw) },
instructions: { entry: { list: Endpoint5_22(raw), put: Endpoint5_23(raw), remove: Endpoint5_24(raw) } },
generate: Endpoint5_25(raw),
log: Endpoint5_26(raw),
interrupt: Endpoint5_27(raw),
background: Endpoint5_28(raw),
message: Endpoint5_29(raw),
})
const Endpoint6_0 = (raw: RawClient["server.message"]) => (input: Endpoint6_0Input) =>
@@ -15,10 +15,6 @@ import type {
SessionListOutput,
SessionCreateInput,
SessionCreateOutput,
SessionImportInput,
SessionImportOutput,
SessionExportInput,
SessionExportOutput,
SessionActiveOutput,
SessionGetInput,
SessionGetOutput,
@@ -480,30 +476,6 @@ export function make(options: ClientOptions) {
},
requestOptions,
).then((value) => value.data),
import: (input: SessionImportInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionImportOutput }>(
{
method: "POST",
path: `/api/session/import`,
body: { info: input["info"], messages: input["messages"], location: input["location"] },
successStatus: 200,
declaredStatuses: [409, 401, 400],
empty: false,
},
requestOptions,
).then((value) => value.data),
export: (input: SessionExportInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionExportOutput }>(
{
method: "GET",
path: `/api/session/${encodeURIComponent(input.sessionID)}/export`,
query: { sanitize: input["sanitize"] },
successStatus: 200,
declaredStatuses: [404, 500, 401, 400],
empty: false,
},
requestOptions,
).then((value) => value.data),
active: (requestOptions?: RequestOptions) =>
request<{ readonly data: SessionActiveOutput }>(
{
+44 -793
View File
@@ -35,6 +35,18 @@ export type FileDiffInfo = {
status: "added" | "deleted" | "modified"
}
export type SessionActive = { type: "running" }
export type PromptBase64 = string
export type PromptFileSource = { type: "inline" } | { type: "uri"; uri: string }
export type PromptMention = { start: number; end: number; text: string }
export type SessionPendingSyntheticData = { text: string; description?: string; metadata?: { [x: string]: JsonValue } }
export type SessionPendingCompaction = { id: string; sessionID: string; timeCreated: number; type: "compaction" }
export type SessionMessageAgentSelected = {
id: string
metadata?: { [x: string]: JsonValue }
@@ -43,12 +55,6 @@ export type SessionMessageAgentSelected = {
agent: string
}
export type PromptBase64 = string
export type PromptFileSource = { type: "inline" } | { type: "uri"; uri: string }
export type PromptMention = { start: number; end: number; text: string }
export type SessionMessageSynthetic = {
id: string
metadata?: { [x: string]: JsonValue }
@@ -126,12 +132,6 @@ export type SessionMessageCompactionCompleted = {
recent: string
}
export type SessionActive = { type: "running" }
export type SessionPendingSyntheticData = { text: string; description?: string; metadata?: { [x: string]: JsonValue } }
export type SessionPendingCompaction = { id: string; sessionID: string; timeCreated: number; type: "compaction" }
export type InstructionEntryKey = string
export type SessionGenerateResponse = { data: { text: string } }
@@ -1050,6 +1050,15 @@ export type PromptFileAttachment = {
export type PromptAgentAttachment = { name: string; mention?: PromptMention }
export type SessionPendingSynthetic = {
id: string
sessionID: string
timeCreated: number
type: "synthetic"
data: SessionPendingSyntheticData
delivery: "steer" | "queue"
}
export type SessionMessageAssistantText = { type: "text"; text: string; state?: SessionMessageProviderState }
export type SessionMessageAssistantReasoning = {
@@ -1121,15 +1130,6 @@ export type SessionCompactionFailed = {
data: { sessionID: string; reason: "auto" | "manual"; error: SessionStructuredError; inputID?: string }
}
export type SessionPendingSynthetic = {
id: string
sessionID: string
timeCreated: number
type: "synthetic"
data: SessionPendingSyntheticData
delivery: "steer" | "queue"
}
export type InstructionEntryInfo = { key: InstructionEntryKey; value: JsonValue }
export type SessionPendingSyntheticMessage = {
@@ -1530,6 +1530,13 @@ export type SessionRevertStaged = {
data: { sessionID: string; revert: SessionRevert }
}
export type SessionPendingUserData = {
text: string
files?: Array<PromptFileAttachment>
agents?: Array<PromptAgentAttachment>
metadata?: { [x: string]: JsonValue }
}
export type SessionMessageUser = {
id: string
metadata?: { [x: string]: JsonValue }
@@ -1540,13 +1547,6 @@ export type SessionMessageUser = {
type: "user"
}
export type SessionPendingUserData = {
text: string
files?: Array<PromptFileAttachment>
agents?: Array<PromptAgentAttachment>
metadata?: { [x: string]: JsonValue }
}
export type SessionPendingUserData1 = {
text: string
files?: Array<PromptFileAttachment>
@@ -1838,8 +1838,6 @@ export type SessionEventDurable =
| SessionRevertCommitted
| SessionUsageRecorded
export type SessionTransferData = { info: SessionInfo; messages: Array<SessionMessageInfo> }
export type SessionMessagesResponse = {
data: Array<SessionMessageInfo>
cursor: { previous?: string | null; next?: string | null }
@@ -1959,14 +1957,6 @@ export type InvalidCursorError = { readonly _tag: "InvalidCursorError"; readonly
export const isInvalidCursorError = (value: unknown): value is InvalidCursorError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "InvalidCursorError"
export type ConflictError = {
readonly _tag: "ConflictError"
readonly message: string
readonly resource?: string | undefined
}
export const isConflictError = (value: unknown): value is ConflictError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ConflictError"
export type SessionNotFoundError = {
readonly _tag: "SessionNotFoundError"
readonly sessionID: string
@@ -1975,14 +1965,6 @@ export type SessionNotFoundError = {
export const isSessionNotFoundError = (value: unknown): value is SessionNotFoundError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "SessionNotFoundError"
export type UnknownError = {
readonly _tag: "UnknownError"
readonly message: string
readonly ref?: string | undefined
}
export const isUnknownError = (value: unknown): value is UnknownError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnknownError"
export type MessageNotFoundError = {
readonly _tag: "MessageNotFoundError"
readonly sessionID: string
@@ -1992,6 +1974,14 @@ export type MessageNotFoundError = {
export const isMessageNotFoundError = (value: unknown): value is MessageNotFoundError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "MessageNotFoundError"
export type ConflictError = {
readonly _tag: "ConflictError"
readonly message: string
readonly resource?: string | undefined
}
export const isConflictError = (value: unknown): value is ConflictError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ConflictError"
export type CommandNotFoundError = {
readonly _tag: "CommandNotFoundError"
readonly command: string
@@ -2032,6 +2022,14 @@ export type SessionBusyError = {
export const isSessionBusyError = (value: unknown): value is SessionBusyError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "SessionBusyError"
export type UnknownError = {
readonly _tag: "UnknownError"
readonly message: string
readonly ref?: string | undefined
}
export const isUnknownError = (value: unknown): value is UnknownError =>
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnknownError"
export type InstructionEntryValueTooLargeError = {
readonly _tag: "InstructionEntryValueTooLargeError"
readonly actualBytes: number
@@ -2309,753 +2307,6 @@ export type SessionCreateInput = {
export type SessionCreateOutput = { data: SessionInfo }["data"]
export type SessionImportInput = {
readonly info: {
readonly info: {
readonly id: string
readonly parentID?: string
readonly fork?: {
readonly sessionID: string
readonly boundary:
| { readonly type: "before"; readonly messageID: string }
| { readonly type: "through"; readonly messageID: string }
}
readonly projectID: string
readonly agent?: string
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly cost: number
readonly tokens: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number }
readonly title?: string
readonly location: { readonly directory: string; readonly workspaceID?: string }
readonly subpath?: string
readonly revert?: {
readonly messageID: string
readonly partID?: string
readonly snapshot?: string
readonly files?: ReadonlyArray<{
readonly file: string
readonly patch: string
readonly additions: number
readonly deletions: number
readonly status: "added" | "deleted" | "modified"
}>
}
}
readonly messages: ReadonlyArray<
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "agent-switched"
readonly agent: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "model-switched"
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly previous?: { readonly id: string; readonly providerID: string; readonly variant?: string }
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly files?: ReadonlyArray<{
readonly data: string
readonly mime: string
readonly source: { readonly type: "inline" } | { readonly type: "uri"; readonly uri: string }
readonly name?: string
readonly description?: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly type: "user"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly description?: string
readonly type: "synthetic"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "system"
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "skill"
readonly skill: string
readonly name: string
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "shell"
readonly shellID: string
readonly command: string
readonly status: "running" | "exited" | "timeout" | "killed"
readonly exit?: number | "Infinity" | "-Infinity" | "NaN"
readonly output?: {
readonly output: string
readonly cursor: number
readonly size: number
readonly truncated: boolean
}
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "assistant"
readonly agent: string
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly text: string; readonly state?: { readonly [x: string]: JsonValue } }
| {
readonly type: "reasoning"
readonly text: string
readonly state?: { readonly [x: string]: JsonValue }
readonly time?: { readonly created: number; readonly completed?: number }
}
| {
readonly type: "tool"
readonly id: string
readonly name: string
readonly executed?: boolean
readonly providerState?: { readonly [x: string]: JsonValue }
readonly providerResultState?: { readonly [x: string]: JsonValue }
readonly state:
| { readonly status: "streaming"; readonly input: string }
| {
readonly status: "running"
readonly input: { readonly [x: string]: JsonValue }
readonly metadata: { readonly [x: string]: JsonValue }
}
| {
readonly status: "completed"
readonly input: { readonly [x: string]: JsonValue }
readonly content: readonly [
(
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
),
...Array<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
>,
]
readonly metadata?: { readonly [x: string]: JsonValue }
}
| {
readonly status: "error"
readonly input: { readonly [x: string]: JsonValue }
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
readonly content?: readonly [
(
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
),
...Array<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
>,
]
readonly metadata?: { readonly [x: string]: JsonValue }
}
readonly time: { readonly created: number; readonly ran?: number; readonly completed?: number }
}
>
readonly snapshot?: { readonly start?: string; readonly end?: string; readonly files?: ReadonlyArray<string> }
readonly finish?: "stop" | "length" | "tool-calls" | "content-filter" | "error" | "unknown"
readonly cost?: number
readonly tokens?: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly error?: { readonly type: string; readonly message: string; readonly status?: number }
readonly retry?: {
readonly attempt: number
readonly at: number
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
}
}
| (
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "running"
readonly reason: "auto" | "manual"
readonly summary: string
readonly recent: string
}
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "completed"
readonly reason: "auto" | "manual"
readonly summary: string
readonly recent: string
}
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "failed"
readonly reason: "auto" | "manual"
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
}
)
>
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["info"]
readonly messages: {
readonly info: {
readonly id: string
readonly parentID?: string
readonly fork?: {
readonly sessionID: string
readonly boundary:
| { readonly type: "before"; readonly messageID: string }
| { readonly type: "through"; readonly messageID: string }
}
readonly projectID: string
readonly agent?: string
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly cost: number
readonly tokens: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number }
readonly title?: string
readonly location: { readonly directory: string; readonly workspaceID?: string }
readonly subpath?: string
readonly revert?: {
readonly messageID: string
readonly partID?: string
readonly snapshot?: string
readonly files?: ReadonlyArray<{
readonly file: string
readonly patch: string
readonly additions: number
readonly deletions: number
readonly status: "added" | "deleted" | "modified"
}>
}
}
readonly messages: ReadonlyArray<
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "agent-switched"
readonly agent: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "model-switched"
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly previous?: { readonly id: string; readonly providerID: string; readonly variant?: string }
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly files?: ReadonlyArray<{
readonly data: string
readonly mime: string
readonly source: { readonly type: "inline" } | { readonly type: "uri"; readonly uri: string }
readonly name?: string
readonly description?: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly type: "user"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly description?: string
readonly type: "synthetic"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "system"
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "skill"
readonly skill: string
readonly name: string
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "shell"
readonly shellID: string
readonly command: string
readonly status: "running" | "exited" | "timeout" | "killed"
readonly exit?: number | "Infinity" | "-Infinity" | "NaN"
readonly output?: {
readonly output: string
readonly cursor: number
readonly size: number
readonly truncated: boolean
}
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "assistant"
readonly agent: string
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly text: string; readonly state?: { readonly [x: string]: JsonValue } }
| {
readonly type: "reasoning"
readonly text: string
readonly state?: { readonly [x: string]: JsonValue }
readonly time?: { readonly created: number; readonly completed?: number }
}
| {
readonly type: "tool"
readonly id: string
readonly name: string
readonly executed?: boolean
readonly providerState?: { readonly [x: string]: JsonValue }
readonly providerResultState?: { readonly [x: string]: JsonValue }
readonly state:
| { readonly status: "streaming"; readonly input: string }
| {
readonly status: "running"
readonly input: { readonly [x: string]: JsonValue }
readonly metadata: { readonly [x: string]: JsonValue }
}
| {
readonly status: "completed"
readonly input: { readonly [x: string]: JsonValue }
readonly content: readonly [
(
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
),
...Array<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
>,
]
readonly metadata?: { readonly [x: string]: JsonValue }
}
| {
readonly status: "error"
readonly input: { readonly [x: string]: JsonValue }
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
readonly content?: readonly [
(
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
),
...Array<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
>,
]
readonly metadata?: { readonly [x: string]: JsonValue }
}
readonly time: { readonly created: number; readonly ran?: number; readonly completed?: number }
}
>
readonly snapshot?: { readonly start?: string; readonly end?: string; readonly files?: ReadonlyArray<string> }
readonly finish?: "stop" | "length" | "tool-calls" | "content-filter" | "error" | "unknown"
readonly cost?: number
readonly tokens?: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly error?: { readonly type: string; readonly message: string; readonly status?: number }
readonly retry?: {
readonly attempt: number
readonly at: number
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
}
}
| (
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "running"
readonly reason: "auto" | "manual"
readonly summary: string
readonly recent: string
}
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "completed"
readonly reason: "auto" | "manual"
readonly summary: string
readonly recent: string
}
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "failed"
readonly reason: "auto" | "manual"
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
}
)
>
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["messages"]
readonly location?: {
readonly info: {
readonly id: string
readonly parentID?: string
readonly fork?: {
readonly sessionID: string
readonly boundary:
| { readonly type: "before"; readonly messageID: string }
| { readonly type: "through"; readonly messageID: string }
}
readonly projectID: string
readonly agent?: string
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly cost: number
readonly tokens: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number }
readonly title?: string
readonly location: { readonly directory: string; readonly workspaceID?: string }
readonly subpath?: string
readonly revert?: {
readonly messageID: string
readonly partID?: string
readonly snapshot?: string
readonly files?: ReadonlyArray<{
readonly file: string
readonly patch: string
readonly additions: number
readonly deletions: number
readonly status: "added" | "deleted" | "modified"
}>
}
}
readonly messages: ReadonlyArray<
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "agent-switched"
readonly agent: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "model-switched"
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly previous?: { readonly id: string; readonly providerID: string; readonly variant?: string }
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly files?: ReadonlyArray<{
readonly data: string
readonly mime: string
readonly source: { readonly type: "inline" } | { readonly type: "uri"; readonly uri: string }
readonly name?: string
readonly description?: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly type: "user"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly description?: string
readonly type: "synthetic"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "system"
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "skill"
readonly skill: string
readonly name: string
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "shell"
readonly shellID: string
readonly command: string
readonly status: "running" | "exited" | "timeout" | "killed"
readonly exit?: number | "Infinity" | "-Infinity" | "NaN"
readonly output?: {
readonly output: string
readonly cursor: number
readonly size: number
readonly truncated: boolean
}
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "assistant"
readonly agent: string
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly text: string; readonly state?: { readonly [x: string]: JsonValue } }
| {
readonly type: "reasoning"
readonly text: string
readonly state?: { readonly [x: string]: JsonValue }
readonly time?: { readonly created: number; readonly completed?: number }
}
| {
readonly type: "tool"
readonly id: string
readonly name: string
readonly executed?: boolean
readonly providerState?: { readonly [x: string]: JsonValue }
readonly providerResultState?: { readonly [x: string]: JsonValue }
readonly state:
| { readonly status: "streaming"; readonly input: string }
| {
readonly status: "running"
readonly input: { readonly [x: string]: JsonValue }
readonly metadata: { readonly [x: string]: JsonValue }
}
| {
readonly status: "completed"
readonly input: { readonly [x: string]: JsonValue }
readonly content: readonly [
(
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
),
...Array<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
>,
]
readonly metadata?: { readonly [x: string]: JsonValue }
}
| {
readonly status: "error"
readonly input: { readonly [x: string]: JsonValue }
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
readonly content?: readonly [
(
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
),
...Array<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
>,
]
readonly metadata?: { readonly [x: string]: JsonValue }
}
readonly time: { readonly created: number; readonly ran?: number; readonly completed?: number }
}
>
readonly snapshot?: { readonly start?: string; readonly end?: string; readonly files?: ReadonlyArray<string> }
readonly finish?: "stop" | "length" | "tool-calls" | "content-filter" | "error" | "unknown"
readonly cost?: number
readonly tokens?: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly error?: { readonly type: string; readonly message: string; readonly status?: number }
readonly retry?: {
readonly attempt: number
readonly at: number
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
}
}
| (
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "running"
readonly reason: "auto" | "manual"
readonly summary: string
readonly recent: string
}
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "completed"
readonly reason: "auto" | "manual"
readonly summary: string
readonly recent: string
}
| {
readonly type: "compaction"
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly status: "failed"
readonly reason: "auto" | "manual"
readonly error: { readonly type: string; readonly message: string; readonly status?: number }
}
)
>
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["location"]
}
export type SessionImportOutput = { data: SessionInfo }["data"]
export type SessionExportInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly sanitize?: { readonly sanitize?: boolean | undefined }["sanitize"]
}
export type SessionExportOutput = { data: SessionTransferData }["data"]
export type SessionActiveOutput = { data: { [x: string]: SessionActive } }["data"]
export type SessionGetInput = { readonly sessionID: { readonly sessionID: string }["sessionID"] }
-49
View File
@@ -1,49 +0,0 @@
import { create } from "@opencode-ai/schema/identifier"
const prefixes = {
job: "job",
event: "evt",
session: "ses",
message: "msg",
permission: "per",
question: "que",
part: "prt",
pty: "pty",
tool: "tool",
workspace: "wrk",
} as const
export function ascending(prefix: keyof typeof prefixes, given?: string) {
return generateID(prefix, "ascending", given)
}
export function descending(prefix: keyof typeof prefixes, given?: string) {
return generateID(prefix, "descending", given)
}
function generateID(prefix: keyof typeof prefixes, direction: "descending" | "ascending", given?: string): string {
if (!given) {
return createID(prefixes[prefix], direction)
}
if (!given.startsWith(prefixes[prefix])) {
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
}
return given
}
function createID(prefix: string, direction: "descending" | "ascending", timestamp?: number): string {
return prefix + "_" + create(direction === "descending", timestamp)
}
export { createID as create }
/** Extract timestamp from an ascending ID. Does not work with descending IDs. */
export function timestamp(id: string): number {
const prefix = id.split("_")[0]
const hex = id.slice(prefix.length + 1, prefix.length + 13)
const encoded = BigInt("0x" + hex)
return Number(encoded / BigInt(0x1000))
}
export * as Identifier from "./id"
+2 -2
View File
@@ -1,8 +1,8 @@
export * as Job from "./job"
import { Cause, Clock, Context, Deferred, Effect, Exit, Layer, Scope, SynchronizedRef } from "effect"
import { JobID } from "@opencode-ai/schema/job-id"
import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
import { Identifier } from "./id/id"
import { SessionSchema } from "./session/schema"
export type Status = "running" | "completed" | "error" | "cancelled"
@@ -202,7 +202,7 @@ export const make = Effect.gen(function* () {
const start: Interface["start"] = Effect.fn("Job.start")(function* (input) {
return yield* Effect.uninterruptibleMask((restore) =>
Effect.gen(function* () {
const id = input.id ?? Identifier.ascending("job")
const id = input.id ?? JobID.create()
const started_at = yield* Clock.currentTimeMillis
const done = yield* Deferred.make<Info>()
const backgrounded = yield* Deferred.make<Info>()
@@ -1,4 +1,5 @@
import type { IntegrationOAuthMethodRegistration } from "@opencode-ai/plugin/effect/integration"
import { shouldUseResponsesApi } from "@opencode-ai/ai/providers/github-copilot"
import { Effect, Option, Schema, Semaphore, Stream } from "effect"
import { Catalog } from "../../catalog"
import { Credential } from "../../credential"
@@ -140,14 +141,6 @@ const oauth = (app: App.Info) => ({
}),
}) satisfies IntegrationOAuthMethodRegistration
function shouldUseResponses(modelID: string) {
// Copilot supports Responses for GPT-5 class models, except mini variants
// which still need the chat-completions endpoint.
const match = /^gpt-(\d+)/.exec(modelID)
if (!match) return false
return Number(match[1]) >= 5 && !modelID.startsWith("gpt-5-mini")
}
export const GithubCopilotPlugin = define({
id: "opencode.provider.github-copilot",
effect: Effect.fn(function* (ctx) {
@@ -269,7 +262,7 @@ export const GithubCopilotPlugin = define({
return
}
const id = evt.model.modelID ?? evt.model.id
evt.language = shouldUseResponses(id) ? evt.sdk.responses(id) : evt.sdk.chat(id)
evt.language = shouldUseResponsesApi(id) ? evt.sdk.responses(id) : evt.sdk.chat(id)
}),
)
}),
+2 -2
View File
@@ -92,7 +92,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/RepositoryCache") {}
export function isError(error: unknown): error is Error {
function isError(error: unknown): error is Error {
return (
error instanceof InvalidBranchError ||
error instanceof CloneFailedError ||
@@ -104,7 +104,7 @@ export function isError(error: unknown): error is Error {
)
}
export const validateBranch = Effect.fn("RepositoryCache.validateBranch")(function* (branch: string) {
const validateBranch = Effect.fn("RepositoryCache.validateBranch")(function* (branch: string) {
return yield* Effect.try({
try: () => Repository.validateBranch(branch),
catch: (error) => new InvalidBranchError({ branch, message: errorMessage(error) }),
-323
View File
@@ -1,323 +0,0 @@
export * as SessionTransfer from "./transfer"
import { SessionTransfer } from "@opencode-ai/schema/session-transfer"
import { Tool } from "@opencode-ai/schema/tool"
import { eq, isNotNull, isNull, ne, or } from "drizzle-orm"
import { Context, DateTime, Effect, Layer, Schema } from "effect"
import path from "path"
import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
import { App } from "../app"
import { Bus } from "../bus"
import { Database } from "../database/database"
import { Location } from "../location"
import { Project } from "../project"
import { ProjectTable } from "../project/sql"
import { AbsolutePath, RelativePath } from "../schema"
import { Session } from "../session"
import { Slug } from "../util/slug"
import { SessionEvent } from "./event"
import { SessionMessage } from "./message"
import { SessionProjector } from "./projector"
import { SessionMessageTable, SessionTable } from "./sql"
export const Data = SessionTransfer.Data
export type Data = SessionTransfer.Data
export class ImportConflictError extends Schema.TaggedErrorClass<ImportConflictError>()(
"SessionTransfer.ImportConflictError",
{ sessionID: Session.ID },
) {}
export interface Interface {
readonly export: (input: {
sessionID: Session.ID
sanitize?: boolean
}) => Effect.Effect<Data, Session.NotFoundError | Session.MessageDecodeError>
readonly import: (input: {
data: Data
location: Location.Ref
}) => Effect.Effect<Session.Info, ImportConflictError>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionTransfer") {}
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const app = yield* App.Metadata
const bus = yield* Bus.Service
const { db } = yield* Database.Service
const projects = yield* Project.Service
const sessions = yield* Session.Service
const encodeMessage = Schema.encodeSync(SessionMessage.Info)
const persistProject = (project: Project.Resolved) => {
const vcs = project.vcs?.type
return db
.insert(ProjectTable)
.values({ id: project.id, worktree: project.canonical, vcs, sandboxes: [] })
.onConflictDoUpdate({
target: ProjectTable.id,
set: { worktree: project.canonical, vcs: vcs ?? null },
setWhere: or(
ne(ProjectTable.worktree, project.canonical),
vcs ? or(isNull(ProjectTable.vcs), ne(ProjectTable.vcs, vcs)) : isNotNull(ProjectTable.vcs),
),
})
.run()
.pipe(Effect.orDie)
}
return Service.of({
export: Effect.fn("SessionTransfer.export")(function* (input) {
const data = {
info: yield* sessions.get(input.sessionID),
messages: yield* sessions.messages({ sessionID: input.sessionID, order: "asc" }),
}
return input.sanitize ? sanitize(data) : data
}),
import: Effect.fn("SessionTransfer.import")(function* (input) {
const sessionID = input.data.info.id
const recorded = yield* db
.select({ id: SessionTable.id })
.from(SessionTable)
.where(eq(SessionTable.id, sessionID))
.get()
.pipe(Effect.orDie)
if (recorded) return yield* new ImportConflictError({ sessionID })
const project = yield* projects.resolve(input.location.directory)
yield* persistProject(project)
const messages = input.data.messages.map((message, index) => {
const encoded = encodeMessage(message)
const { id: _, type, ...data } = encoded
return {
id: message.id,
session_id: sessionID,
type,
seq: index + 1,
time_created: DateTime.toEpochMillis(message.time.created),
data,
}
})
yield* bus
.publish(
SessionEvent.Created,
{
sessionID,
slug: Slug.create(),
version: app.version,
projectID: project.id,
location: input.location,
subpath: RelativePath.make(path.relative(project.directory, input.location.directory).replaceAll("\\", "/")),
title: input.data.info.title,
agent: input.data.info.agent,
model: input.data.info.model,
},
{
location: input.location,
commit: (seq) =>
Effect.gen(function* () {
if (messages.length > 0) {
yield* db.insert(SessionMessageTable).values(messages).run().pipe(Effect.orDie)
yield* Bus.reserveSequence(db, sessionID, seq + messages.length)
}
yield* db
.update(SessionTable)
.set({
cost: input.data.info.cost,
tokens_input: input.data.info.tokens.input,
tokens_output: input.data.info.tokens.output,
tokens_reasoning: input.data.info.tokens.reasoning,
tokens_cache_read: input.data.info.tokens.cache.read,
tokens_cache_write: input.data.info.tokens.cache.write,
time_created: DateTime.toEpochMillis(input.data.info.time.created),
time_updated: DateTime.toEpochMillis(input.data.info.time.updated),
time_archived: input.data.info.time.archived
? DateTime.toEpochMillis(input.data.info.time.archived)
: null,
})
.where(eq(SessionTable.id, sessionID))
.run()
.pipe(Effect.orDie)
}),
},
)
.pipe(
Effect.catchDefect((defect) =>
defect instanceof SessionProjector.SessionAlreadyProjected
? Effect.fail(new ImportConflictError({ sessionID }))
: Effect.die(defect),
),
)
return yield* sessions.get(sessionID).pipe(Effect.orDie)
}),
})
}),
)
export const node = makeGlobalNode({
service: Service,
layer,
deps: [App.node, Bus.node, Database.node, Project.node, Session.node],
})
function redact(kind: string, id: string, value: string) {
return value.trim() ? `[redacted:${kind}:${id}]` : value
}
function metadata(kind: string, id: string, value: Readonly<Record<string, unknown>> | undefined) {
if (!value) return value
return Object.keys(value).length > 0 ? { redacted: `${kind}:${id}` } : value
}
function sanitize(data: Data): Data {
return {
info: {
...data.info,
title: data.info.title === undefined ? undefined : redact("session-title", data.info.id, data.info.title),
location: {
...data.info.location,
directory: AbsolutePath.make(`/${redact("session-directory", data.info.id, data.info.location.directory)}`),
},
revert: data.info.revert
? {
...data.info.revert,
files: data.info.revert.files?.map((file, index) => ({
...file,
file: redact("revert-file", String(index), file.file),
patch: redact("revert-patch", String(index), file.patch),
})),
}
: undefined,
},
messages: data.messages.map(sanitizeMessage),
}
}
function sanitizeMessage(message: SessionMessage.Info): SessionMessage.Info {
const meta = metadata("message-metadata", message.id, message.metadata)
if (message.type === "user")
return {
...message,
metadata: meta,
text: redact("text", message.id, message.text),
files: message.files?.map((file, index) => ({
...file,
data: "",
source: { type: "inline" },
name: file.name === undefined ? undefined : redact("file-name", String(index), file.name),
description:
file.description === undefined ? undefined : redact("file-description", String(index), file.description),
mention: file.mention
? { ...file.mention, text: redact("file-mention", String(index), file.mention.text) }
: undefined,
})),
agents: message.agents?.map((agent, index) => ({
...agent,
name: redact("agent-name", String(index), agent.name),
mention: agent.mention
? { ...agent.mention, text: redact("agent-mention", String(index), agent.mention.text) }
: undefined,
})),
}
if (message.type === "synthetic")
return {
...message,
metadata: meta,
text: redact("synthetic", message.id, message.text),
description:
message.description === undefined
? undefined
: redact("synthetic-description", message.id, message.description),
}
if (message.type === "system")
return { ...message, metadata: meta, text: redact("system", message.id, message.text) }
if (message.type === "skill") return { ...message, metadata: meta, text: redact("skill", message.id, message.text) }
if (message.type === "shell")
return {
...message,
metadata: meta,
command: redact("shell-command", message.id, message.command),
output: message.output
? { ...message.output, output: redact("shell-output", message.id, message.output.output) }
: undefined,
}
if (message.type === "assistant")
return {
...message,
metadata: meta,
content: message.content.map((content) => {
if (content.type === "text")
return {
...content,
text: redact("text", message.id, content.text),
state: content.state ? { redacted: `text-state:${message.id}` } : undefined,
}
if (content.type === "reasoning")
return {
...content,
text: redact("reasoning", message.id, content.text),
state: content.state ? { redacted: `reasoning-state:${message.id}` } : undefined,
}
return {
...content,
providerState: content.providerState ? { redacted: `tool-provider-state:${message.id}` } : undefined,
providerResultState: content.providerResultState
? { redacted: `tool-provider-result-state:${message.id}` }
: undefined,
state: sanitizeToolState(message.id, content.state),
}
}),
}
if (message.type === "compaction") {
if (message.status === "failed")
return {
...message,
metadata: meta,
}
return {
...message,
metadata: meta,
summary: redact("compaction-summary", message.id, message.summary),
recent: redact("compaction-recent", message.id, message.recent),
}
}
return { ...message, metadata: meta }
}
function sanitizeToolState(id: string, state: SessionMessage.ToolState): SessionMessage.ToolState {
if (state.status === "streaming") return { ...state, input: redact("tool-input", id, state.input) }
if (state.status === "running")
return { ...state, input: { redacted: `tool-input:${id}` }, metadata: { redacted: `tool-metadata:${id}` } }
const meta = state.metadata === undefined ? undefined : { redacted: `tool-metadata:${id}` }
if (state.status === "completed")
return {
...state,
input: { redacted: `tool-input:${id}` },
content: [
sanitizeToolContent(id, state.content[0]),
...state.content.slice(1).map((item) => sanitizeToolContent(id, item)),
],
metadata: meta,
}
return {
...state,
input: { redacted: `tool-input:${id}` },
content: state.content
? [
sanitizeToolContent(id, state.content[0]),
...state.content.slice(1).map((item) => sanitizeToolContent(id, item)),
]
: undefined,
metadata: meta,
}
}
function sanitizeToolContent(id: string, content: Tool.Content): Tool.Content {
if (content.type === "text") return { ...content, text: redact("tool-output", id, content.text) }
return {
...content,
uri: redact("tool-file-uri", id, content.uri),
name: content.name === undefined ? undefined : redact("tool-file-name", id, content.name),
}
}
-1
View File
@@ -1 +0,0 @@
export * as Identifier from "@opencode-ai/schema/identifier"
-8
View File
@@ -21,11 +21,3 @@ export function getFilenameTruncated(path: string | undefined, maxLength: number
if (available <= 0) return filename.slice(0, maxLength - 1) + "…"
return filename.slice(0, available) + "…" + ext
}
export function truncateMiddle(text: string, maxLength: number = 20) {
if (text.length <= maxLength) return text
const available = maxLength - 1 // -1 for ellipsis
const start = Math.ceil(available / 2)
const end = Math.floor(available / 2)
return text.slice(0, start) + "…" + text.slice(-end)
}
@@ -4,7 +4,6 @@ import path from "path"
import { pathToFileURL } from "url"
import { Effect, Layer } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
import { Global } from "@opencode-ai/util/global"
import { Repository } from "@opencode-ai/core/repository"
import { RepositoryCache } from "@opencode-ai/core/repository-cache"
+1 -83
View File
@@ -23,7 +23,6 @@ import { SessionPending } from "@opencode-ai/core/session/pending"
import { SessionEvent } from "@opencode-ai/core/session/event"
import { SessionTable } from "@opencode-ai/core/session/sql"
import { SessionStore } from "@opencode-ai/core/session/store"
import { SessionTransfer } from "@opencode-ai/core/session/transfer"
import { Workspace } from "@opencode-ai/core/workspace"
import { testEffect } from "./lib/effect"
import { tmpdir } from "./fixture/tmpdir"
@@ -38,14 +37,7 @@ const projects = Layer.succeed(
)
const it = testEffect(
AppNodeBuilder.build(
LayerNode.group([
Database.node,
Bus.node,
SessionProjector.node,
SessionStore.node,
Session.node,
SessionTransfer.node,
]),
LayerNode.group([Database.node, Bus.node, SessionProjector.node, SessionStore.node, Session.node]),
[
[Bus.node, Bus.configured({ persist: true })],
[Project.node, projects],
@@ -748,77 +740,3 @@ describe("Session.create", () => {
}),
)
})
describe("SessionTransfer", () => {
it.effect("imports projected messages and reserves their aggregate sequence", () =>
Effect.gen(function* () {
const session = yield* Session.Service
const transfer = yield* SessionTransfer.Service
const bus = yield* Bus.Service
const { db } = yield* Database.Service
const template = yield* session.create({ location, title: "Exported" })
const sessionID = Session.ID.create()
const sourceMessageID = SessionMessage.ID.create()
const errorMessageID = SessionMessage.ID.create()
const imported = yield* transfer.import({
data: {
info: { ...template, id: sessionID },
messages: [
{
id: sourceMessageID,
type: "user",
text: "Imported message",
time: { created: DateTime.makeUnsafe(100) },
},
{
id: errorMessageID,
type: "compaction",
status: "failed",
reason: "manual",
error: { type: "test_error", message: "Original error" },
time: { created: DateTime.makeUnsafe(101) },
},
],
},
location,
})
const messages = yield* session.messages({ sessionID, order: "asc" })
expect(imported).toMatchObject({ id: sessionID, title: "Exported", location })
expect(messages).toMatchObject([
{ id: sourceMessageID, type: "user", text: "Imported message" },
{ id: errorMessageID, type: "compaction", error: { type: "test_error", message: "Original error" } },
])
expect(yield* Bus.latestSequence(db, sessionID)).toBe(2)
expect((yield* transfer.export({ sessionID })).messages).toEqual(messages)
expect((yield* transfer.export({ sessionID, sanitize: true })).messages).toMatchObject([
{ id: sourceMessageID, text: `[redacted:text:${sourceMessageID}]` },
{ id: errorMessageID, error: { type: "test_error", message: "Original error" } },
])
yield* session.prompt({ sessionID, text: "Continue", resume: false })
yield* SessionPending.promote(db, bus, sessionID, "steer")
expect((yield* session.messages({ sessionID, order: "asc" })).map((message) => message.type)).toEqual([
"user",
"compaction",
"user",
])
expect(yield* Bus.latestSequence(db, sessionID)).toBe(4)
}),
)
it.effect("rejects an existing session ID without changing its transcript", () =>
Effect.gen(function* () {
const session = yield* Session.Service
const transfer = yield* SessionTransfer.Service
const existing = yield* session.create({ location, title: "Existing" })
const exit = yield* Effect.exit(transfer.import({ data: { info: existing, messages: [] }, location }))
expect(exit._tag).toBe("Failure")
expect((yield* session.get(existing.id)).title).toBe("Existing")
expect(yield* session.messages({ sessionID: existing.id })).toEqual([])
}),
)
})
+1 -1
View File
@@ -23,7 +23,7 @@ import { Permission } from "@opencode-ai/schema/permission"
import { Pty } from "@opencode-ai/schema/pty"
import { Reference } from "@opencode-ai/schema/reference"
import { Skill } from "@opencode-ai/schema/skill"
import { AbsolutePath, DateTimeUtcFromMillis, optional, statics } from "@opencode-ai/schema/schema"
import { AbsolutePath, optional, statics } from "@opencode-ai/schema/schema"
test("Core reuses the canonical shared schemas", async () => {
const schemaAgent = await import("@opencode-ai/schema/agent")
-12
View File
@@ -1,12 +0,0 @@
import { $ } from "bun"
import * as path from "node:path"
import { RUST_TARGET } from "./utils"
if (!RUST_TARGET) throw new Error("RUST_TARGET not defined")
const BUNDLE_DIR = "dist"
const BUNDLES_OUT_DIR = path.join(process.cwd(), "dist/bundles")
await $`mkdir -p ${BUNDLES_OUT_DIR}`
await $`cp -r ${BUNDLE_DIR}/* ${BUNDLES_OUT_DIR}`
+1
View File
@@ -33,6 +33,7 @@
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@opencode-ai/schema": "workspace:*",
"@tailwindcss/vite": "catalog:",
"@typescript/native-preview": "catalog:",
"@types/bun": "catalog:",
+15 -14
View File
@@ -1,11 +1,12 @@
import { describe, expect, test } from "bun:test"
import { descending } from "@opencode-ai/schema/identifier"
import { SessionID } from "@opencode-ai/schema/session-id"
import { Share } from "../../src/core/share"
import { Storage } from "../../src/core/storage"
import { Identifier } from "@opencode-ai/core/util/identifier"
describe.concurrent("core.share", () => {
test("should create a share", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
expect(share.sessionID).toBe(sessionID)
@@ -15,7 +16,7 @@ describe.concurrent("core.share", () => {
})
test("should remove a share as admin", async () => {
const share = await Share.create({ sessionID: Identifier.descending() })
const share = await Share.create({ sessionID: SessionID.create() })
await Share.removeAdmin({ id: share.id })
@@ -23,7 +24,7 @@ describe.concurrent("core.share", () => {
})
test("should sync data to a share", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data: Share.Data[] = [
@@ -45,7 +46,7 @@ describe.concurrent("core.share", () => {
})
test("should sync multiple batches of data", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data1: Share.Data[] = [
@@ -79,7 +80,7 @@ describe.concurrent("core.share", () => {
})
test("should retrieve synced data", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data: Share.Data[] = [
@@ -108,7 +109,7 @@ describe.concurrent("core.share", () => {
})
test("should retrieve data from multiple syncs", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data1: Share.Data[] = [
@@ -154,7 +155,7 @@ describe.concurrent("core.share", () => {
})
test("should return latest data when syncing duplicate parts", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data1: Share.Data[] = [
@@ -192,7 +193,7 @@ describe.concurrent("core.share", () => {
})
test("should return empty array for share with no data", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const result = await Share.data(share.id)
@@ -203,7 +204,7 @@ describe.concurrent("core.share", () => {
})
test("should migrate legacy event data into the snapshot", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data: Share.Data[] = [
{
@@ -213,7 +214,7 @@ describe.concurrent("core.share", () => {
]
await Storage.remove(["share_snapshot", share.id])
await Storage.write(["share_event", share.id, Identifier.descending()], data)
await Storage.write(["share_event", share.id, descending()], data)
const result = await Share.data(share.id)
const snapshot = await Storage.read<{ data: Share.Data[] }>(["share_snapshot", share.id])
@@ -225,7 +226,7 @@ describe.concurrent("core.share", () => {
})
test("should throw error for invalid secret", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data: Share.Data[] = [
@@ -246,7 +247,7 @@ describe.concurrent("core.share", () => {
})
test("should throw error for non-existent share", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const data: Share.Data[] = [
{
type: "part",
@@ -263,7 +264,7 @@ describe.concurrent("core.share", () => {
})
test("should handle different data types", async () => {
const sessionID = Identifier.descending()
const sessionID = SessionID.create()
const share = await Share.create({ sessionID })
const data: Share.Data[] = [
+1 -32
View File
@@ -1,5 +1,4 @@
import { SessionMessage } from "@opencode-ai/schema/session-message"
import { SessionTransfer } from "@opencode-ai/schema/session-transfer"
import { SessionPending } from "@opencode-ai/schema/session-pending"
import { PromptInput } from "@opencode-ai/schema/prompt-input"
import { Session } from "@opencode-ai/schema/session"
@@ -116,7 +115,7 @@ const SessionsQueryCursor = SessionsCursor.annotate({
description: "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response.",
})
export const SessionsQuery = Schema.Struct({
const SessionsQuery = Schema.Struct({
...SessionsQueryFields,
directory: AbsolutePath.pipe(Schema.optional),
project: Project.ID.pipe(Schema.optional),
@@ -164,36 +163,6 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
}),
),
)
.add(
HttpApiEndpoint.post("session.import", "/api/session/import", {
payload: Schema.Struct({
...SessionTransfer.Data.fields,
location: Location.Ref.pipe(Schema.optional),
}),
success: Schema.Struct({ data: Session.Info }),
error: ConflictError,
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.import",
summary: "Import session",
description: "Import a projected session transcript at the requested location.",
}),
),
)
.add(
HttpApiEndpoint.get("session.export", "/api/session/:sessionID/export", {
params: { sessionID: Session.ID },
query: Schema.Struct({ sanitize: BooleanFromString.pipe(Schema.optional) }),
success: Schema.Struct({ data: SessionTransfer.Data }),
error: [SessionNotFoundError, UnknownError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.export",
summary: "Export session",
description: "Export a complete projected session transcript.",
}),
),
)
.add(
HttpApiEndpoint.get("session.active", "/api/session/active", {
success: Schema.Struct({ data: Schema.Record(Session.ID, SessionActive) }),
-1
View File
@@ -25,7 +25,6 @@ export { Vcs } from "./vcs.js"
export { SessionPending } from "./session-pending.js"
export { SessionError } from "./session-error.js"
export { SessionMessage } from "./session-message.js"
export { SessionTransfer } from "./session-transfer.js"
export { Snapshot } from "./snapshot.js"
export { Shell } from "./shell.js"
export { Skill } from "./skill.js"
+9
View File
@@ -0,0 +1,9 @@
import { Schema } from "effect"
import { ascending } from "./identifier.js"
import { statics } from "./schema.js"
export const JobID = Schema.String.check(Schema.isStartsWith("job_")).pipe(
Schema.brand("JobID"),
statics((schema) => ({ create: () => schema.make("job_" + ascending()) })),
)
export type JobID = typeof JobID.Type
-11
View File
@@ -1,11 +0,0 @@
export * as SessionTransfer from "./session-transfer.js"
import { Schema } from "effect"
import { Session } from "./session.js"
import { SessionMessage } from "./session-message.js"
export interface Data extends Schema.Schema.Type<typeof Data> {}
export const Data = Schema.Struct({
info: Session.Info,
messages: Schema.Array(SessionMessage.Info),
}).annotate({ identifier: "SessionTransfer.Data" })
@@ -3,6 +3,7 @@ import { DateTime, Schema } from "effect"
import { Agent } from "../src/agent.js"
import { FileSystem } from "../src/filesystem.js"
import { Form } from "../src/form.js"
import { JobID } from "../src/job-id.js"
import { Mcp } from "../src/mcp.js"
import { Model } from "../src/model.js"
import { Project } from "../src/project.js"
@@ -131,6 +132,7 @@ describe("contract hygiene", () => {
})
test("current ID constructors expose create", () => {
expect(JobID.create()).toStartWith("job_")
expect(Question.ID.create()).toStartWith("que_")
expect(Pty.ID.create()).toStartWith("pty_")
})
-52
View File
@@ -1,5 +1,4 @@
import { Session } from "@opencode-ai/core/session"
import { SessionTransfer } from "@opencode-ai/core/session/transfer"
import { InstructionEntry } from "@opencode-ai/core/session/instruction-entry"
import { DateTime, Effect, Stream } from "effect"
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
@@ -25,7 +24,6 @@ const DefaultSessionsLimit = 50
export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handlers) =>
Effect.gen(function* () {
const session = yield* Session.Service
const transfer = yield* SessionTransfer.Service
return handlers
.handle(
@@ -88,56 +86,6 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
}
}),
)
.handle(
"session.import",
Effect.fn(function* (ctx) {
return {
data: yield* transfer
.import({
data: { info: ctx.payload.info, messages: ctx.payload.messages },
location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) },
})
.pipe(
Effect.catchTag(
"SessionTransfer.ImportConflictError",
(error) =>
new ConflictError({
message: `Session already exists: ${error.sessionID}`,
resource: error.sessionID,
}),
),
),
}
}),
)
.handle(
"session.export",
Effect.fn(function* (ctx) {
return {
data: yield* transfer.export({ sessionID: ctx.params.sessionID, sanitize: ctx.query.sanitize }).pipe(
Effect.catchTag(
"Session.NotFoundError",
(error) =>
new SessionNotFoundError({
sessionID: error.sessionID,
message: `Session not found: ${error.sessionID}`,
}),
),
Effect.catchTag("Session.MessageDecodeError", (error) => {
const ref = `err_${crypto.randomUUID().slice(0, 8)}`
return Effect.logError("failed to decode session message").pipe(
Effect.annotateLogs({ ref, sessionID: error.sessionID, messageID: error.messageID }),
Effect.andThen(
Effect.fail(
new UnknownError({ message: "Unexpected server error. Check server logs for details.", ref }),
),
),
)
}),
),
}
}),
)
.handle(
"session.active",
Effect.fn(function* () {
@@ -2,7 +2,6 @@ import { Effect } from "effect"
import { HttpApiMiddleware } from "effect/unstable/httpapi"
import { InvalidRequestError } from "@opencode-ai/protocol/errors"
import { SchemaErrorMiddleware } from "@opencode-ai/protocol/middleware/schema-error"
export { SchemaErrorMiddleware } from "@opencode-ai/protocol/middleware/schema-error"
const REASON_LIMIT = 1024
-2
View File
@@ -16,7 +16,6 @@ import { PtyTicket } from "@opencode-ai/core/pty/ticket"
import { Pty } from "@opencode-ai/core/pty"
import { Project } from "@opencode-ai/core/project"
import { Session } from "@opencode-ai/core/session"
import { SessionTransfer } from "@opencode-ai/core/session/transfer"
import { Shell } from "@opencode-ai/core/shell"
import { Job } from "@opencode-ai/core/job"
import { MCP } from "@opencode-ai/core/mcp/index"
@@ -52,7 +51,6 @@ const applicationServices = LayerNode.group([
Job.node,
Project.node,
Session.node,
SessionTransfer.node,
PluginRuntime.providerNode,
SdkPlugins.node,
PermissionSaved.node,
-307
View File
@@ -1,307 +0,0 @@
[14:25:48.462] [INFO] storybook v10.2.10
[14:25:48.749] [DEBUG] Getting package.json info for /Users/davidhill/Documents/Local/opencode/packages/storybook/package.json...
[14:25:48.997] [INFO] Starting...
[14:25:49.095] [DEBUG] Starting preview..
[14:25:49.098] [WARN] 🚨 Unable to index files:
- ./../ui/src/components/accordion.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/accordion.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/app-icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/app-icon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/avatar.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/avatar.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/basic-tool.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/basic-tool.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/checkbox.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/checkbox.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/code.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/code.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/collapsible.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/collapsible.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/context-menu.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/context-menu.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/dialog.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/dialog.stories.tsx (line 10, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/diff-changes.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/diff-changes.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/diff-ssr.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/diff-ssr.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/diff.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/diff.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/dock-prompt.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/dock-prompt.stories.tsx (line 15, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/dropdown-menu.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/dropdown-menu.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/favicon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/favicon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/file-icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/file-icon.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/font.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/font.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/hover-card.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/hover-card.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/icon-button.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/icon-button.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/icon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/image-preview.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/image-preview.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/inline-input.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/inline-input.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/keybind.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/keybind.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/line-comment.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/line-comment.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/list.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/list.stories.tsx (line 15, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/logo.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/logo.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/markdown.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/markdown.stories.tsx (line 12, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/message-nav.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/message-nav.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/message-part.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/message-part.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/popover.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/popover.stories.tsx (line 16, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/progress-circle.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/progress-circle.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/progress.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/progress.stories.tsx (line 15, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/provider-icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/provider-icon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/radio-group.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/radio-group.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/resize-handle.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/resize-handle.stories.tsx (line 17, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/select.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/select.stories.tsx (line 16, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/session-review.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/session-review.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/session-turn.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/session-turn.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/spinner.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/spinner.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/sticky-accordion-header.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/sticky-accordion-header.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/switch.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/switch.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/tabs.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/tabs.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/tag.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/tag.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/text-field.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/text-field.stories.tsx (line 14, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/text-shimmer.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/text-shimmer.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/toast.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/toast.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/tooltip.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/tooltip.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/typewriter.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/typewriter.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
[14:25:49.109] [ERROR] Failed to build the preview
[14:25:49.110] [ERROR] Error: Unable to index files:
- ./../ui/src/components/accordion.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/accordion.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/app-icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/app-icon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/avatar.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/avatar.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/basic-tool.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/basic-tool.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/checkbox.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/checkbox.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/code.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/code.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/collapsible.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/collapsible.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/context-menu.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/context-menu.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/dialog.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/dialog.stories.tsx (line 10, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/diff-changes.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/diff-changes.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/diff-ssr.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/diff-ssr.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/diff.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/diff.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/dock-prompt.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/dock-prompt.stories.tsx (line 15, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/dropdown-menu.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/dropdown-menu.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/favicon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/favicon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/file-icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/file-icon.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/font.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/font.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/hover-card.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/hover-card.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/icon-button.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/icon-button.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/icon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/image-preview.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/image-preview.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/inline-input.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/inline-input.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/keybind.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/keybind.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/line-comment.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/line-comment.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/list.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/list.stories.tsx (line 15, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/logo.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/logo.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/markdown.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/markdown.stories.tsx (line 12, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/message-nav.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/message-nav.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/message-part.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/message-part.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/popover.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/popover.stories.tsx (line 16, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/progress-circle.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/progress-circle.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/progress.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/progress.stories.tsx (line 15, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/provider-icon.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/provider-icon.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/radio-group.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/radio-group.stories.tsx (line 13, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/resize-handle.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/resize-handle.stories.tsx (line 17, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/select.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/select.stories.tsx (line 16, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/session-review.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/session-review.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/session-turn.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/session-turn.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/spinner.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/spinner.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/sticky-accordion-header.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/sticky-accordion-header.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/switch.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/switch.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/tabs.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/tabs.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/tag.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/tag.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/text-field.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/text-field.stories.tsx (line 14, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/text-shimmer.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/text-shimmer.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/toast.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/toast.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/tooltip.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/tooltip.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
- ./../ui/src/components/typewriter.stories.tsx: CSF: default export must be an object /Users/davidhill/Documents/Local/opencode/packages/ui/src/components/typewriter.stories.tsx (line 6, col 0)
More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export
at _StoryIndexGenerator.getIndexAndStats (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/core-server/index.js:6085:15)
at async _StoryIndexGenerator.getIndex (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/core-server/index.js:6074:13)
at async getOptimizeDeps (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/@storybook+builder-vite@10.2.10+a2a25316dbcddd7f/node_modules/@storybook/builder-vite/dist/index.js:1862:15)
at async createViteServer (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/@storybook+builder-vite@10.2.10+a2a25316dbcddd7f/node_modules/@storybook/builder-vite/dist/index.js:1888:19)
at async Module.start (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/@storybook+builder-vite@10.2.10+a2a25316dbcddd7f/node_modules/@storybook/builder-vite/dist/index.js:1923:17)
at async storybookDevServer (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/core-server/index.js:7241:83)
at async buildOrThrow (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/core-server/index.js:4504:12)
at async buildDevStandalone (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/core-server/index.js:7611:66)
at async withTelemetry (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/_node-chunks/chunk-S3MWHNYJ.js:218:12)
at async dev (file:///Users/davidhill/Documents/Local/opencode/node_modules/.bun/storybook@10.2.10+4edd68b244e756bb/node_modules/storybook/dist/bin/core.js:2734:3)
[14:25:49.118] [WARN] Broken build, fix the error above.
You may need to refresh the browser.
+16 -7
View File
@@ -824,13 +824,22 @@ export function Session() {
if (options === null) return
const content =
options.format === "markdown"
? formatSessionTranscript(sessionData, messages(), options.thinking)
: JSON.stringify(
await client.api.session.export({ sessionID: sessionData.id, sanitize: options.sanitize }),
null,
2,
) + EOL
options.format === "markdown"
? formatSessionTranscript(sessionData, messages(), options.thinking)
: await (async () => {
const messages: unknown[] = []
let cursor: string | undefined
do {
const page = await client.api.message.list(
cursor
? { sessionID: sessionData.id, limit: 200, cursor }
: { sessionID: sessionData.id, limit: 200, order: "asc" },
)
messages.push(...page.data)
cursor = page.data.length ? (page.cursor.next ?? undefined) : undefined
} while (cursor)
return JSON.stringify({ info: sessionData, messages }, null, 2) + EOL
})()
if (options.action === "copy") {
await clipboard.write?.(content)
+3 -47
View File
@@ -9,11 +9,11 @@ export type ExportFormat = "markdown" | "json"
export type DialogExportOptionsProps = {
defaultThinking: boolean
onConfirm?: (options: { action: "copy" | "export"; format: ExportFormat; thinking: boolean; sanitize: boolean }) => void
onConfirm?: (options: { action: "copy" | "export"; format: ExportFormat; thinking: boolean }) => void
onCancel?: () => void
}
type Active = ExportFormat | "thinking" | "sanitize" | "copy" | "export"
type Active = ExportFormat | "thinking" | "copy" | "export"
export function DialogExportOptions(props: DialogExportOptionsProps) {
const dialog = useDialog()
@@ -22,7 +22,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
const [store, setStore] = createStore({
format: "markdown" as ExportFormat,
thinking: props.defaultThinking,
sanitize: false,
active: "markdown" as Active,
})
@@ -31,7 +30,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
action,
format: store.format,
thinking: store.thinking,
sanitize: store.sanitize,
})
const activate = () => {
@@ -40,7 +38,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
return
}
if (store.active === "thinking") setStore("thinking", !store.thinking)
if (store.active === "sanitize") setStore("sanitize", !store.sanitize)
if (store.active === "copy" || store.active === "export") confirm(store.active)
}
@@ -55,7 +52,7 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
const order: Active[] =
store.format === "markdown"
? ["markdown", "json", "thinking", "copy", "export"]
: ["markdown", "json", "sanitize", "copy", "export"]
: ["markdown", "json", "copy", "export"]
setStore("active", order[(order.indexOf(store.active) + 1) % order.length])
},
},
@@ -156,46 +153,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
</text>
</box>
</Show>
<Show when={store.format === "json"}>
<box
flexDirection="row"
gap={1}
backgroundColor={
store.active === "sanitize"
? theme.background.formfield.focused
: store.sanitize
? theme.background.formfield.selected
: theme.background.formfield.default
}
onMouseUp={() => {
setStore("active", "sanitize")
setStore("sanitize", !store.sanitize)
}}
>
<text
fg={
store.active === "sanitize"
? theme.text.formfield.focused
: store.sanitize
? theme.text.formfield.selected
: theme.text.formfield.default
}
>
{store.sanitize ? "[x]" : "[ ]"}
</text>
<text
fg={
store.active === "sanitize"
? theme.text.formfield.focused
: store.sanitize
? theme.text.formfield.selected
: theme.text.formfield.default
}
>
Sanitize sensitive data
</text>
</box>
</Show>
<box flexDirection="row" justifyContent="flex-end" gap={1} paddingBottom={1}>
<box
paddingLeft={4}
@@ -229,7 +186,6 @@ DialogExportOptions.show = (dialog: DialogContext, defaultThinking: boolean) =>
action: "copy" | "export"
format: ExportFormat
thinking: boolean
sanitize: boolean
} | null>((resolve) => {
dialog.replace(
() => (