mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-09 10:59:49 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a8da2c985 | |||
| d7651519f3 | |||
| 1eb3a43add |
@@ -40,7 +40,7 @@ export default Runtime.handler(
|
|||||||
const sessionID = requested ?? selected?.session.id
|
const sessionID = requested ?? selected?.session.id
|
||||||
if (!sessionID) return
|
if (!sessionID) return
|
||||||
const data = yield* Effect.promise(() =>
|
const data = yield* Effect.promise(() =>
|
||||||
client.sessionTransfer.export({ sessionID, sanitize: selected?.sanitize ?? input.sanitize }),
|
client.session.export({ sessionID, sanitize: selected?.sanitize ?? input.sanitize }),
|
||||||
)
|
)
|
||||||
process.stdout.write(yield* Effect.promise(() => writeExport(data, sessionID, requested !== undefined)))
|
process.stdout.write(yield* Effect.promise(() => writeExport(data, sessionID, requested !== undefined)))
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { isConflictError, OpenCode, type SessionTransferImportInput } from "@opencode-ai/client"
|
import { OpenCode } from "@opencode-ai/client"
|
||||||
import { Service } from "@opencode-ai/client/effect/service"
|
import { Service } from "@opencode-ai/client/effect/service"
|
||||||
|
import { Session } from "@opencode-ai/schema/session"
|
||||||
import { SessionTransfer } from "@opencode-ai/schema/session-transfer"
|
import { SessionTransfer } from "@opencode-ai/schema/session-transfer"
|
||||||
import { Effect, Option, Schema } from "effect"
|
import { Effect, Option, Schema } from "effect"
|
||||||
import { EOL } from "node:os"
|
import { EOL } from "node:os"
|
||||||
@@ -19,11 +20,10 @@ export default Runtime.handler(
|
|||||||
return response.text()
|
return response.text()
|
||||||
})
|
})
|
||||||
: Bun.file(input.file).text(),
|
: Bun.file(input.file).text(),
|
||||||
catch: (cause) =>
|
catch: (cause) => new Error(`Failed to read session data: ${cause instanceof Error ? cause.message : String(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 data = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(SessionTransfer.Data))(text)
|
||||||
const encoded = Schema.encodeSync(SessionTransfer.Data)(data) as SessionTransferImportInput
|
const encoded = Schema.encodeSync(SessionTransfer.Data)(data)
|
||||||
const server = yield* ServerConnection.resolve({
|
const server = yield* ServerConnection.resolve({
|
||||||
server: Option.getOrUndefined(input.server),
|
server: Option.getOrUndefined(input.server),
|
||||||
standalone: input.standalone,
|
standalone: input.standalone,
|
||||||
@@ -37,18 +37,24 @@ export default Runtime.handler(
|
|||||||
location: { directory: path.resolve(Option.getOrElse(input.directory, () => process.cwd())) },
|
location: { directory: path.resolve(Option.getOrElse(input.directory, () => process.cwd())) },
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
const imported = yield* Effect.tryPromise({
|
const response = yield* Effect.promise(() =>
|
||||||
try: () =>
|
fetch(new URL("/api/session/import", server.endpoint.url), {
|
||||||
client.sessionTransfer.import({
|
method: "POST",
|
||||||
|
headers: { ...Service.headers(server.endpoint), "content-type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
...encoded,
|
...encoded,
|
||||||
location: { directory: location.directory, workspaceID: location.workspaceID },
|
location: { directory: location.directory, workspaceID: location.workspaceID },
|
||||||
}),
|
}),
|
||||||
catch: (cause) => cause,
|
}),
|
||||||
}).pipe(Effect.catchIf(isConflictError, () => Effect.succeed(undefined)))
|
)
|
||||||
if (!imported) {
|
if (response.status === 409) {
|
||||||
process.stderr.write(`Session already exists${EOL}`)
|
process.stderr.write(`Session already exists${EOL}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
process.stdout.write(`Imported session: ${imported.id}${EOL}`)
|
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}`)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -94,11 +94,9 @@ async function execute(input: RunCommandInput, prepared: Prepared, endpoint: End
|
|||||||
prepare: async (next) => {
|
prepare: async (next) => {
|
||||||
const selected =
|
const selected =
|
||||||
next.model ??
|
next.model ??
|
||||||
(options.variant
|
(await client.model
|
||||||
? await client.model
|
.default({ location: { directory: next.location.directory, workspace: next.location.workspaceID } })
|
||||||
.default({ location: { directory: next.location.directory, workspace: next.location.workspaceID } })
|
.then((result) => result.data))
|
||||||
.then((result) => result.data)
|
|
||||||
: undefined)
|
|
||||||
const model = selected
|
const model = selected
|
||||||
? {
|
? {
|
||||||
providerID: selected.providerID,
|
providerID: selected.providerID,
|
||||||
@@ -108,7 +106,12 @@ async function execute(input: RunCommandInput, prepared: Prepared, endpoint: End
|
|||||||
: undefined
|
: undefined
|
||||||
if ((options.variant ?? explicit?.variant) && !model)
|
if ((options.variant ?? explicit?.variant) && !model)
|
||||||
throw new RunTargetError("Cannot select a variant before selecting a model", next.session?.id)
|
throw new RunTargetError("Cannot select a variant before selecting a model", next.session?.id)
|
||||||
return { model, agent: next.agent }
|
const agent =
|
||||||
|
next.agent ??
|
||||||
|
(await client.agent
|
||||||
|
.list({ location: { directory: next.location.directory, workspace: next.location.workspaceID } })
|
||||||
|
.then((result) => result.data.find((item) => item.mode !== "subagent" && !item.hidden)?.id))
|
||||||
|
return { model, agent }
|
||||||
},
|
},
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (!(error instanceof RunTargetError)) throw error
|
if (!(error instanceof RunTargetError)) throw error
|
||||||
|
|||||||
@@ -56,13 +56,16 @@ export async function resolveSessionTarget(input: {
|
|||||||
agent: input.agent ?? selected?.agent,
|
agent: input.agent ?? selected?.agent,
|
||||||
signal: input.signal,
|
signal: input.signal,
|
||||||
})
|
})
|
||||||
|
if (!selected && (!prepared.agent || !prepared.model)) {
|
||||||
|
throw new SessionTargetMutationError(new Error("Creating a session requires an agent and model"))
|
||||||
|
}
|
||||||
const session =
|
const session =
|
||||||
selected ??
|
selected ??
|
||||||
(await input.client.session
|
(await input.client.session
|
||||||
.create(
|
.create(
|
||||||
{
|
{
|
||||||
agent: prepared.agent,
|
agent: prepared.agent!,
|
||||||
model: prepared.model,
|
model: prepared.model!,
|
||||||
location: { directory: location.directory, workspaceID: location.workspaceID },
|
location: { directory: location.directory, workspaceID: location.workspaceID },
|
||||||
},
|
},
|
||||||
...requestOptions(input.signal),
|
...requestOptions(input.signal),
|
||||||
|
|||||||
@@ -159,7 +159,14 @@ test("import validates a file and sends it to the resolved location", async () =
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [stdout, , exitCode] = await run(["import", file, "--directory", root, "--server", server.url.toString()])
|
const [stdout, , exitCode] = await run([
|
||||||
|
"import",
|
||||||
|
file,
|
||||||
|
"--directory",
|
||||||
|
root,
|
||||||
|
"--server",
|
||||||
|
server.url.toString(),
|
||||||
|
])
|
||||||
|
|
||||||
expect(exitCode).toBe(0)
|
expect(exitCode).toBe(0)
|
||||||
expect(stdout).toBe(`Imported session: ${info.id}${os.EOL}`)
|
expect(stdout).toBe(`Imported session: ${info.id}${os.EOL}`)
|
||||||
@@ -185,12 +192,7 @@ test("import reports an existing session without a stack trace", async () => {
|
|||||||
project: { id: "global", directory: root, canonical: root },
|
project: { id: "global", directory: root, canonical: root },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (url.pathname === "/api/session/import") {
|
if (url.pathname === "/api/session/import") return new Response("Conflict", { status: 409 })
|
||||||
return Response.json(
|
|
||||||
{ _tag: "ConflictError", message: `Session already exists: ${info.id}`, resource: info.id },
|
|
||||||
{ status: 409 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return new Response("Not found", { status: 404 })
|
return new Response("Not found", { status: 404 })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ describe("session target resolver", () => {
|
|||||||
spyOn(client.location, "get").mockResolvedValue(location("/server", "work_1"))
|
spyOn(client.location, "get").mockResolvedValue(location("/server", "work_1"))
|
||||||
const create = spyOn(client.session, "create").mockImplementation(async (input) => {
|
const create = spyOn(client.session, "create").mockImplementation(async (input) => {
|
||||||
order.push("create")
|
order.push("create")
|
||||||
expect(input).toMatchObject({ agent: "prepared", location: { directory: "/server", workspaceID: "work_1" } })
|
expect(input).toMatchObject({
|
||||||
|
agent: "prepared",
|
||||||
|
model: { providerID: "openai", id: "gpt-5" },
|
||||||
|
location: { directory: "/server", workspaceID: "work_1" },
|
||||||
|
})
|
||||||
return session("ses_fresh", "/server", "work_1")
|
return session("ses_fresh", "/server", "work_1")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -71,20 +75,22 @@ describe("session target resolver", () => {
|
|||||||
prepare: async (input) => {
|
prepare: async (input) => {
|
||||||
order.push("prepare")
|
order.push("prepare")
|
||||||
expect(input.location.workspaceID).toBe("work_1")
|
expect(input.location.workspaceID).toBe("work_1")
|
||||||
return { model: input.model, agent: "prepared" }
|
return { model: { providerID: "openai", id: "gpt-5" }, agent: "prepared" }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(create).toHaveBeenCalledTimes(1)
|
expect(create).toHaveBeenCalledTimes(1)
|
||||||
expect(order).toEqual(["prepare", "create"])
|
expect(order).toEqual(["prepare", "create"])
|
||||||
})
|
})
|
||||||
|
|
||||||
test("uses the agent resolved by the server for a fresh Session", async () => {
|
test("requires an explicit agent and model for a fresh Session", async () => {
|
||||||
const client = OpenCode.make({ baseUrl: "https://opencode.test" })
|
const client = OpenCode.make({ baseUrl: "https://opencode.test" })
|
||||||
spyOn(client.location, "get").mockResolvedValue(location("/project"))
|
spyOn(client.location, "get").mockResolvedValue(location("/project"))
|
||||||
spyOn(client.session, "create").mockResolvedValue({ ...session("ses_fresh", "/project"), agent: "review" })
|
const create = spyOn(client.session, "create")
|
||||||
|
|
||||||
const target = await resolveSessionTarget({ client, prepare })
|
await expect(resolveSessionTarget({ client, prepare })).rejects.toThrow(
|
||||||
expect(target.agent).toBe("review")
|
"Creating a session requires an agent and model",
|
||||||
|
)
|
||||||
|
expect(create).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("does not retry an ambiguous Session creation", async () => {
|
test("does not retry an ambiguous Session creation", async () => {
|
||||||
|
|||||||
@@ -120,49 +120,61 @@ export type SessionListOperation<E = never> = (input?: Endpoint5_0Input) => Effe
|
|||||||
export type Endpoint5_1Input = {
|
export type Endpoint5_1Input = {
|
||||||
readonly id?: Session.ID | undefined
|
readonly id?: Session.ID | undefined
|
||||||
readonly title?: string | undefined
|
readonly title?: string | undefined
|
||||||
readonly agent?: Agent.ID | undefined
|
readonly agent: Agent.ID
|
||||||
readonly model?: Model.Ref | undefined
|
readonly model: Model.Ref
|
||||||
readonly location?: Location.Ref | undefined
|
readonly location?: Location.Ref | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_1Output = Session.Info
|
export type Endpoint5_1Output = Session.Info
|
||||||
export type SessionCreateOperation<E = never> = (input?: Endpoint5_1Input) => Effect.Effect<Endpoint5_1Output, E>
|
export type SessionCreateOperation<E = never> = (input: Endpoint5_1Input) => Effect.Effect<Endpoint5_1Output, E>
|
||||||
|
|
||||||
export type Endpoint5_2Output = { readonly [x: Session.ID]: { readonly type: "running" } }
|
export type Endpoint5_2Input = {
|
||||||
export type SessionActiveOperation<E = never> = () => Effect.Effect<Endpoint5_2Output, E>
|
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_3Input = { readonly sessionID: Session.ID }
|
export type Endpoint5_3Input = { readonly sessionID: Session.ID; readonly sanitize?: boolean | undefined }
|
||||||
export type Endpoint5_3Output = Session.Info
|
export type Endpoint5_3Output = { readonly info: Session.Info; readonly messages: ReadonlyArray<SessionMessage.Info> }
|
||||||
export type SessionGetOperation<E = never> = (input: Endpoint5_3Input) => Effect.Effect<Endpoint5_3Output, E>
|
export type SessionExportOperation<E = never> = (input: Endpoint5_3Input) => Effect.Effect<Endpoint5_3Output, E>
|
||||||
|
|
||||||
export type Endpoint5_4Input = { readonly sessionID: Session.ID }
|
export type Endpoint5_4Output = { readonly [x: Session.ID]: { readonly type: "running" } }
|
||||||
export type Endpoint5_4Output = void
|
export type SessionActiveOperation<E = never> = () => Effect.Effect<Endpoint5_4Output, E>
|
||||||
export type SessionRemoveOperation<E = never> = (input: Endpoint5_4Input) => Effect.Effect<Endpoint5_4Output, E>
|
|
||||||
|
|
||||||
export type Endpoint5_5Input = { readonly sessionID: Session.ID; readonly boundary: Session.ForkRequestBoundary }
|
export type Endpoint5_5Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_5Output = Session.Info
|
export type Endpoint5_5Output = Session.Info
|
||||||
export type SessionForkOperation<E = never> = (input: Endpoint5_5Input) => Effect.Effect<Endpoint5_5Output, E>
|
export type SessionGetOperation<E = never> = (input: Endpoint5_5Input) => Effect.Effect<Endpoint5_5Output, E>
|
||||||
|
|
||||||
export type Endpoint5_6Input = { readonly sessionID: Session.ID; readonly agent: Agent.ID }
|
export type Endpoint5_6Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_6Output = void
|
export type Endpoint5_6Output = void
|
||||||
export type SessionSwitchAgentOperation<E = never> = (input: Endpoint5_6Input) => Effect.Effect<Endpoint5_6Output, E>
|
export type SessionRemoveOperation<E = never> = (input: Endpoint5_6Input) => Effect.Effect<Endpoint5_6Output, E>
|
||||||
|
|
||||||
export type Endpoint5_7Input = { readonly sessionID: Session.ID; readonly model: Model.Ref }
|
export type Endpoint5_7Input = { readonly sessionID: Session.ID; readonly boundary: Session.ForkRequestBoundary }
|
||||||
export type Endpoint5_7Output = void
|
export type Endpoint5_7Output = Session.Info
|
||||||
export type SessionSwitchModelOperation<E = never> = (input: Endpoint5_7Input) => Effect.Effect<Endpoint5_7Output, E>
|
export type SessionForkOperation<E = never> = (input: Endpoint5_7Input) => Effect.Effect<Endpoint5_7Output, E>
|
||||||
|
|
||||||
export type Endpoint5_8Input = { readonly sessionID: Session.ID; readonly title: string }
|
export type Endpoint5_8Input = { readonly sessionID: Session.ID; readonly agent: Agent.ID }
|
||||||
export type Endpoint5_8Output = void
|
export type Endpoint5_8Output = void
|
||||||
export type SessionRenameOperation<E = never> = (input: Endpoint5_8Input) => Effect.Effect<Endpoint5_8Output, E>
|
export type SessionSwitchAgentOperation<E = never> = (input: Endpoint5_8Input) => Effect.Effect<Endpoint5_8Output, E>
|
||||||
|
|
||||||
export type Endpoint5_9Input = {
|
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 = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly directory: AbsolutePath
|
readonly directory: AbsolutePath
|
||||||
readonly workspaceID?: Workspace.ID | undefined
|
readonly workspaceID?: Workspace.ID | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_9Output = void
|
export type Endpoint5_11Output = void
|
||||||
export type SessionMoveOperation<E = never> = (input: Endpoint5_9Input) => Effect.Effect<Endpoint5_9Output, E>
|
export type SessionMoveOperation<E = never> = (input: Endpoint5_11Input) => Effect.Effect<Endpoint5_11Output, E>
|
||||||
|
|
||||||
export type Endpoint5_10Input = {
|
export type Endpoint5_12Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly id?: SessionMessage.ID | undefined
|
readonly id?: SessionMessage.ID | undefined
|
||||||
readonly text: string
|
readonly text: string
|
||||||
@@ -172,10 +184,10 @@ export type Endpoint5_10Input = {
|
|||||||
readonly delivery?: "steer" | "queue" | undefined
|
readonly delivery?: "steer" | "queue" | undefined
|
||||||
readonly resume?: boolean | undefined
|
readonly resume?: boolean | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_10Output = SessionPending.User
|
export type Endpoint5_12Output = SessionPending.User
|
||||||
export type SessionPromptOperation<E = never> = (input: Endpoint5_10Input) => Effect.Effect<Endpoint5_10Output, E>
|
export type SessionPromptOperation<E = never> = (input: Endpoint5_12Input) => Effect.Effect<Endpoint5_12Output, E>
|
||||||
|
|
||||||
export type Endpoint5_11Input = {
|
export type Endpoint5_13Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly id?: SessionMessage.ID | undefined
|
readonly id?: SessionMessage.ID | undefined
|
||||||
readonly command: string
|
readonly command: string
|
||||||
@@ -187,19 +199,19 @@ export type Endpoint5_11Input = {
|
|||||||
readonly delivery?: "steer" | "queue" | undefined
|
readonly delivery?: "steer" | "queue" | undefined
|
||||||
readonly resume?: boolean | undefined
|
readonly resume?: boolean | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_11Output = SessionPending.User
|
export type Endpoint5_13Output = SessionPending.User
|
||||||
export type SessionCommandOperation<E = never> = (input: Endpoint5_11Input) => Effect.Effect<Endpoint5_11Output, E>
|
export type SessionCommandOperation<E = never> = (input: Endpoint5_13Input) => Effect.Effect<Endpoint5_13Output, E>
|
||||||
|
|
||||||
export type Endpoint5_12Input = {
|
export type Endpoint5_14Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly id?: SessionMessage.ID | undefined
|
readonly id?: SessionMessage.ID | undefined
|
||||||
readonly skill: Skill.ID
|
readonly skill: Skill.ID
|
||||||
readonly resume?: boolean | undefined
|
readonly resume?: boolean | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_12Output = void
|
export type Endpoint5_14Output = void
|
||||||
export type SessionSkillOperation<E = never> = (input: Endpoint5_12Input) => Effect.Effect<Endpoint5_12Output, E>
|
export type SessionSkillOperation<E = never> = (input: Endpoint5_14Input) => Effect.Effect<Endpoint5_14Output, E>
|
||||||
|
|
||||||
export type Endpoint5_13Input = {
|
export type Endpoint5_15Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly id?: SessionMessage.ID | undefined
|
readonly id?: SessionMessage.ID | undefined
|
||||||
readonly text: string
|
readonly text: string
|
||||||
@@ -208,81 +220,81 @@ export type Endpoint5_13Input = {
|
|||||||
readonly delivery?: "steer" | "queue" | undefined
|
readonly delivery?: "steer" | "queue" | undefined
|
||||||
readonly resume?: boolean | undefined
|
readonly resume?: boolean | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_13Output = SessionPending.Synthetic
|
export type Endpoint5_15Output = SessionPending.Synthetic
|
||||||
export type SessionSyntheticOperation<E = never> = (input: Endpoint5_13Input) => Effect.Effect<Endpoint5_13Output, E>
|
export type SessionSyntheticOperation<E = never> = (input: Endpoint5_15Input) => Effect.Effect<Endpoint5_15Output, E>
|
||||||
|
|
||||||
export type Endpoint5_14Input = {
|
export type Endpoint5_16Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly id?: Event.ID | undefined
|
readonly id?: Event.ID | undefined
|
||||||
readonly command: string
|
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 Endpoint5_16Output = void
|
||||||
export type SessionWaitOperation<E = never> = (input: Endpoint5_16Input) => Effect.Effect<Endpoint5_16Output, E>
|
export type SessionShellOperation<E = never> = (input: Endpoint5_16Input) => Effect.Effect<Endpoint5_16Output, E>
|
||||||
|
|
||||||
export type Endpoint5_17Input = {
|
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 = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly messageID: SessionMessage.ID
|
readonly messageID: SessionMessage.ID
|
||||||
readonly files?: boolean | undefined
|
readonly files?: boolean | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_17Output = Session.Revert
|
export type Endpoint5_19Output = Session.Revert
|
||||||
export type SessionRevertStageOperation<E = never> = (input: Endpoint5_17Input) => Effect.Effect<Endpoint5_17Output, E>
|
export type SessionRevertStageOperation<E = never> = (input: Endpoint5_19Input) => Effect.Effect<Endpoint5_19Output, 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_20Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_20Output = ReadonlyArray<SessionMessage.Info>
|
export type Endpoint5_20Output = void
|
||||||
export type SessionContextOperation<E = never> = (input: Endpoint5_20Input) => Effect.Effect<Endpoint5_20Output, E>
|
export type SessionRevertClearOperation<E = never> = (input: Endpoint5_20Input) => Effect.Effect<Endpoint5_20Output, E>
|
||||||
|
|
||||||
export type Endpoint5_21Input = { readonly sessionID: Session.ID }
|
export type Endpoint5_21Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_21Output = ReadonlyArray<SessionPending.Info>
|
export type Endpoint5_21Output = void
|
||||||
export type SessionPendingListOperation<E = never> = (input: Endpoint5_21Input) => Effect.Effect<Endpoint5_21Output, E>
|
export type SessionRevertCommitOperation<E = never> = (input: Endpoint5_21Input) => Effect.Effect<Endpoint5_21Output, E>
|
||||||
|
|
||||||
export type Endpoint5_22Input = { readonly sessionID: Session.ID }
|
export type Endpoint5_22Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_22Output = ReadonlyArray<InstructionEntry.Info>
|
export type Endpoint5_22Output = ReadonlyArray<SessionMessage.Info>
|
||||||
export type SessionInstructionsEntryListOperation<E = never> = (
|
export type SessionContextOperation<E = never> = (input: Endpoint5_22Input) => Effect.Effect<Endpoint5_22Output, E>
|
||||||
input: Endpoint5_22Input,
|
|
||||||
) => Effect.Effect<Endpoint5_22Output, E>
|
|
||||||
|
|
||||||
export type Endpoint5_23Input = {
|
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 SessionInstructionsEntryListOperation<E = never> = (
|
||||||
|
input: Endpoint5_24Input,
|
||||||
|
) => Effect.Effect<Endpoint5_24Output, E>
|
||||||
|
|
||||||
|
export type Endpoint5_25Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly key: InstructionEntry.Key
|
readonly key: InstructionEntry.Key
|
||||||
readonly value: Schema.Json
|
readonly value: Schema.Json
|
||||||
}
|
}
|
||||||
export type Endpoint5_23Output = void
|
export type Endpoint5_25Output = void
|
||||||
export type SessionInstructionsEntryPutOperation<E = never> = (
|
export type SessionInstructionsEntryPutOperation<E = never> = (
|
||||||
input: Endpoint5_23Input,
|
input: Endpoint5_25Input,
|
||||||
) => Effect.Effect<Endpoint5_23Output, E>
|
) => Effect.Effect<Endpoint5_25Output, E>
|
||||||
|
|
||||||
export type Endpoint5_24Input = { readonly sessionID: Session.ID; readonly key: InstructionEntry.Key }
|
export type Endpoint5_26Input = { readonly sessionID: Session.ID; readonly key: InstructionEntry.Key }
|
||||||
export type Endpoint5_24Output = void
|
export type Endpoint5_26Output = void
|
||||||
export type SessionInstructionsEntryRemoveOperation<E = never> = (
|
export type SessionInstructionsEntryRemoveOperation<E = never> = (
|
||||||
input: Endpoint5_24Input,
|
input: Endpoint5_26Input,
|
||||||
) => Effect.Effect<Endpoint5_24Output, E>
|
) => Effect.Effect<Endpoint5_26Output, E>
|
||||||
|
|
||||||
export type Endpoint5_25Input = { readonly sessionID: Session.ID; readonly prompt: string }
|
export type Endpoint5_27Input = { readonly sessionID: Session.ID; readonly prompt: string }
|
||||||
export type Endpoint5_25Output = { readonly text: string }
|
export type Endpoint5_27Output = { readonly text: string }
|
||||||
export type SessionGenerateOperation<E = never> = (input: Endpoint5_25Input) => Effect.Effect<Endpoint5_25Output, E>
|
export type SessionGenerateOperation<E = never> = (input: Endpoint5_27Input) => Effect.Effect<Endpoint5_27Output, E>
|
||||||
|
|
||||||
export type Endpoint5_26Input = {
|
export type Endpoint5_28Input = {
|
||||||
readonly sessionID: Session.ID
|
readonly sessionID: Session.ID
|
||||||
readonly after?: Event.Seq | undefined
|
readonly after?: Event.Seq | undefined
|
||||||
readonly follow?: boolean | undefined
|
readonly follow?: boolean | undefined
|
||||||
}
|
}
|
||||||
export type Endpoint5_26Output =
|
export type Endpoint5_28Output =
|
||||||
| (
|
| (
|
||||||
| {
|
| {
|
||||||
readonly id: Event.ID
|
readonly id: Event.ID
|
||||||
@@ -850,23 +862,25 @@ export type Endpoint5_26Output =
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
| EventLog.Synced
|
| EventLog.Synced
|
||||||
export type SessionLogOperation<E = never> = (input: Endpoint5_26Input) => Stream.Stream<Endpoint5_26Output, E>
|
export type SessionLogOperation<E = never> = (input: Endpoint5_28Input) => Stream.Stream<Endpoint5_28Output, E>
|
||||||
|
|
||||||
export type Endpoint5_27Input = { readonly sessionID: Session.ID }
|
export type Endpoint5_29Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_27Output = void
|
export type Endpoint5_29Output = void
|
||||||
export type SessionInterruptOperation<E = never> = (input: Endpoint5_27Input) => Effect.Effect<Endpoint5_27Output, E>
|
export type SessionInterruptOperation<E = never> = (input: Endpoint5_29Input) => Effect.Effect<Endpoint5_29Output, E>
|
||||||
|
|
||||||
export type Endpoint5_28Input = { readonly sessionID: Session.ID }
|
export type Endpoint5_30Input = { readonly sessionID: Session.ID }
|
||||||
export type Endpoint5_28Output = void
|
export type Endpoint5_30Output = void
|
||||||
export type SessionBackgroundOperation<E = never> = (input: Endpoint5_28Input) => Effect.Effect<Endpoint5_28Output, E>
|
export type SessionBackgroundOperation<E = never> = (input: Endpoint5_30Input) => Effect.Effect<Endpoint5_30Output, E>
|
||||||
|
|
||||||
export type Endpoint5_29Input = { readonly sessionID: Session.ID; readonly messageID: SessionMessage.ID }
|
export type Endpoint5_31Input = { readonly sessionID: Session.ID; readonly messageID: SessionMessage.ID }
|
||||||
export type Endpoint5_29Output = SessionMessage.Info
|
export type Endpoint5_31Output = SessionMessage.Info
|
||||||
export type SessionMessageOperation<E = never> = (input: Endpoint5_29Input) => Effect.Effect<Endpoint5_29Output, E>
|
export type SessionMessageOperation<E = never> = (input: Endpoint5_31Input) => Effect.Effect<Endpoint5_31Output, E>
|
||||||
|
|
||||||
export interface SessionApi<E = never> {
|
export interface SessionApi<E = never> {
|
||||||
readonly list: SessionListOperation<E>
|
readonly list: SessionListOperation<E>
|
||||||
readonly create: SessionCreateOperation<E>
|
readonly create: SessionCreateOperation<E>
|
||||||
|
readonly import: SessionImportOperation<E>
|
||||||
|
readonly export: SessionExportOperation<E>
|
||||||
readonly active: SessionActiveOperation<E>
|
readonly active: SessionActiveOperation<E>
|
||||||
readonly get: SessionGetOperation<E>
|
readonly get: SessionGetOperation<E>
|
||||||
readonly remove: SessionRemoveOperation<E>
|
readonly remove: SessionRemoveOperation<E>
|
||||||
@@ -1606,27 +1620,6 @@ export interface ConfigApi<E = never> {
|
|||||||
readonly get: ConfigGetOperation<E>
|
readonly get: ConfigGetOperation<E>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Endpoint30_0Input = {
|
|
||||||
readonly info: Session.Info
|
|
||||||
readonly messages: ReadonlyArray<SessionMessage.Info>
|
|
||||||
readonly location?: Location.Ref | undefined
|
|
||||||
}
|
|
||||||
export type Endpoint30_0Output = Session.Info
|
|
||||||
export type SessionTransferImportOperation<E = never> = (
|
|
||||||
input: Endpoint30_0Input,
|
|
||||||
) => Effect.Effect<Endpoint30_0Output, E>
|
|
||||||
|
|
||||||
export type Endpoint30_1Input = { readonly sessionID: Session.ID; readonly sanitize?: boolean | undefined }
|
|
||||||
export type Endpoint30_1Output = { readonly info: Session.Info; readonly messages: ReadonlyArray<SessionMessage.Info> }
|
|
||||||
export type SessionTransferExportOperation<E = never> = (
|
|
||||||
input: Endpoint30_1Input,
|
|
||||||
) => Effect.Effect<Endpoint30_1Output, E>
|
|
||||||
|
|
||||||
export interface SessionTransferApi<E = never> {
|
|
||||||
readonly import: SessionTransferImportOperation<E>
|
|
||||||
readonly export: SessionTransferExportOperation<E>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppApi<E = never> {
|
export interface AppApi<E = never> {
|
||||||
readonly health: HealthApi<E>
|
readonly health: HealthApi<E>
|
||||||
readonly server: ServerApi<E>
|
readonly server: ServerApi<E>
|
||||||
@@ -1658,5 +1651,4 @@ export interface AppApi<E = never> {
|
|||||||
readonly migration: MigrationApi<E>
|
readonly migration: MigrationApi<E>
|
||||||
readonly websearch: WebsearchApi<E>
|
readonly websearch: WebsearchApi<E>
|
||||||
readonly config: ConfigApi<E>
|
readonly config: ConfigApi<E>
|
||||||
readonly sessionTransfer: SessionTransferApi<E>
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ import type {
|
|||||||
Endpoint5_0Output,
|
Endpoint5_0Output,
|
||||||
Endpoint5_1Input,
|
Endpoint5_1Input,
|
||||||
Endpoint5_1Output,
|
Endpoint5_1Output,
|
||||||
|
Endpoint5_2Input,
|
||||||
Endpoint5_2Output,
|
Endpoint5_2Output,
|
||||||
Endpoint5_3Input,
|
Endpoint5_3Input,
|
||||||
Endpoint5_3Output,
|
Endpoint5_3Output,
|
||||||
Endpoint5_4Input,
|
|
||||||
Endpoint5_4Output,
|
Endpoint5_4Output,
|
||||||
Endpoint5_5Input,
|
Endpoint5_5Input,
|
||||||
Endpoint5_5Output,
|
Endpoint5_5Output,
|
||||||
@@ -76,6 +76,10 @@ import type {
|
|||||||
Endpoint5_28Output,
|
Endpoint5_28Output,
|
||||||
Endpoint5_29Input,
|
Endpoint5_29Input,
|
||||||
Endpoint5_29Output,
|
Endpoint5_29Output,
|
||||||
|
Endpoint5_30Input,
|
||||||
|
Endpoint5_30Output,
|
||||||
|
Endpoint5_31Input,
|
||||||
|
Endpoint5_31Output,
|
||||||
Endpoint6_0Input,
|
Endpoint6_0Input,
|
||||||
Endpoint6_0Output,
|
Endpoint6_0Output,
|
||||||
Endpoint7_0Input,
|
Endpoint7_0Input,
|
||||||
@@ -222,10 +226,6 @@ import type {
|
|||||||
Endpoint28_1Output,
|
Endpoint28_1Output,
|
||||||
Endpoint29_0Input,
|
Endpoint29_0Input,
|
||||||
Endpoint29_0Output,
|
Endpoint29_0Output,
|
||||||
Endpoint30_0Input,
|
|
||||||
Endpoint30_0Output,
|
|
||||||
Endpoint30_1Input,
|
|
||||||
Endpoint30_1Output,
|
|
||||||
} from "../api/api.js"
|
} from "../api/api.js"
|
||||||
import { ClientError } from "./client-error"
|
import { ClientError } from "./client-error"
|
||||||
|
|
||||||
@@ -305,15 +305,15 @@ const Endpoint5_0 = (raw: RawClient["server.session"]) => (input?: Endpoint5_0In
|
|||||||
}).pipe(Effect.mapError(mapClientError)),
|
}).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_1 = (raw: RawClient["server.session"]) => (input?: Endpoint5_1Input) =>
|
const Endpoint5_1 = (raw: RawClient["server.session"]) => (input: Endpoint5_1Input) =>
|
||||||
preserveEffect<Endpoint5_1Output>()(
|
preserveEffect<Endpoint5_1Output>()(
|
||||||
raw["session.create"]({
|
raw["session.create"]({
|
||||||
payload: {
|
payload: {
|
||||||
id: input?.["id"],
|
id: input["id"],
|
||||||
title: input?.["title"],
|
title: input["title"],
|
||||||
agent: input?.["agent"],
|
agent: input["agent"],
|
||||||
model: input?.["model"],
|
model: input["model"],
|
||||||
location: input?.["location"],
|
location: input["location"],
|
||||||
},
|
},
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
@@ -321,9 +321,11 @@ const Endpoint5_1 = (raw: RawClient["server.session"]) => (input?: Endpoint5_1In
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_2 = (raw: RawClient["server.session"]) => () =>
|
const Endpoint5_2 = (raw: RawClient["server.session"]) => (input: Endpoint5_2Input) =>
|
||||||
preserveEffect<Endpoint5_2Output>()(
|
preserveEffect<Endpoint5_2Output>()(
|
||||||
raw["session.active"]({}).pipe(
|
raw["session.import"]({
|
||||||
|
payload: { info: input["info"], messages: input["messages"], location: input["location"] },
|
||||||
|
}).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
@@ -331,20 +333,23 @@ const Endpoint5_2 = (raw: RawClient["server.session"]) => () =>
|
|||||||
|
|
||||||
const Endpoint5_3 = (raw: RawClient["server.session"]) => (input: Endpoint5_3Input) =>
|
const Endpoint5_3 = (raw: RawClient["server.session"]) => (input: Endpoint5_3Input) =>
|
||||||
preserveEffect<Endpoint5_3Output>()(
|
preserveEffect<Endpoint5_3Output>()(
|
||||||
raw["session.get"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
raw["session.export"]({ params: { sessionID: input["sessionID"] }, query: { sanitize: input["sanitize"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_4 = (raw: RawClient["server.session"]) => (input: Endpoint5_4Input) =>
|
const Endpoint5_4 = (raw: RawClient["server.session"]) => () =>
|
||||||
preserveEffect<Endpoint5_4Output>()(
|
preserveEffect<Endpoint5_4Output>()(
|
||||||
raw["session.remove"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
raw["session.active"]({}).pipe(
|
||||||
|
Effect.mapError(mapClientError),
|
||||||
|
Effect.map((value) => value.data),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_5 = (raw: RawClient["server.session"]) => (input: Endpoint5_5Input) =>
|
const Endpoint5_5 = (raw: RawClient["server.session"]) => (input: Endpoint5_5Input) =>
|
||||||
preserveEffect<Endpoint5_5Output>()(
|
preserveEffect<Endpoint5_5Output>()(
|
||||||
raw["session.fork"]({ params: { sessionID: input["sessionID"] }, payload: { boundary: input["boundary"] } }).pipe(
|
raw["session.get"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
@@ -352,35 +357,48 @@ const Endpoint5_5 = (raw: RawClient["server.session"]) => (input: Endpoint5_5Inp
|
|||||||
|
|
||||||
const Endpoint5_6 = (raw: RawClient["server.session"]) => (input: Endpoint5_6Input) =>
|
const Endpoint5_6 = (raw: RawClient["server.session"]) => (input: Endpoint5_6Input) =>
|
||||||
preserveEffect<Endpoint5_6Output>()(
|
preserveEffect<Endpoint5_6Output>()(
|
||||||
raw["session.switchAgent"]({ params: { sessionID: input["sessionID"] }, payload: { agent: input["agent"] } }).pipe(
|
raw["session.remove"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
||||||
Effect.mapError(mapClientError),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_7 = (raw: RawClient["server.session"]) => (input: Endpoint5_7Input) =>
|
const Endpoint5_7 = (raw: RawClient["server.session"]) => (input: Endpoint5_7Input) =>
|
||||||
preserveEffect<Endpoint5_7Output>()(
|
preserveEffect<Endpoint5_7Output>()(
|
||||||
raw["session.switchModel"]({ params: { sessionID: input["sessionID"] }, payload: { model: input["model"] } }).pipe(
|
raw["session.fork"]({ params: { sessionID: input["sessionID"] }, payload: { boundary: input["boundary"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_8 = (raw: RawClient["server.session"]) => (input: Endpoint5_8Input) =>
|
const Endpoint5_8 = (raw: RawClient["server.session"]) => (input: Endpoint5_8Input) =>
|
||||||
preserveEffect<Endpoint5_8Output>()(
|
preserveEffect<Endpoint5_8Output>()(
|
||||||
raw["session.rename"]({ params: { sessionID: input["sessionID"] }, payload: { title: input["title"] } }).pipe(
|
raw["session.switchAgent"]({ params: { sessionID: input["sessionID"] }, payload: { agent: input["agent"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_9 = (raw: RawClient["server.session"]) => (input: Endpoint5_9Input) =>
|
const Endpoint5_9 = (raw: RawClient["server.session"]) => (input: Endpoint5_9Input) =>
|
||||||
preserveEffect<Endpoint5_9Output>()(
|
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"]({
|
raw["session.move"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: { directory: input["directory"], workspaceID: input["workspaceID"] },
|
payload: { directory: input["directory"], workspaceID: input["workspaceID"] },
|
||||||
}).pipe(Effect.mapError(mapClientError)),
|
}).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_10 = (raw: RawClient["server.session"]) => (input: Endpoint5_10Input) =>
|
const Endpoint5_12 = (raw: RawClient["server.session"]) => (input: Endpoint5_12Input) =>
|
||||||
preserveEffect<Endpoint5_10Output>()(
|
preserveEffect<Endpoint5_12Output>()(
|
||||||
raw["session.prompt"]({
|
raw["session.prompt"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: {
|
payload: {
|
||||||
@@ -398,8 +416,8 @@ const Endpoint5_10 = (raw: RawClient["server.session"]) => (input: Endpoint5_10I
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_11 = (raw: RawClient["server.session"]) => (input: Endpoint5_11Input) =>
|
const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13Input) =>
|
||||||
preserveEffect<Endpoint5_11Output>()(
|
preserveEffect<Endpoint5_13Output>()(
|
||||||
raw["session.command"]({
|
raw["session.command"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: {
|
payload: {
|
||||||
@@ -419,16 +437,16 @@ const Endpoint5_11 = (raw: RawClient["server.session"]) => (input: Endpoint5_11I
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_12 = (raw: RawClient["server.session"]) => (input: Endpoint5_12Input) =>
|
const Endpoint5_14 = (raw: RawClient["server.session"]) => (input: Endpoint5_14Input) =>
|
||||||
preserveEffect<Endpoint5_12Output>()(
|
preserveEffect<Endpoint5_14Output>()(
|
||||||
raw["session.skill"]({
|
raw["session.skill"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: { id: input["id"], skill: input["skill"], resume: input["resume"] },
|
payload: { id: input["id"], skill: input["skill"], resume: input["resume"] },
|
||||||
}).pipe(Effect.mapError(mapClientError)),
|
}).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13Input) =>
|
const Endpoint5_15 = (raw: RawClient["server.session"]) => (input: Endpoint5_15Input) =>
|
||||||
preserveEffect<Endpoint5_13Output>()(
|
preserveEffect<Endpoint5_15Output>()(
|
||||||
raw["session.synthetic"]({
|
raw["session.synthetic"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: {
|
payload: {
|
||||||
@@ -445,29 +463,29 @@ const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13I
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_14 = (raw: RawClient["server.session"]) => (input: Endpoint5_14Input) =>
|
const Endpoint5_16 = (raw: RawClient["server.session"]) => (input: Endpoint5_16Input) =>
|
||||||
preserveEffect<Endpoint5_14Output>()(
|
preserveEffect<Endpoint5_16Output>()(
|
||||||
raw["session.shell"]({
|
raw["session.shell"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: { id: input["id"], command: input["command"] },
|
payload: { id: input["id"], command: input["command"] },
|
||||||
}).pipe(Effect.mapError(mapClientError)),
|
}).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_15 = (raw: RawClient["server.session"]) => (input: Endpoint5_15Input) =>
|
const Endpoint5_17 = (raw: RawClient["server.session"]) => (input: Endpoint5_17Input) =>
|
||||||
preserveEffect<Endpoint5_15Output>()(
|
preserveEffect<Endpoint5_17Output>()(
|
||||||
raw["session.compact"]({ params: { sessionID: input["sessionID"] }, payload: { id: input["id"] } }).pipe(
|
raw["session.compact"]({ params: { sessionID: input["sessionID"] }, payload: { id: input["id"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_16 = (raw: RawClient["server.session"]) => (input: Endpoint5_16Input) =>
|
const Endpoint5_18 = (raw: RawClient["server.session"]) => (input: Endpoint5_18Input) =>
|
||||||
preserveEffect<Endpoint5_16Output>()(
|
preserveEffect<Endpoint5_18Output>()(
|
||||||
raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_17 = (raw: RawClient["server.session"]) => (input: Endpoint5_17Input) =>
|
const Endpoint5_19 = (raw: RawClient["server.session"]) => (input: Endpoint5_19Input) =>
|
||||||
preserveEffect<Endpoint5_17Output>()(
|
preserveEffect<Endpoint5_19Output>()(
|
||||||
raw["session.revert.stage"]({
|
raw["session.revert.stage"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
payload: { messageID: input["messageID"], files: input["files"] },
|
payload: { messageID: input["messageID"], files: input["files"] },
|
||||||
@@ -477,35 +495,19 @@ const Endpoint5_17 = (raw: RawClient["server.session"]) => (input: Endpoint5_17I
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
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) =>
|
const Endpoint5_20 = (raw: RawClient["server.session"]) => (input: Endpoint5_20Input) =>
|
||||||
preserveEffect<Endpoint5_20Output>()(
|
preserveEffect<Endpoint5_20Output>()(
|
||||||
raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
||||||
Effect.mapError(mapClientError),
|
|
||||||
Effect.map((value) => value.data),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_21 = (raw: RawClient["server.session"]) => (input: Endpoint5_21Input) =>
|
const Endpoint5_21 = (raw: RawClient["server.session"]) => (input: Endpoint5_21Input) =>
|
||||||
preserveEffect<Endpoint5_21Output>()(
|
preserveEffect<Endpoint5_21Output>()(
|
||||||
raw["session.pending.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
||||||
Effect.mapError(mapClientError),
|
|
||||||
Effect.map((value) => value.data),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_22 = (raw: RawClient["server.session"]) => (input: Endpoint5_22Input) =>
|
const Endpoint5_22 = (raw: RawClient["server.session"]) => (input: Endpoint5_22Input) =>
|
||||||
preserveEffect<Endpoint5_22Output>()(
|
preserveEffect<Endpoint5_22Output>()(
|
||||||
raw["session.instructions.entry.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
@@ -513,29 +515,45 @@ const Endpoint5_22 = (raw: RawClient["server.session"]) => (input: Endpoint5_22I
|
|||||||
|
|
||||||
const Endpoint5_23 = (raw: RawClient["server.session"]) => (input: Endpoint5_23Input) =>
|
const Endpoint5_23 = (raw: RawClient["server.session"]) => (input: Endpoint5_23Input) =>
|
||||||
preserveEffect<Endpoint5_23Output>()(
|
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"]({
|
raw["session.instructions.entry.put"]({
|
||||||
params: { sessionID: input["sessionID"], key: input["key"] },
|
params: { sessionID: input["sessionID"], key: input["key"] },
|
||||||
payload: { value: input["value"] },
|
payload: { value: input["value"] },
|
||||||
}).pipe(Effect.mapError(mapClientError)),
|
}).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_24 = (raw: RawClient["server.session"]) => (input: Endpoint5_24Input) =>
|
const Endpoint5_26 = (raw: RawClient["server.session"]) => (input: Endpoint5_26Input) =>
|
||||||
preserveEffect<Endpoint5_24Output>()(
|
preserveEffect<Endpoint5_26Output>()(
|
||||||
raw["session.instructions.entry.remove"]({ params: { sessionID: input["sessionID"], key: input["key"] } }).pipe(
|
raw["session.instructions.entry.remove"]({ params: { sessionID: input["sessionID"], key: input["key"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_25 = (raw: RawClient["server.session"]) => (input: Endpoint5_25Input) =>
|
const Endpoint5_27 = (raw: RawClient["server.session"]) => (input: Endpoint5_27Input) =>
|
||||||
preserveEffect<Endpoint5_25Output>()(
|
preserveEffect<Endpoint5_27Output>()(
|
||||||
raw["session.generate"]({ params: { sessionID: input["sessionID"] }, payload: { prompt: input["prompt"] } }).pipe(
|
raw["session.generate"]({ params: { sessionID: input["sessionID"] }, payload: { prompt: input["prompt"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_26 = (raw: RawClient["server.session"]) => (input: Endpoint5_26Input) =>
|
const Endpoint5_28 = (raw: RawClient["server.session"]) => (input: Endpoint5_28Input) =>
|
||||||
preserveStream<Endpoint5_26Output>()(
|
preserveStream<Endpoint5_28Output>()(
|
||||||
Stream.unwrap(
|
Stream.unwrap(
|
||||||
raw["session.log"]({
|
raw["session.log"]({
|
||||||
params: { sessionID: input["sessionID"] },
|
params: { sessionID: input["sessionID"] },
|
||||||
@@ -547,18 +565,18 @@ const Endpoint5_26 = (raw: RawClient["server.session"]) => (input: Endpoint5_26I
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_27 = (raw: RawClient["server.session"]) => (input: Endpoint5_27Input) =>
|
const Endpoint5_29 = (raw: RawClient["server.session"]) => (input: Endpoint5_29Input) =>
|
||||||
preserveEffect<Endpoint5_27Output>()(
|
preserveEffect<Endpoint5_29Output>()(
|
||||||
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_28 = (raw: RawClient["server.session"]) => (input: Endpoint5_28Input) =>
|
const Endpoint5_30 = (raw: RawClient["server.session"]) => (input: Endpoint5_30Input) =>
|
||||||
preserveEffect<Endpoint5_28Output>()(
|
preserveEffect<Endpoint5_30Output>()(
|
||||||
raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Endpoint5_29 = (raw: RawClient["server.session"]) => (input: Endpoint5_29Input) =>
|
const Endpoint5_31 = (raw: RawClient["server.session"]) => (input: Endpoint5_31Input) =>
|
||||||
preserveEffect<Endpoint5_29Output>()(
|
preserveEffect<Endpoint5_31Output>()(
|
||||||
raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
|
raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
|
||||||
Effect.mapError(mapClientError),
|
Effect.mapError(mapClientError),
|
||||||
Effect.map((value) => value.data),
|
Effect.map((value) => value.data),
|
||||||
@@ -568,30 +586,32 @@ const Endpoint5_29 = (raw: RawClient["server.session"]) => (input: Endpoint5_29I
|
|||||||
const adaptGroup5 = (raw: RawClient["server.session"]) => ({
|
const adaptGroup5 = (raw: RawClient["server.session"]) => ({
|
||||||
list: Endpoint5_0(raw),
|
list: Endpoint5_0(raw),
|
||||||
create: Endpoint5_1(raw),
|
create: Endpoint5_1(raw),
|
||||||
active: Endpoint5_2(raw),
|
import: Endpoint5_2(raw),
|
||||||
get: Endpoint5_3(raw),
|
export: Endpoint5_3(raw),
|
||||||
remove: Endpoint5_4(raw),
|
active: Endpoint5_4(raw),
|
||||||
fork: Endpoint5_5(raw),
|
get: Endpoint5_5(raw),
|
||||||
switchAgent: Endpoint5_6(raw),
|
remove: Endpoint5_6(raw),
|
||||||
switchModel: Endpoint5_7(raw),
|
fork: Endpoint5_7(raw),
|
||||||
rename: Endpoint5_8(raw),
|
switchAgent: Endpoint5_8(raw),
|
||||||
move: Endpoint5_9(raw),
|
switchModel: Endpoint5_9(raw),
|
||||||
prompt: Endpoint5_10(raw),
|
rename: Endpoint5_10(raw),
|
||||||
command: Endpoint5_11(raw),
|
move: Endpoint5_11(raw),
|
||||||
skill: Endpoint5_12(raw),
|
prompt: Endpoint5_12(raw),
|
||||||
synthetic: Endpoint5_13(raw),
|
command: Endpoint5_13(raw),
|
||||||
shell: Endpoint5_14(raw),
|
skill: Endpoint5_14(raw),
|
||||||
compact: Endpoint5_15(raw),
|
synthetic: Endpoint5_15(raw),
|
||||||
wait: Endpoint5_16(raw),
|
shell: Endpoint5_16(raw),
|
||||||
revert: { stage: Endpoint5_17(raw), clear: Endpoint5_18(raw), commit: Endpoint5_19(raw) },
|
compact: Endpoint5_17(raw),
|
||||||
context: Endpoint5_20(raw),
|
wait: Endpoint5_18(raw),
|
||||||
pending: { list: Endpoint5_21(raw) },
|
revert: { stage: Endpoint5_19(raw), clear: Endpoint5_20(raw), commit: Endpoint5_21(raw) },
|
||||||
instructions: { entry: { list: Endpoint5_22(raw), put: Endpoint5_23(raw), remove: Endpoint5_24(raw) } },
|
context: Endpoint5_22(raw),
|
||||||
generate: Endpoint5_25(raw),
|
pending: { list: Endpoint5_23(raw) },
|
||||||
log: Endpoint5_26(raw),
|
instructions: { entry: { list: Endpoint5_24(raw), put: Endpoint5_25(raw), remove: Endpoint5_26(raw) } },
|
||||||
interrupt: Endpoint5_27(raw),
|
generate: Endpoint5_27(raw),
|
||||||
background: Endpoint5_28(raw),
|
log: Endpoint5_28(raw),
|
||||||
message: Endpoint5_29(raw),
|
interrupt: Endpoint5_29(raw),
|
||||||
|
background: Endpoint5_30(raw),
|
||||||
|
message: Endpoint5_31(raw),
|
||||||
})
|
})
|
||||||
|
|
||||||
const Endpoint6_0 = (raw: RawClient["server.message"]) => (input: Endpoint6_0Input) =>
|
const Endpoint6_0 = (raw: RawClient["server.message"]) => (input: Endpoint6_0Input) =>
|
||||||
@@ -1254,32 +1274,6 @@ const Endpoint29_0 = (raw: RawClient["server.config"]) => (input?: Endpoint29_0I
|
|||||||
|
|
||||||
const adaptGroup29 = (raw: RawClient["server.config"]) => ({ get: Endpoint29_0(raw) })
|
const adaptGroup29 = (raw: RawClient["server.config"]) => ({ get: Endpoint29_0(raw) })
|
||||||
|
|
||||||
const Endpoint30_0 = (raw: RawClient["server.sessionTransfer"]) => (input: Endpoint30_0Input) =>
|
|
||||||
preserveEffect<Endpoint30_0Output>()(
|
|
||||||
raw["sessionTransfer.import"]({
|
|
||||||
payload: { info: input["info"], messages: input["messages"], location: input["location"] },
|
|
||||||
}).pipe(
|
|
||||||
Effect.mapError(mapClientError),
|
|
||||||
Effect.map((value) => value.data),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const Endpoint30_1 = (raw: RawClient["server.sessionTransfer"]) => (input: Endpoint30_1Input) =>
|
|
||||||
preserveEffect<Endpoint30_1Output>()(
|
|
||||||
raw["sessionTransfer.export"]({
|
|
||||||
params: { sessionID: input["sessionID"] },
|
|
||||||
query: { sanitize: input["sanitize"] },
|
|
||||||
}).pipe(
|
|
||||||
Effect.mapError(mapClientError),
|
|
||||||
Effect.map((value) => value.data),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const adaptGroup30 = (raw: RawClient["server.sessionTransfer"]) => ({
|
|
||||||
import: Endpoint30_0(raw),
|
|
||||||
export: Endpoint30_1(raw),
|
|
||||||
})
|
|
||||||
|
|
||||||
const adaptClient = (raw: RawClient) => ({
|
const adaptClient = (raw: RawClient) => ({
|
||||||
health: adaptGroup0(raw["server.health"]),
|
health: adaptGroup0(raw["server.health"]),
|
||||||
server: adaptGroup1(raw["server.server"]),
|
server: adaptGroup1(raw["server.server"]),
|
||||||
@@ -1311,7 +1305,6 @@ const adaptClient = (raw: RawClient) => ({
|
|||||||
migration: adaptGroup27(raw["server.migration"]),
|
migration: adaptGroup27(raw["server.migration"]),
|
||||||
websearch: adaptGroup28(raw["server.websearch"]),
|
websearch: adaptGroup28(raw["server.websearch"]),
|
||||||
config: adaptGroup29(raw["server.config"]),
|
config: adaptGroup29(raw["server.config"]),
|
||||||
sessionTransfer: adaptGroup30(raw["server.sessionTransfer"]),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const make = (options?: { readonly baseUrl?: URL | string }) =>
|
export const make = (options?: { readonly baseUrl?: URL | string }) =>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export type {
|
|||||||
ReferenceApi,
|
ReferenceApi,
|
||||||
WebSearchApi,
|
WebSearchApi,
|
||||||
SessionApi,
|
SessionApi,
|
||||||
SessionTransferApi,
|
|
||||||
SkillApi,
|
SkillApi,
|
||||||
} from "./api.js"
|
} from "./api.js"
|
||||||
export { Agent } from "@opencode-ai/schema/agent"
|
export { Agent } from "@opencode-ai/schema/agent"
|
||||||
@@ -44,7 +43,6 @@ export { AbsolutePath, RelativePath } from "@opencode-ai/schema/schema"
|
|||||||
export { Session } from "@opencode-ai/schema/session"
|
export { Session } from "@opencode-ai/schema/session"
|
||||||
export { SessionPending } from "@opencode-ai/schema/session-pending"
|
export { SessionPending } from "@opencode-ai/schema/session-pending"
|
||||||
export { SessionMessage } from "@opencode-ai/schema/session-message"
|
export { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||||
export { SessionTransfer } from "@opencode-ai/schema/session-transfer"
|
|
||||||
export { Skill } from "@opencode-ai/schema/skill"
|
export { Skill } from "@opencode-ai/schema/skill"
|
||||||
export { Prompt } from "@opencode-ai/schema/prompt"
|
export { Prompt } from "@opencode-ai/schema/prompt"
|
||||||
export { PromptInput } from "@opencode-ai/schema/prompt-input"
|
export { PromptInput } from "@opencode-ai/schema/prompt-input"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export type ProviderApi = Client["provider"]
|
|||||||
export type ReferenceApi = Client["reference"]
|
export type ReferenceApi = Client["reference"]
|
||||||
export type WebSearchApi = Client["websearch"]
|
export type WebSearchApi = Client["websearch"]
|
||||||
export type SessionApi = Client["session"]
|
export type SessionApi = Client["session"]
|
||||||
export type SessionTransferApi = Client["sessionTransfer"]
|
|
||||||
export type SkillApi = Client["skill"]
|
export type SkillApi = Client["skill"]
|
||||||
|
|
||||||
export interface CatalogApi {
|
export interface CatalogApi {
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import type {
|
|||||||
SessionListOutput,
|
SessionListOutput,
|
||||||
SessionCreateInput,
|
SessionCreateInput,
|
||||||
SessionCreateOutput,
|
SessionCreateOutput,
|
||||||
|
SessionImportInput,
|
||||||
|
SessionImportOutput,
|
||||||
|
SessionExportInput,
|
||||||
|
SessionExportOutput,
|
||||||
SessionActiveOutput,
|
SessionActiveOutput,
|
||||||
SessionGetInput,
|
SessionGetInput,
|
||||||
SessionGetOutput,
|
SessionGetOutput,
|
||||||
@@ -218,10 +222,6 @@ import type {
|
|||||||
WebsearchQueryOutput,
|
WebsearchQueryOutput,
|
||||||
ConfigGetInput,
|
ConfigGetInput,
|
||||||
ConfigGetOutput,
|
ConfigGetOutput,
|
||||||
SessionTransferImportInput,
|
|
||||||
SessionTransferImportOutput,
|
|
||||||
SessionTransferExportInput,
|
|
||||||
SessionTransferExportOutput,
|
|
||||||
} from "./types"
|
} from "./types"
|
||||||
import { ClientError } from "./client-error"
|
import { ClientError } from "./client-error"
|
||||||
|
|
||||||
@@ -464,17 +464,17 @@ export function make(options: ClientOptions) {
|
|||||||
},
|
},
|
||||||
requestOptions,
|
requestOptions,
|
||||||
),
|
),
|
||||||
create: (input?: SessionCreateInput, requestOptions?: RequestOptions) =>
|
create: (input: SessionCreateInput, requestOptions?: RequestOptions) =>
|
||||||
request<{ readonly data: SessionCreateOutput }>(
|
request<{ readonly data: SessionCreateOutput }>(
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
path: `/api/session`,
|
path: `/api/session`,
|
||||||
body: {
|
body: {
|
||||||
id: input?.["id"],
|
id: input["id"],
|
||||||
title: input?.["title"],
|
title: input["title"],
|
||||||
agent: input?.["agent"],
|
agent: input["agent"],
|
||||||
model: input?.["model"],
|
model: input["model"],
|
||||||
location: input?.["location"],
|
location: input["location"],
|
||||||
},
|
},
|
||||||
successStatus: 200,
|
successStatus: 200,
|
||||||
declaredStatuses: [401, 400],
|
declaredStatuses: [401, 400],
|
||||||
@@ -482,6 +482,30 @@ export function make(options: ClientOptions) {
|
|||||||
},
|
},
|
||||||
requestOptions,
|
requestOptions,
|
||||||
).then((value) => value.data),
|
).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) =>
|
active: (requestOptions?: RequestOptions) =>
|
||||||
request<{ readonly data: SessionActiveOutput }>(
|
request<{ readonly data: SessionActiveOutput }>(
|
||||||
{
|
{
|
||||||
@@ -1831,32 +1855,6 @@ export function make(options: ClientOptions) {
|
|||||||
requestOptions,
|
requestOptions,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
sessionTransfer: {
|
|
||||||
import: (input: SessionTransferImportInput, requestOptions?: RequestOptions) =>
|
|
||||||
request<{ readonly data: SessionTransferImportOutput }>(
|
|
||||||
{
|
|
||||||
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: SessionTransferExportInput, requestOptions?: RequestOptions) =>
|
|
||||||
request<{ readonly data: SessionTransferExportOutput }>(
|
|
||||||
{
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@ export type {
|
|||||||
ReferenceApi,
|
ReferenceApi,
|
||||||
WebSearchApi,
|
WebSearchApi,
|
||||||
SessionApi,
|
SessionApi,
|
||||||
SessionTransferApi,
|
|
||||||
SkillApi,
|
SkillApi,
|
||||||
} from "./api.js"
|
} from "./api.js"
|
||||||
export type { EventSubscribeOutput as OpenCodeEvent } from "./generated/types"
|
export type { EventSubscribeOutput as OpenCodeEvent } from "./generated/types"
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ test("session methods retain decoded Effect inputs and outputs", async () => {
|
|||||||
const page = yield* client.session.list({ limit: 10 })
|
const page = yield* client.session.list({ limit: 10 })
|
||||||
const active = yield* client.session.active()
|
const active = yield* client.session.active()
|
||||||
const created = yield* client.session.create({
|
const created = yield* client.session.create({
|
||||||
|
agent: Agent.ID.make("build"),
|
||||||
|
model: Model.Ref.make({ id: "claude", providerID: "anthropic" }),
|
||||||
location: Location.Ref.make({ directory: AbsolutePath.make("/tmp/project") }),
|
location: Location.Ref.make({ directory: AbsolutePath.make("/tmp/project") }),
|
||||||
})
|
})
|
||||||
yield* client.session.switchAgent({ sessionID: Session.ID.make("ses_test"), agent: Agent.ID.make("build") })
|
yield* client.session.switchAgent({ sessionID: Session.ID.make("ses_test"), agent: Agent.ID.make("build") })
|
||||||
|
|||||||
@@ -454,7 +454,11 @@ test("session methods use the public HTTP contract", async () => {
|
|||||||
|
|
||||||
const page = await client.session.list({ limit: 10, order: "desc", parentID: null })
|
const page = await client.session.list({ limit: 10, order: "desc", parentID: null })
|
||||||
const active = await client.session.active()
|
const active = await client.session.active()
|
||||||
const created = await client.session.create({ location: { directory: "/tmp/project" } })
|
const created = await client.session.create({
|
||||||
|
agent: "build",
|
||||||
|
model: { id: "claude", providerID: "anthropic" },
|
||||||
|
location: { directory: "/tmp/project" },
|
||||||
|
})
|
||||||
await client.session.switchAgent({ sessionID: "ses_test", agent: "build" })
|
await client.session.switchAgent({ sessionID: "ses_test", agent: "build" })
|
||||||
await client.session.switchModel({
|
await client.session.switchModel({
|
||||||
sessionID: "ses_test",
|
sessionID: "ses_test",
|
||||||
@@ -528,7 +532,7 @@ test("middleware errors remain declared client errors", async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.session.create({})
|
await client.session.create({ agent: "build", model: { id: "claude", providerID: "anthropic" } })
|
||||||
throw new Error("Expected request to fail")
|
throw new Error("Expected request to fail")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(isUnauthorizedError(error)).toBe(true)
|
expect(isUnauthorizedError(error)).toBe(true)
|
||||||
|
|||||||
@@ -161,11 +161,14 @@ function isPathAction(action: string): action is PathAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function expandHome(resource: string, home: string) {
|
function expandHome(resource: string, home: string) {
|
||||||
if (resource.startsWith("~/")) return home + resource.slice(1)
|
|
||||||
if (resource === "~") return home
|
if (resource === "~") return home
|
||||||
if (resource === "$HOME") return home
|
if (resource === "$HOME") return home
|
||||||
if (resource.startsWith("$HOME/")) return home + resource.slice(5)
|
const relative = resource.startsWith("~/")
|
||||||
if (resource.startsWith("$HOME\\")) return home + resource.slice(5)
|
? resource.slice(2)
|
||||||
|
: resource.startsWith("$HOME/") || resource.startsWith("$HOME\\")
|
||||||
|
? resource.slice(6)
|
||||||
|
: undefined
|
||||||
|
if (relative !== undefined) return (path.posix.isAbsolute(home) ? path.posix : path.win32).join(home, relative)
|
||||||
return resource
|
return resource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -340,12 +340,12 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: import("../p
|
|||||||
hook: (name, callback) => hooks.register("session", name, callback),
|
hook: (name, callback) => hooks.register("session", name, callback),
|
||||||
create: (input) =>
|
create: (input) =>
|
||||||
runtime.session.create({
|
runtime.session.create({
|
||||||
id: input?.id,
|
id: input.id,
|
||||||
title: input?.title,
|
title: input.title,
|
||||||
agent: input?.agent,
|
agent: input.agent,
|
||||||
model: input?.model,
|
model: input.model,
|
||||||
location:
|
location:
|
||||||
input?.location ?? Location.Ref.make({ directory: location.directory, workspaceID: location.workspaceID }),
|
input.location ?? Location.Ref.make({ directory: location.directory, workspaceID: location.workspaceID }),
|
||||||
}),
|
}),
|
||||||
get: (input) => runtime.session.get(input.sessionID),
|
get: (input) => runtime.session.get(input.sessionID),
|
||||||
prompt: runtime.session.prompt,
|
prompt: runtime.session.prompt,
|
||||||
|
|||||||
@@ -269,25 +269,21 @@ export function fromPromise(plugin: Plugin) {
|
|||||||
register(host.session.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
register(host.session.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
||||||
create: (input) =>
|
create: (input) =>
|
||||||
run(
|
run(
|
||||||
host.session.create(
|
host.session.create({
|
||||||
input === undefined
|
id: input.id == null ? undefined : Session.ID.make(input.id),
|
||||||
? undefined
|
agent: Agent.ID.make(input.agent),
|
||||||
: {
|
model: model(input.model),
|
||||||
id: input.id == null ? undefined : Session.ID.make(input.id),
|
location:
|
||||||
agent: input.agent == null ? undefined : Agent.ID.make(input.agent),
|
input.location == null
|
||||||
model: input.model == null ? undefined : model(input.model),
|
? undefined
|
||||||
location:
|
: Location.Ref.make({
|
||||||
input.location == null
|
directory: AbsolutePath.make(input.location.directory),
|
||||||
? undefined
|
workspaceID:
|
||||||
: Location.Ref.make({
|
input.location.workspaceID === undefined
|
||||||
directory: AbsolutePath.make(input.location.directory),
|
? undefined
|
||||||
workspaceID:
|
: Workspace.ID.make(input.location.workspaceID),
|
||||||
input.location.workspaceID === undefined
|
}),
|
||||||
? undefined
|
}),
|
||||||
: Workspace.ID.make(input.location.workspaceID),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
get: (input) => run(host.session.get({ sessionID: Session.ID.make(input.sessionID) })),
|
get: (input) => run(host.session.get({ sessionID: Session.ID.make(input.sessionID) })),
|
||||||
prompt: (input) =>
|
prompt: (input) =>
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ describe("ConfigAgentPlugin.Plugin", () => {
|
|||||||
it.effect("matches Windows paths against home-relative permissions", () =>
|
it.effect("matches Windows paths against home-relative permissions", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const permissions = yield* loadHomePermissions("C:\\Users\\test")
|
const permissions = yield* loadHomePermissions("C:\\Users\\test")
|
||||||
|
expect(permissions).toContainEqual({
|
||||||
|
action: "external_directory",
|
||||||
|
resource: "C:\\Users\\test\\p\\**",
|
||||||
|
effect: "allow",
|
||||||
|
})
|
||||||
expect(
|
expect(
|
||||||
Permission.evaluate("external_directory", "C:\\Users\\test\\p\\opencode\\src\\*", permissions).effect,
|
Permission.evaluate("external_directory", "C:\\Users\\test\\p\\opencode\\src\\*", permissions).effect,
|
||||||
).toBe("allow")
|
).toBe("allow")
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { MessageGroup } from "./groups/message.js"
|
|||||||
import { ModelGroup } from "./groups/model.js"
|
import { ModelGroup } from "./groups/model.js"
|
||||||
import { ProviderGroup } from "./groups/provider.js"
|
import { ProviderGroup } from "./groups/provider.js"
|
||||||
import { makeSessionGroup } from "./groups/session.js"
|
import { makeSessionGroup } from "./groups/session.js"
|
||||||
import { SessionTransferGroup } from "./groups/session-transfer.js"
|
|
||||||
import { makePermissionGroup } from "./groups/permission.js"
|
import { makePermissionGroup } from "./groups/permission.js"
|
||||||
import { FileSystemGroup } from "./groups/fs.js"
|
import { FileSystemGroup } from "./groups/fs.js"
|
||||||
import { makeFormGroup } from "./groups/form.js"
|
import { makeFormGroup } from "./groups/form.js"
|
||||||
@@ -90,7 +89,6 @@ type ApiGroups<
|
|||||||
| typeof ServerGroup
|
| typeof ServerGroup
|
||||||
| typeof DebugGroup
|
| typeof DebugGroup
|
||||||
| typeof MigrationGroup
|
| typeof MigrationGroup
|
||||||
| typeof SessionTransferGroup
|
|
||||||
| LocationGroups<LocationId>
|
| LocationGroups<LocationId>
|
||||||
| FormGroups<LocationId, LocationService, FormLocationId, FormLocationService>
|
| FormGroups<LocationId, LocationService, FormLocationId, FormLocationService>
|
||||||
| SessionGroups<SessionLocationId, SessionLocationService>
|
| SessionGroups<SessionLocationId, SessionLocationService>
|
||||||
@@ -180,7 +178,6 @@ const makeApiFromGroup = <
|
|||||||
.add(MigrationGroup)
|
.add(MigrationGroup)
|
||||||
.add(WebSearchGroup.middleware(locationMiddleware))
|
.add(WebSearchGroup.middleware(locationMiddleware))
|
||||||
.add(ConfigGroup.middleware(locationMiddleware))
|
.add(ConfigGroup.middleware(locationMiddleware))
|
||||||
.add(SessionTransferGroup)
|
|
||||||
.annotateMerge(
|
.annotateMerge(
|
||||||
OpenApi.annotations({
|
OpenApi.annotations({
|
||||||
title: "opencode HttpApi",
|
title: "opencode HttpApi",
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ export const groupNames = {
|
|||||||
"server.agent": "agent",
|
"server.agent": "agent",
|
||||||
"server.plugin": "plugin",
|
"server.plugin": "plugin",
|
||||||
"server.session": "session",
|
"server.session": "session",
|
||||||
"server.sessionTransfer": "sessionTransfer",
|
|
||||||
"server.message": "message",
|
"server.message": "message",
|
||||||
"server.model": "model",
|
"server.model": "model",
|
||||||
"server.generate": "generate",
|
"server.generate": "generate",
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import { Location } from "@opencode-ai/schema/location"
|
|
||||||
import { Session } from "@opencode-ai/schema/session"
|
|
||||||
import { SessionTransfer } from "@opencode-ai/schema/session-transfer"
|
|
||||||
import { Schema, SchemaGetter } from "effect"
|
|
||||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
|
||||||
import { ConflictError, SessionNotFoundError, UnknownError } from "../errors.js"
|
|
||||||
|
|
||||||
const BooleanFromString = Schema.Literals(["true", "false"]).pipe(
|
|
||||||
Schema.decodeTo(Schema.Boolean, {
|
|
||||||
decode: SchemaGetter.transform((value) => value === "true"),
|
|
||||||
encode: SchemaGetter.transform((value): "true" | "false" => (value ? "true" : "false")),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const SessionTransferGroup = HttpApiGroup.make("server.sessionTransfer")
|
|
||||||
.add(
|
|
||||||
HttpApiEndpoint.post("sessionTransfer.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.sessionTransfer.import",
|
|
||||||
summary: "Import session",
|
|
||||||
description: "Import a projected session transcript at the requested location.",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.add(
|
|
||||||
HttpApiEndpoint.get("sessionTransfer.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.sessionTransfer.export",
|
|
||||||
summary: "Export session",
|
|
||||||
description: "Export a complete projected session transcript.",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { SessionMessage } from "@opencode-ai/schema/session-message"
|
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 { SessionPending } from "@opencode-ai/schema/session-pending"
|
||||||
import { PromptInput } from "@opencode-ai/schema/prompt-input"
|
import { PromptInput } from "@opencode-ai/schema/prompt-input"
|
||||||
import { Session } from "@opencode-ai/schema/session"
|
import { Session } from "@opencode-ai/schema/session"
|
||||||
@@ -150,8 +151,8 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
|
|||||||
payload: Schema.Struct({
|
payload: Schema.Struct({
|
||||||
id: Session.ID.pipe(Schema.optional),
|
id: Session.ID.pipe(Schema.optional),
|
||||||
title: Schema.String.pipe(Schema.optional),
|
title: Schema.String.pipe(Schema.optional),
|
||||||
agent: Agent.ID.pipe(Schema.optional),
|
agent: Agent.ID,
|
||||||
model: Model.Ref.pipe(Schema.optional),
|
model: Model.Ref,
|
||||||
location: Location.Ref.pipe(Schema.optional),
|
location: Location.Ref.pipe(Schema.optional),
|
||||||
}),
|
}),
|
||||||
success: Schema.Struct({ data: Session.Info }),
|
success: Schema.Struct({ data: Session.Info }),
|
||||||
@@ -159,7 +160,37 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
|
|||||||
OpenApi.annotations({
|
OpenApi.annotations({
|
||||||
identifier: "v2.session.create",
|
identifier: "v2.session.create",
|
||||||
summary: "Create session",
|
summary: "Create session",
|
||||||
description: "Create a session at the requested location.",
|
description: "Create a session with an explicit agent and model at the requested location.",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.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.",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { MessageHandler } from "./handlers/message"
|
|||||||
import { ModelHandler } from "./handlers/model"
|
import { ModelHandler } from "./handlers/model"
|
||||||
import { ProviderHandler } from "./handlers/provider"
|
import { ProviderHandler } from "./handlers/provider"
|
||||||
import { SessionHandler } from "./handlers/session"
|
import { SessionHandler } from "./handlers/session"
|
||||||
import { SessionTransferHandler } from "./handlers/session-transfer"
|
|
||||||
import { PermissionHandler } from "./handlers/permission"
|
import { PermissionHandler } from "./handlers/permission"
|
||||||
import { FileSystemHandler } from "./handlers/fs"
|
import { FileSystemHandler } from "./handlers/fs"
|
||||||
import { FormHandler } from "./handlers/form"
|
import { FormHandler } from "./handlers/form"
|
||||||
@@ -41,7 +40,6 @@ export const handlers = Layer.mergeAll(
|
|||||||
AgentHandler,
|
AgentHandler,
|
||||||
PluginHandler,
|
PluginHandler,
|
||||||
SessionHandler,
|
SessionHandler,
|
||||||
SessionTransferHandler,
|
|
||||||
MessageHandler,
|
MessageHandler,
|
||||||
ModelHandler,
|
ModelHandler,
|
||||||
GenerateHandler,
|
GenerateHandler,
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
|
||||||
import { SessionTransfer } from "@opencode-ai/core/session/transfer"
|
|
||||||
import { ConflictError, SessionNotFoundError, UnknownError } from "@opencode-ai/protocol/errors"
|
|
||||||
import { Effect } from "effect"
|
|
||||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
|
||||||
import { Api } from "../api"
|
|
||||||
|
|
||||||
export const SessionTransferHandler = HttpApiBuilder.group(Api, "server.sessionTransfer", (handlers) =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const transfer = yield* SessionTransfer.Service
|
|
||||||
|
|
||||||
return handlers
|
|
||||||
.handle(
|
|
||||||
"sessionTransfer.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(
|
|
||||||
"sessionTransfer.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 }),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Session } from "@opencode-ai/core/session"
|
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 { InstructionEntry } from "@opencode-ai/core/session/instruction-entry"
|
||||||
import { DateTime, Effect, Stream } from "effect"
|
import { DateTime, Effect, Stream } from "effect"
|
||||||
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
||||||
@@ -24,6 +25,7 @@ const DefaultSessionsLimit = 50
|
|||||||
export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handlers) =>
|
export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handlers) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* Session.Service
|
const session = yield* Session.Service
|
||||||
|
const transfer = yield* SessionTransfer.Service
|
||||||
|
|
||||||
return handlers
|
return handlers
|
||||||
.handle(
|
.handle(
|
||||||
@@ -86,6 +88,56 @@ 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(
|
.handle(
|
||||||
"session.active",
|
"session.active",
|
||||||
Effect.fn(function* () {
|
Effect.fn(function* () {
|
||||||
|
|||||||
@@ -512,7 +512,7 @@ function App(props: { pair?: DialogPairCredentials }) {
|
|||||||
const terminalTitleEnabled = () => config.data.terminal?.title ?? true
|
const terminalTitleEnabled = () => config.data.terminal?.title ?? true
|
||||||
const copyOnSelectEnabled = () => config.data.terminal?.copy_on_select ?? process.platform !== "win32"
|
const copyOnSelectEnabled = () => config.data.terminal?.copy_on_select ?? process.platform !== "win32"
|
||||||
const pasteSummaryEnabled = () => config.data.prompt?.paste !== "full"
|
const pasteSummaryEnabled = () => config.data.prompt?.paste !== "full"
|
||||||
const tabsVertical = () => (config.data.tabs?.vertical ?? false) && sessionTabsFitVertically(dimensions().width)
|
const tabsVertical = () => config.data.tabs.layout === "vertical" && sessionTabsFitVertically(dimensions().width)
|
||||||
const tabsVisible = () =>
|
const tabsVisible = () =>
|
||||||
sessionTabs.enabled() && (sessionTabs.tabs().length > 0 || sessionTabs.newTab()) && route.data.type !== "plugin"
|
sessionTabs.enabled() && (sessionTabs.tabs().length > 0 || sessionTabs.newTab()) && route.data.type !== "plugin"
|
||||||
|
|
||||||
|
|||||||
@@ -101,12 +101,11 @@ export const settings: Setting[] = [
|
|||||||
labels: ["current directory", "global"],
|
labels: ["current directory", "global"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Vertical",
|
title: "Layout",
|
||||||
category: "Tabs",
|
category: "Tabs",
|
||||||
path: ["tabs", "vertical"],
|
path: ["tabs", "layout"],
|
||||||
default: false,
|
default: "horizontal",
|
||||||
values: [false, true],
|
values: ["horizontal", "vertical"],
|
||||||
labels: ["off", "on"],
|
|
||||||
keywords: ["sidebar", "orientation", "left"],
|
keywords: ["sidebar", "orientation", "left"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,17 +8,21 @@ import * as fuzzysort from "fuzzysort"
|
|||||||
import { useConnected } from "./use-connected"
|
import { useConnected } from "./use-connected"
|
||||||
import { useData } from "../context/data"
|
import { useData } from "../context/data"
|
||||||
import { modelPreferenceKey } from "../model-preference"
|
import { modelPreferenceKey } from "../model-preference"
|
||||||
|
import { useLocation } from "../context/location"
|
||||||
|
|
||||||
export function DialogModel(props: { providerID?: string }) {
|
export function DialogModel(props: { providerID?: string }) {
|
||||||
const local = useLocal()
|
const local = useLocal()
|
||||||
const data = useData()
|
const data = useData()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
|
const location = useLocation()
|
||||||
const [query, setQuery] = createSignal("")
|
const [query, setQuery] = createSignal("")
|
||||||
const favoritePriority = new Set(local.model.favorite().map(modelPreferenceKey))
|
const favoritePriority = new Set(local.model.favorite().map(modelPreferenceKey))
|
||||||
|
|
||||||
const connected = useConnected()
|
const connected = useConnected()
|
||||||
const providers = createMemo(() => new Map((data.location.provider.list() ?? []).map((item) => [item.id, item])))
|
const providers = createMemo(
|
||||||
const models = createMemo(() => data.location.model.list() ?? [])
|
() => new Map((data.location.provider.list(location.ref) ?? []).map((item) => [item.id, item])),
|
||||||
|
)
|
||||||
|
const models = createMemo(() => data.location.model.list(location.ref) ?? [])
|
||||||
|
|
||||||
const showExtra = createMemo(() => connected() && !props.providerID)
|
const showExtra = createMemo(() => connected() && !props.providerID)
|
||||||
|
|
||||||
|
|||||||
@@ -327,10 +327,6 @@ export function Prompt(props: PromptProps) {
|
|||||||
if (!session) return
|
if (!session) return
|
||||||
const agent = session.agent && local.agent.list().find((agent) => agent.id === session.agent)
|
const agent = session.agent && local.agent.list().find((agent) => agent.id === session.agent)
|
||||||
if (agent && !args.agent) local.agent.set(agent.id)
|
if (agent && !args.agent) local.agent.set(agent.id)
|
||||||
if (session.model) {
|
|
||||||
local.model.set({ providerID: session.model.providerID, modelID: session.model.id })
|
|
||||||
local.model.variant.set(session.model.variant)
|
|
||||||
}
|
|
||||||
syncedSessionID = sessionID
|
syncedSessionID = sessionID
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -943,15 +939,43 @@ export function Prompt(props: PromptProps) {
|
|||||||
await slash.command.run(slash.input)
|
await slash.command.run(slash.input)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
const inputText = expandTrackedPastedText(
|
||||||
|
store.prompt.text,
|
||||||
|
input.extmarks.getAllForTypeId(promptPartTypeId).flatMap((extmark) => {
|
||||||
|
const ref = store.extmarkToPart.get(extmark.id)
|
||||||
|
if (ref?.type !== "pasted") return []
|
||||||
|
const part = store.prompt.pasted[ref.index]
|
||||||
|
if (!part) return []
|
||||||
|
return [{ start: extmark.start, end: extmark.end, text: part.text }]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const slashHead = parseSlashHead(inputText, /\s/)
|
||||||
|
const isSkill =
|
||||||
|
slashHead !== undefined &&
|
||||||
|
(data.location.skill.list(currentLocation.ref) ?? []).some(
|
||||||
|
(skill) => skill.slash === true && skill.id === slashHead.name,
|
||||||
|
)
|
||||||
|
const isCommand =
|
||||||
|
slashHead !== undefined &&
|
||||||
|
(data.location.command.list(currentLocation.ref) ?? []).some((command) => command.name === slashHead.name)
|
||||||
const agent = local.agent.current()
|
const agent = local.agent.current()
|
||||||
if (!agent) return false
|
if (!agent) return false
|
||||||
const selectedModel = local.model.current()
|
const selection = local.model.selection()
|
||||||
if (!selectedModel) {
|
if (!selection) {
|
||||||
void promptModelWarning()
|
void promptModelWarning()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
const usesModel = !props.sessionID || (store.mode !== "shell" && !isSkill)
|
||||||
|
if (usesModel && !local.model.available(selection)) {
|
||||||
|
toast.show({
|
||||||
|
title: "Model unavailable",
|
||||||
|
message: `${selection.providerID}/${selection.modelID} is not available in this session's location`,
|
||||||
|
variant: "warning",
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const variant = local.model.variant.current()
|
const variant = selection.variant
|
||||||
let sessionID = props.sessionID
|
let sessionID = props.sessionID
|
||||||
let session = sessionID ? data.session.get(sessionID) : undefined
|
let session = sessionID ? data.session.get(sessionID) : undefined
|
||||||
let finishMoveProgress = false
|
let finishMoveProgress = false
|
||||||
@@ -969,8 +993,8 @@ export function Prompt(props: PromptProps) {
|
|||||||
location: directory ? { directory } : location,
|
location: directory ? { directory } : location,
|
||||||
agent: agent.id,
|
agent: agent.id,
|
||||||
model: {
|
model: {
|
||||||
providerID: selectedModel.providerID,
|
providerID: selection.providerID,
|
||||||
id: selectedModel.modelID,
|
id: selection.modelID,
|
||||||
variant,
|
variant,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -990,17 +1014,6 @@ export function Prompt(props: PromptProps) {
|
|||||||
session = created
|
session = created
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputText = expandTrackedPastedText(
|
|
||||||
store.prompt.text,
|
|
||||||
input.extmarks.getAllForTypeId(promptPartTypeId).flatMap((extmark) => {
|
|
||||||
const ref = store.extmarkToPart.get(extmark.id)
|
|
||||||
if (ref?.type !== "pasted") return []
|
|
||||||
const part = store.prompt.pasted[ref.index]
|
|
||||||
if (!part) return []
|
|
||||||
return [{ start: extmark.start, end: extmark.end, text: part.text }]
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Capture mode before it gets reset
|
// Capture mode before it gets reset
|
||||||
const currentMode = store.mode
|
const currentMode = store.mode
|
||||||
const editorSelection = editorContext()
|
const editorSelection = editorContext()
|
||||||
@@ -1013,43 +1026,30 @@ export function Prompt(props: PromptProps) {
|
|||||||
command: inputText,
|
command: inputText,
|
||||||
})
|
})
|
||||||
setStore("mode", "normal")
|
setStore("mode", "normal")
|
||||||
} else if (
|
} else if (slashHead && isCommand) {
|
||||||
inputText.startsWith("/") &&
|
|
||||||
(data.location.command.list(currentLocation.current) ?? []).some(
|
|
||||||
(command) => command.name === inputText.split("\n")[0].split(" ")[0].slice(1),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
move.startSubmit()
|
move.startSubmit()
|
||||||
// Parse command from first line, preserve multi-line content in arguments
|
const model = { providerID: selection.providerID, id: selection.modelID, variant }
|
||||||
const firstLineEnd = inputText.indexOf("\n")
|
const cancelCommit = local.model.trackSessionCommit(sessionID, model)
|
||||||
const firstLine = firstLineEnd === -1 ? inputText : inputText.slice(0, firstLineEnd)
|
|
||||||
const [command, ...firstLineArgs] = firstLine.split(" ")
|
|
||||||
const restOfInput = firstLineEnd === -1 ? "" : inputText.slice(firstLineEnd + 1)
|
|
||||||
const args = firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : "")
|
|
||||||
|
|
||||||
void client.api.session
|
void client.api.session
|
||||||
.command({
|
.command({
|
||||||
sessionID,
|
sessionID,
|
||||||
command: command.slice(1),
|
command: slashHead.name,
|
||||||
arguments: args,
|
arguments: slashHead.arguments,
|
||||||
agent: agent.id,
|
agent: agent.id,
|
||||||
model: { providerID: selectedModel.providerID, id: selectedModel.modelID, variant },
|
model,
|
||||||
files: store.prompt.files,
|
files: store.prompt.files,
|
||||||
agents: store.prompt.agents,
|
agents: store.prompt.agents,
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
cancelCommit()
|
||||||
toast.show({ title: "Failed to run command", message: errorMessage(error), variant: "error" })
|
toast.show({ title: "Failed to run command", message: errorMessage(error), variant: "error" })
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (isSkill) {
|
||||||
inputText.startsWith("/") &&
|
|
||||||
(data.location.skill.list(currentLocation.current) ?? []).some(
|
|
||||||
(skill) => skill.slash === true && skill.id === inputText.split("\n")[0].split(" ")[0].slice(1),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
move.startSubmit()
|
move.startSubmit()
|
||||||
void client.api.session.skill({
|
void client.api.session.skill({
|
||||||
sessionID,
|
sessionID,
|
||||||
skill: inputText.split("\n")[0].split(" ")[0].slice(1),
|
skill: slashHead!.name,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
move.startSubmit()
|
move.startSubmit()
|
||||||
@@ -1061,13 +1061,15 @@ export function Prompt(props: PromptProps) {
|
|||||||
await client.api.session.switchAgent({ sessionID, agent: agent.id })
|
await client.api.session.switchAgent({ sessionID, agent: agent.id })
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
session?.model?.providerID !== selectedModel.providerID ||
|
session?.model?.providerID !== selection.providerID ||
|
||||||
session.model.id !== selectedModel.modelID ||
|
session.model.id !== selection.modelID ||
|
||||||
(session.model.variant ?? "default") !== (variant ?? "default")
|
(session.model.variant ?? "default") !== (variant ?? "default")
|
||||||
) {
|
) {
|
||||||
await client.api.session.switchModel({
|
const model = { providerID: selection.providerID, id: selection.modelID, variant }
|
||||||
sessionID,
|
const cancelCommit = local.model.trackSessionCommit(sessionID, model)
|
||||||
model: { providerID: selectedModel.providerID, id: selectedModel.modelID, variant },
|
await client.api.session.switchModel({ sessionID, model }).catch((error) => {
|
||||||
|
cancelCommit()
|
||||||
|
throw error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (session?.revert) {
|
if (session?.revert) {
|
||||||
@@ -1320,10 +1322,7 @@ export function Prompt(props: PromptProps) {
|
|||||||
return `Ask anything... "${list()[store.placeholder % list().length]}"`
|
return `Ask anything... "${list()[store.placeholder % list().length]}"`
|
||||||
})()
|
})()
|
||||||
if (!value) return undefined
|
if (!value) return undefined
|
||||||
const width =
|
const width = dimensions().width < 44 ? dimensions().width - 5 : Math.min(75, dimensions().width - 4) - 5
|
||||||
dimensions().width < 44
|
|
||||||
? dimensions().width - 5
|
|
||||||
: Math.min(75, dimensions().width - 4) - 5
|
|
||||||
return Locale.takeWidth(value, Math.max(1, width)).trimEnd()
|
return Locale.takeWidth(value, Math.max(1, width)).trimEnd()
|
||||||
})
|
})
|
||||||
const locationLabel = createMemo(() => {
|
const locationLabel = createMemo(() => {
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ export const Info = Schema.Struct({
|
|||||||
scope: Schema.optional(Schema.Literals(["global", "cwd"])).annotate({
|
scope: Schema.optional(Schema.Literals(["global", "cwd"])).annotate({
|
||||||
description: "Share tabs globally or keep a separate set for each working directory",
|
description: "Share tabs globally or keep a separate set for each working directory",
|
||||||
}),
|
}),
|
||||||
vertical: Schema.optional(Schema.Boolean).annotate({
|
layout: Schema.optional(Schema.Literals(["horizontal", "vertical"])).annotate({
|
||||||
description: "Show tabs in a left sidebar instead of a horizontal strip",
|
description: "Show tabs in a horizontal strip or vertical sidebar",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
).annotate({ description: "Tab strip settings" }),
|
).annotate({ description: "Tab strip settings" }),
|
||||||
@@ -194,7 +194,7 @@ export type Resolved = Omit<Info, "attention" | "keybinds" | "leader" | "mouse"
|
|||||||
tabs: {
|
tabs: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
scope: "global" | "cwd"
|
scope: "global" | "cwd"
|
||||||
vertical?: boolean
|
layout: "horizontal" | "vertical"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +230,7 @@ export function resolve(input: Info, options: { terminalSuspend: boolean }): Res
|
|||||||
...input.tabs,
|
...input.tabs,
|
||||||
enabled: input.tabs?.enabled ?? true,
|
enabled: input.tabs?.enabled ?? true,
|
||||||
scope: input.tabs?.scope ?? "cwd",
|
scope: input.tabs?.scope ?? "cwd",
|
||||||
|
layout: input.tabs?.layout ?? "horizontal",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { dedupeWith } from "effect/Array"
|
import { dedupeWith } from "effect/Array"
|
||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
import { batch, createMemo } from "solid-js"
|
import { batch, createMemo, onCleanup } from "solid-js"
|
||||||
import { useEvent } from "./event"
|
import { useEvent } from "./event"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { useTuiPaths } from "./runtime"
|
import { useTuiPaths } from "./runtime"
|
||||||
@@ -22,6 +22,7 @@ import { useToast } from "../ui/toast"
|
|||||||
import { useRoute } from "./route"
|
import { useRoute } from "./route"
|
||||||
import { useData } from "./data"
|
import { useData } from "./data"
|
||||||
import { usePermission } from "./permission"
|
import { usePermission } from "./permission"
|
||||||
|
import { useLocation } from "./location"
|
||||||
|
|
||||||
export function parseModel(model: string) {
|
export function parseModel(model: string) {
|
||||||
const [providerID, ...rest] = model.split("/")
|
const [providerID, ...rest] = model.split("/")
|
||||||
@@ -57,26 +58,29 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
const args = useArgs()
|
const args = useArgs()
|
||||||
const event = useEvent()
|
const event = useEvent()
|
||||||
const permission = usePermission()
|
const permission = usePermission()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const models = () => data.location.model.list(location.ref)
|
||||||
|
const providers = () => data.location.provider.list(location.ref)
|
||||||
|
|
||||||
function isModelValid(model: ModelPreferenceModel) {
|
function isModelValid(model: ModelPreferenceModel) {
|
||||||
return !!data.location.model
|
return !!models()?.some((item) => item.providerID === model.providerID && item.id === model.modelID)
|
||||||
.list()
|
|
||||||
?.some((item) => item.providerID === model.providerID && item.id === model.modelID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFirstValidModel(...modelFns: (() => ModelPreferenceModel | undefined)[]) {
|
function getFirstValidModel(...modelFns: (() => ModelPreferenceModel | undefined)[]) {
|
||||||
for (const modelFn of modelFns) {
|
for (const modelFn of modelFns) {
|
||||||
const model = modelFn()
|
const model = modelFn()
|
||||||
if (!model) continue
|
if (model && isModelValid(model)) return model
|
||||||
if (isModelValid(model)) return model
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAgent() {
|
function createAgent() {
|
||||||
const agents = createMemo(() =>
|
const agents = createMemo(() =>
|
||||||
(data.location.agent.list() ?? []).filter((agent) => agent.mode !== "subagent" && !agent.hidden),
|
(data.location.agent.list(location.ref) ?? []).filter((agent) => agent.mode !== "subagent" && !agent.hidden),
|
||||||
|
)
|
||||||
|
const visibleAgents = createMemo(() =>
|
||||||
|
(data.location.agent.list(location.ref) ?? []).filter((agent) => !agent.hidden),
|
||||||
)
|
)
|
||||||
const visibleAgents = createMemo(() => (data.location.agent.list() ?? []).filter((agent) => !agent.hidden))
|
|
||||||
const [agentStore, setAgentStore] = createStore({
|
const [agentStore, setAgentStore] = createStore({
|
||||||
current: undefined as string | undefined,
|
current: undefined as string | undefined,
|
||||||
})
|
})
|
||||||
@@ -128,35 +132,40 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
const agent = createAgent()
|
const agent = createAgent()
|
||||||
|
|
||||||
function createModel() {
|
function createModel() {
|
||||||
const [modelStore, setModelStore] = createStore<
|
type ModelSelection = ModelPreferenceModel & { variant?: string }
|
||||||
ModelPreference & {
|
const [preferences, setPreferences] = createStore<ModelPreference & { ready: boolean }>({
|
||||||
ready: boolean
|
|
||||||
model: Record<string, ModelPreferenceModel>
|
|
||||||
}
|
|
||||||
>({
|
|
||||||
ready: false,
|
ready: false,
|
||||||
model: {},
|
|
||||||
recent: [],
|
recent: [],
|
||||||
favorite: [],
|
favorite: [],
|
||||||
variant: {},
|
variant: {},
|
||||||
})
|
})
|
||||||
|
const [selectionState, setSelectionState] = createStore<{
|
||||||
|
newSessionModelByLocationAgent: Record<string, ModelPreferenceModel | undefined>
|
||||||
|
draftBySession: Record<string, ModelSelection | undefined>
|
||||||
|
}>({
|
||||||
|
newSessionModelByLocationAgent: {},
|
||||||
|
draftBySession: {},
|
||||||
|
})
|
||||||
|
|
||||||
const repository = createModelPreferenceRepository(path.join(paths.state, "model.json"))
|
const repository = createModelPreferenceRepository(path.join(paths.state, "model.json"))
|
||||||
const state = {
|
const pendingSelectionCommits = new Map<string, string>()
|
||||||
|
const selectionKey = (value: ModelSelection) =>
|
||||||
|
`${modelPreferenceKey(value)}:${normalizeModelVariant(value.variant) ?? "default"}`
|
||||||
|
const saveState = {
|
||||||
pending: false,
|
pending: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function savePreferences() {
|
||||||
if (!modelStore.ready) {
|
if (!preferences.ready) {
|
||||||
state.pending = true
|
saveState.pending = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.pending = false
|
saveState.pending = false
|
||||||
void repository
|
void repository
|
||||||
.patch({
|
.patch({
|
||||||
recent: modelStore.recent,
|
recent: preferences.recent,
|
||||||
favorite: modelStore.favorite,
|
favorite: preferences.favorite,
|
||||||
variant: modelStore.variant,
|
variant: preferences.variant,
|
||||||
})
|
})
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
}
|
}
|
||||||
@@ -164,14 +173,14 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
repository
|
repository
|
||||||
.load()
|
.load()
|
||||||
.then((value) => {
|
.then((value) => {
|
||||||
setModelStore("recent", value.recent)
|
setPreferences("recent", value.recent)
|
||||||
setModelStore("favorite", value.favorite)
|
setPreferences("favorite", value.favorite)
|
||||||
setModelStore("variant", value.variant)
|
setPreferences("variant", value.variant)
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setModelStore("ready", true)
|
setPreferences("ready", true)
|
||||||
if (state.pending) save()
|
if (saveState.pending) savePreferences()
|
||||||
})
|
})
|
||||||
|
|
||||||
const fallbackModel = createMemo(() => {
|
const fallbackModel = createMemo(() => {
|
||||||
@@ -185,13 +194,13 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const item of modelStore.recent) {
|
for (const item of preferences.recent) {
|
||||||
if (isModelValid(item)) {
|
if (isModelValid(item)) {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const model = data.location.model.list()?.[0]
|
const model = models()?.[0]
|
||||||
if (!model) return undefined
|
if (!model) return undefined
|
||||||
return {
|
return {
|
||||||
providerID: model.providerID,
|
providerID: model.providerID,
|
||||||
@@ -199,30 +208,134 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentModel = createMemo(() => {
|
const newSessionModel = createMemo(() => {
|
||||||
const a = agent.current()
|
const a = agent.current()
|
||||||
return (
|
return getFirstValidModel(
|
||||||
getFirstValidModel(
|
() => a && selectionState.newSessionModelByLocationAgent[locationAgentKey(a.id)],
|
||||||
() => a && modelStore.model[a.id],
|
() => a?.model && { providerID: a.model.providerID, modelID: a.model.id },
|
||||||
() => a?.model && { providerID: a.model.providerID, modelID: a.model.id },
|
fallbackModel,
|
||||||
fallbackModel,
|
|
||||||
) ?? undefined
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const currentSelection = createMemo<ModelSelection | undefined>(() => {
|
||||||
|
if (route.data.type === "session") return sessionSelection(route.data.sessionID)
|
||||||
|
const model = newSessionModel()
|
||||||
|
if (!model) return
|
||||||
|
return { ...model, variant: normalizeModelVariant(preferences.variant[modelPreferenceKey(model)]) }
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentModel = createMemo(() => {
|
||||||
|
const selection = currentSelection()
|
||||||
|
if (!selection) return
|
||||||
|
return { providerID: selection.providerID, modelID: selection.modelID }
|
||||||
|
})
|
||||||
|
|
||||||
|
function locationAgentKey(agentID: string) {
|
||||||
|
const ref = location.ref ?? data.location.default()
|
||||||
|
return `${JSON.stringify([ref.directory, ref.workspaceID])}:${agentID}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function durableSelection(sessionID: string): ModelSelection | undefined {
|
||||||
|
const model = data.session.get(sessionID)?.model
|
||||||
|
if (!model) return
|
||||||
|
return {
|
||||||
|
providerID: model.providerID,
|
||||||
|
modelID: model.id,
|
||||||
|
variant: normalizeModelVariant(model.variant),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sessionSelection(sessionID: string) {
|
||||||
|
return selectionState.draftBySession[sessionID] ?? durableSelection(sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSessionDraft(sessionID: string, selection: ModelSelection) {
|
||||||
|
const durable = durableSelection(sessionID)
|
||||||
|
setSelectionState(
|
||||||
|
"draftBySession",
|
||||||
|
sessionID,
|
||||||
|
durable && selectionKey(durable) === selectionKey(selection) ? undefined : selection,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectModel(model: ModelPreferenceModel) {
|
||||||
|
if (route.data.type === "session") {
|
||||||
|
const sessionID = route.data.sessionID
|
||||||
|
const current = sessionSelection(sessionID)
|
||||||
|
const preferred = normalizeModelVariant(
|
||||||
|
current?.providerID === model.providerID && current.modelID === model.modelID
|
||||||
|
? current.variant
|
||||||
|
: preferences.variant[modelPreferenceKey(model)],
|
||||||
|
)
|
||||||
|
const info = models()?.find((item) => item.providerID === model.providerID && item.id === model.modelID)
|
||||||
|
const variant = preferred && info?.variants?.some((item) => item.id === preferred) ? preferred : undefined
|
||||||
|
setSessionDraft(sessionID, { ...model, variant })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const current = agent.current()
|
||||||
|
if (!current) return false
|
||||||
|
setSelectionState("newSessionModelByLocationAgent", locationAgentKey(current.id), model)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
onCleanup(
|
||||||
|
event.on("session.model.selected", (evt) => {
|
||||||
|
const expected = pendingSelectionCommits.get(evt.data.sessionID)
|
||||||
|
if (!expected) return
|
||||||
|
const committed = selectionKey({
|
||||||
|
providerID: evt.data.model.providerID,
|
||||||
|
modelID: evt.data.model.id,
|
||||||
|
variant: evt.data.model.variant,
|
||||||
|
})
|
||||||
|
if (committed !== expected) return
|
||||||
|
pendingSelectionCommits.delete(evt.data.sessionID)
|
||||||
|
const draft = selectionState.draftBySession[evt.data.sessionID]
|
||||||
|
if (draft && selectionKey(draft) === committed)
|
||||||
|
setSelectionState("draftBySession", evt.data.sessionID, undefined)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
onCleanup(
|
||||||
|
event.on("session.deleted", (evt) => {
|
||||||
|
pendingSelectionCommits.delete(evt.data.sessionID)
|
||||||
|
setSelectionState("draftBySession", evt.data.sessionID, undefined)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
current: currentModel,
|
current: currentModel,
|
||||||
|
selection: currentSelection,
|
||||||
|
available(model = currentModel()) {
|
||||||
|
return model ? isModelValid(model) : false
|
||||||
|
},
|
||||||
|
trackSessionCommit(
|
||||||
|
sessionID: string,
|
||||||
|
value: {
|
||||||
|
providerID: string
|
||||||
|
id: string
|
||||||
|
variant?: string
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const committed = selectionKey({ providerID: value.providerID, modelID: value.id, variant: value.variant })
|
||||||
|
pendingSelectionCommits.set(sessionID, committed)
|
||||||
|
return () => {
|
||||||
|
if (pendingSelectionCommits.get(sessionID) === committed) pendingSelectionCommits.delete(sessionID)
|
||||||
|
}
|
||||||
|
},
|
||||||
get ready() {
|
get ready() {
|
||||||
return modelStore.ready
|
return preferences.ready
|
||||||
|
},
|
||||||
|
get catalogReady() {
|
||||||
|
return models() !== undefined
|
||||||
},
|
},
|
||||||
recent() {
|
recent() {
|
||||||
return modelStore.recent
|
return preferences.recent
|
||||||
},
|
},
|
||||||
favorite() {
|
favorite() {
|
||||||
return modelStore.favorite
|
return preferences.favorite
|
||||||
},
|
},
|
||||||
parsed: createMemo(() => {
|
parsed: createMemo(() => {
|
||||||
const value = currentModel()
|
const value = currentSelection()
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return {
|
return {
|
||||||
provider: "Connect a provider",
|
provider: "Connect a provider",
|
||||||
@@ -230,33 +343,28 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
reasoning: false,
|
reasoning: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const provider = data.location.provider.list()?.find((item) => item.id === value.providerID)
|
const provider = providers()?.find((item) => item.id === value.providerID)
|
||||||
const info = data.location.model
|
const info = models()?.find((item) => item.providerID === value.providerID && item.id === value.modelID)
|
||||||
.list()
|
|
||||||
?.find((item) => item.providerID === value.providerID && item.id === value.modelID)
|
|
||||||
return {
|
return {
|
||||||
provider: provider?.name ?? value.providerID,
|
provider: provider?.name ?? value.providerID,
|
||||||
model: info?.name ?? value.modelID,
|
model: info?.name ?? `${value.modelID} (unavailable)`,
|
||||||
reasoning: (info?.variants?.length ?? 0) !== 0,
|
reasoning: (info?.variants?.length ?? 0) !== 0,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
cycle(direction: 1 | -1) {
|
cycle(direction: 1 | -1) {
|
||||||
const current = currentModel()
|
const current = currentSelection()
|
||||||
if (!current) return
|
if (!current) return
|
||||||
const recent = modelStore.recent
|
const recent = recentModels(current, preferences.recent).filter(isModelValid)
|
||||||
const index = recent.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
|
const index = recent.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
|
||||||
if (index === -1) return
|
let next = index === -1 ? (direction === 1 ? 0 : recent.length - 1) : index + direction
|
||||||
let next = index + direction
|
|
||||||
if (next < 0) next = recent.length - 1
|
if (next < 0) next = recent.length - 1
|
||||||
if (next >= recent.length) next = 0
|
if (next >= recent.length) next = 0
|
||||||
const val = recent[next]
|
const val = recent[next]
|
||||||
if (!val) return
|
if (!val) return
|
||||||
const a = agent.current()
|
selectModel({ ...val })
|
||||||
if (!a) return
|
|
||||||
setModelStore("model", a.id, { ...val })
|
|
||||||
},
|
},
|
||||||
cycleFavorite(direction: 1 | -1) {
|
cycleFavorite(direction: 1 | -1) {
|
||||||
const favorites = modelStore.favorite.filter((item) => isModelValid(item))
|
const favorites = preferences.favorite.filter((item) => isModelValid(item))
|
||||||
if (!favorites.length) {
|
if (!favorites.length) {
|
||||||
toast.show({
|
toast.show({
|
||||||
variant: "info",
|
variant: "info",
|
||||||
@@ -265,7 +373,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const current = currentModel()
|
const current = currentSelection()
|
||||||
let index = -1
|
let index = -1
|
||||||
if (current) {
|
if (current) {
|
||||||
index = favorites.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
|
index = favorites.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
|
||||||
@@ -279,45 +387,39 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
}
|
}
|
||||||
const next = favorites[index]
|
const next = favorites[index]
|
||||||
if (!next) return
|
if (!next) return
|
||||||
const a = agent.current()
|
if (!selectModel({ ...next })) return
|
||||||
if (!a) return
|
setPreferences("recent", recentModels(next, preferences.recent))
|
||||||
setModelStore("model", a.id, { ...next })
|
savePreferences()
|
||||||
setModelStore("recent", recentModels(next, modelStore.recent))
|
|
||||||
save()
|
|
||||||
},
|
},
|
||||||
set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
|
set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
if (!isModelValid(model)) return
|
if (!isModelValid(model)) return
|
||||||
const a = agent.current()
|
if (!selectModel(model)) return
|
||||||
if (!a) return
|
|
||||||
setModelStore("model", a.id, model)
|
|
||||||
if (options?.recent) {
|
if (options?.recent) {
|
||||||
setModelStore("recent", recentModels(model, modelStore.recent))
|
setPreferences("recent", recentModels(model, preferences.recent))
|
||||||
save()
|
savePreferences()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
toggleFavorite(model: { providerID: string; modelID: string }) {
|
toggleFavorite(model: { providerID: string; modelID: string }) {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
if (!isModelValid(model)) return
|
if (!isModelValid(model)) return
|
||||||
const exists = modelStore.favorite.some(
|
const exists = preferences.favorite.some(
|
||||||
(x) => x.providerID === model.providerID && x.modelID === model.modelID,
|
(x) => x.providerID === model.providerID && x.modelID === model.modelID,
|
||||||
)
|
)
|
||||||
const next = exists
|
const next = exists
|
||||||
? modelStore.favorite.filter((x) => x.providerID !== model.providerID || x.modelID !== model.modelID)
|
? preferences.favorite.filter((x) => x.providerID !== model.providerID || x.modelID !== model.modelID)
|
||||||
: [model, ...modelStore.favorite]
|
: [model, ...preferences.favorite]
|
||||||
setModelStore(
|
setPreferences(
|
||||||
"favorite",
|
"favorite",
|
||||||
next.map((x) => ({ providerID: x.providerID, modelID: x.modelID })),
|
next.map((x) => ({ providerID: x.providerID, modelID: x.modelID })),
|
||||||
)
|
)
|
||||||
save()
|
savePreferences()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
selected() {
|
selected() {
|
||||||
const m = currentModel()
|
return currentSelection()?.variant
|
||||||
if (!m) return undefined
|
|
||||||
return normalizeModelVariant(modelStore.variant[modelPreferenceKey(m)])
|
|
||||||
},
|
},
|
||||||
current() {
|
current() {
|
||||||
const v = this.selected()
|
const v = this.selected()
|
||||||
@@ -325,18 +427,20 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
list() {
|
list() {
|
||||||
const m = currentModel()
|
const m = currentSelection()
|
||||||
if (!m) return []
|
if (!m) return []
|
||||||
const info = data.location.model
|
const info = models()?.find((item) => item.providerID === m.providerID && item.id === m.modelID)
|
||||||
.list()
|
|
||||||
?.find((item) => item.providerID === m.providerID && item.id === m.modelID)
|
|
||||||
return info?.variants?.map((variant) => variant.id) ?? []
|
return info?.variants?.map((variant) => variant.id) ?? []
|
||||||
},
|
},
|
||||||
set(value: string | undefined) {
|
set(value: string | undefined) {
|
||||||
const m = currentModel()
|
const m = currentSelection()
|
||||||
if (!m) return
|
if (!m) return
|
||||||
setModelStore("variant", modelPreferenceKey(m), normalizeModelVariant(value))
|
if (route.data.type === "session") {
|
||||||
save()
|
setSessionDraft(route.data.sessionID, { ...m, variant: normalizeModelVariant(value) })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setPreferences("variant", modelPreferenceKey(m), normalizeModelVariant(value))
|
||||||
|
savePreferences()
|
||||||
},
|
},
|
||||||
cycle() {
|
cycle() {
|
||||||
const variants = this.list()
|
const variants = this.list()
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ export function Session() {
|
|||||||
const availableWidth = createMemo(
|
const availableWidth = createMemo(
|
||||||
() =>
|
() =>
|
||||||
dimensions().width -
|
dimensions().width -
|
||||||
(config.tabs?.enabled && config.tabs.vertical && sessionTabsFitVertically(dimensions().width)
|
(config.tabs?.enabled && config.tabs.layout === "vertical" && sessionTabsFitVertically(dimensions().width)
|
||||||
? SESSION_SIDEBAR_WIDTH
|
? SESSION_SIDEBAR_WIDTH
|
||||||
: 0),
|
: 0),
|
||||||
)
|
)
|
||||||
@@ -361,7 +361,7 @@ export function Session() {
|
|||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const current = prompt()
|
const current = prompt()
|
||||||
if (sent || !current || !synced() || !local.model.ready) return
|
if (sent || !current || !synced() || !local.model.ready || !local.model.catalogReady) return
|
||||||
if (!local.agent.current() || !local.model.current()) return
|
if (!local.agent.current() || !local.model.current()) return
|
||||||
if (!args.prompt || route.prompt?.text !== args.prompt || current.current.text !== args.prompt) return
|
if (!args.prompt || route.prompt?.text !== args.prompt || current.current.text !== args.prompt) return
|
||||||
sent = true
|
sent = true
|
||||||
@@ -827,7 +827,7 @@ export function Session() {
|
|||||||
options.format === "markdown"
|
options.format === "markdown"
|
||||||
? formatSessionTranscript(sessionData, messages(), options.thinking)
|
? formatSessionTranscript(sessionData, messages(), options.thinking)
|
||||||
: JSON.stringify(
|
: JSON.stringify(
|
||||||
await client.api.sessionTransfer.export({ sessionID: sessionData.id, sanitize: options.sanitize }),
|
await client.api.session.export({ sessionID: sessionData.id, sanitize: options.sanitize }),
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
) + EOL
|
) + EOL
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ test("validates mini replay settings", () => {
|
|||||||
test("validates the session tabs setting", () => {
|
test("validates the session tabs setting", () => {
|
||||||
const decode = Schema.decodeUnknownSync(Info)
|
const decode = Schema.decodeUnknownSync(Info)
|
||||||
|
|
||||||
expect(decode({ tabs: { enabled: true, vertical: true } })).toEqual({ tabs: { enabled: true, vertical: true } })
|
expect(decode({ tabs: { enabled: true, layout: "vertical" } })).toEqual({
|
||||||
|
tabs: { enabled: true, layout: "vertical" },
|
||||||
|
})
|
||||||
|
expect(() => decode({ tabs: { layout: true } })).toThrow()
|
||||||
expect(() => decode({ tabs: { enabled: "on" } })).toThrow()
|
expect(() => decode({ tabs: { enabled: "on" } })).toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -39,12 +42,13 @@ test("resolves nested config and keybind defaults", () => {
|
|||||||
expect(config.scroll).toEqual({ speed: 2, acceleration: true })
|
expect(config.scroll).toEqual({ speed: 2, acceleration: true })
|
||||||
expect(config.diffs).toEqual({ view: "split" })
|
expect(config.diffs).toEqual({ view: "split" })
|
||||||
expect(config.debug).toEqual({ devtools: true })
|
expect(config.debug).toEqual({ devtools: true })
|
||||||
expect(config.tabs).toEqual({ enabled: true, scope: "cwd" })
|
expect(config.tabs).toEqual({ enabled: true, scope: "cwd", layout: "horizontal" })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("shows resolved tab defaults in settings", () => {
|
test("shows resolved tab defaults in settings", () => {
|
||||||
expect(settings.find((setting) => setting.path.join(".") === "tabs.enabled")?.default).toBe(true)
|
expect(settings.find((setting) => setting.path.join(".") === "tabs.enabled")?.default).toBe(true)
|
||||||
expect(settings.find((setting) => setting.path.join(".") === "tabs.scope")?.default).toBe("cwd")
|
expect(settings.find((setting) => setting.path.join(".") === "tabs.scope")?.default).toBe("cwd")
|
||||||
|
expect(settings.find((setting) => setting.path.join(".") === "tabs.layout")?.default).toBe("horizontal")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("provides config and its host interface", async () => {
|
test("provides config and its host interface", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user