mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-24 06:33:01 -04:00
refactor(tui): simplify selection helpers (#43972)
This commit is contained in:
@@ -3,7 +3,7 @@ import { Effect, Option } from "effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { createClient, loadIntegrations } from "./shared"
|
||||
import { errorMessage } from "../../../ui/prompt"
|
||||
import { errorMessage } from "../../../util/error"
|
||||
|
||||
export default Runtime.handler(Commands.commands.auth.commands.list, (input) =>
|
||||
list(input).pipe(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { ServiceConfig } from "../../../services/service-config"
|
||||
import { createTimelineHost, type TimelineHost } from "../../../ui/timeline"
|
||||
import { errorMessage } from "../../../util/error"
|
||||
|
||||
const integrationID = "opencode"
|
||||
const location = { directory: process.cwd() }
|
||||
@@ -24,9 +25,9 @@ export default Runtime.handler(
|
||||
if (Exit.isSuccess(exit)) return
|
||||
|
||||
const cancelled = timeline.signal.aborted
|
||||
yield* request(() => timeline.failure(cancelled ? "Authorization cancelled" : errorMessage(exit.cause))).pipe(
|
||||
Effect.ignore,
|
||||
)
|
||||
yield* request(() =>
|
||||
timeline.failure(cancelled ? "Authorization cancelled" : errorMessage(Cause.squash(exit.cause))),
|
||||
).pipe(Effect.ignore)
|
||||
process.exitCode = cancelled ? 130 : 1
|
||||
}),
|
||||
)
|
||||
@@ -107,12 +108,3 @@ function request<A>(task: (signal: AbortSignal) => Promise<A>) {
|
||||
function required<A>(value: A | null | undefined, message: string) {
|
||||
return value === null || value === undefined ? Effect.fail(new Error(message)) : Effect.succeed(value)
|
||||
}
|
||||
|
||||
function errorMessage(cause: Cause.Cause<unknown>) {
|
||||
const error = Cause.squash(cause)
|
||||
if (error instanceof Error) return error.message
|
||||
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
||||
return error.message
|
||||
}
|
||||
return String(error)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ import { ServiceConfig } from "../../../services/service-config"
|
||||
export default Runtime.handler(
|
||||
Commands.commands.debug.commands.agents,
|
||||
Effect.fn("cli.debug.agents")(function* () {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const endpoint = found ?? (yield* Service.ensure(options))
|
||||
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const response = yield* Effect.promise(() => client.agent.list({ location: { directory: process.cwd() } }))
|
||||
process.stdout.write(
|
||||
|
||||
@@ -9,9 +9,7 @@ import { ServiceConfig } from "../../../services/service-config"
|
||||
export default Runtime.handler(
|
||||
Commands.commands.debug.commands.config,
|
||||
Effect.fn("cli.debug.config")(function* () {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const endpoint = found ?? (yield* Service.ensure(options))
|
||||
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const entries = yield* Effect.promise(() => client.config.get({ location: { directory: process.cwd() } }))
|
||||
process.stdout.write(JSON.stringify(entries, null, 2) + EOL)
|
||||
|
||||
@@ -6,7 +6,7 @@ import { EOL } from "node:os"
|
||||
import { Commands } from "../commands"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
import { ServerConnection } from "../../services/server-connection"
|
||||
import { errorMessage } from "../../ui/prompt"
|
||||
import { errorMessage } from "../../util/error"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.export,
|
||||
|
||||
@@ -17,9 +17,7 @@ const location = { directory: process.cwd() }
|
||||
export default Runtime.handler(
|
||||
Commands.commands.mcp.commands.auth,
|
||||
Effect.fn("cli.mcp.auth")(function* (input) {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const endpoint = found ?? (yield* Service.ensure(options))
|
||||
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
|
||||
const integration = yield* resolveIntegration(client, input.name, location)
|
||||
|
||||
@@ -9,9 +9,7 @@ import { ServiceConfig } from "../../../services/service-config"
|
||||
export default Runtime.handler(
|
||||
Commands.commands.mcp.commands.list,
|
||||
Effect.fn("cli.mcp.list")(function* () {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const endpoint = found ?? (yield* Service.ensure(options))
|
||||
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const response = yield* Effect.promise(() => client.mcp.list({ location: { directory: process.cwd() } }))
|
||||
const servers = response.data.toSorted((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
@@ -12,9 +12,7 @@ const location = { directory: process.cwd() }
|
||||
export default Runtime.handler(
|
||||
Commands.commands.mcp.commands.logout,
|
||||
Effect.fn("cli.mcp.logout")(function* (input) {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const endpoint = found ?? (yield* Service.ensure(options))
|
||||
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
|
||||
const integration = yield* resolveIntegration(client, input.name, location)
|
||||
|
||||
@@ -12,9 +12,7 @@ import { discoverTuiPlugins, tuiPluginDirectories } from "@opencode-ai/tui/plugi
|
||||
export default Runtime.handler(
|
||||
Commands.commands.plugin.commands.list,
|
||||
Effect.fn("cli.plugin.list")(function* (input) {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const endpoint = found ?? (yield* Service.ensure(options))
|
||||
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const response = yield* Effect.promise(() => client.plugin.list({ location: { directory: process.cwd() } }))
|
||||
const config = yield* Config.Service
|
||||
|
||||
@@ -10,6 +10,7 @@ import { toolInlineInfo } from "@opencode-ai/tui/mini/tool"
|
||||
import { runNonInteractivePrompt } from "./noninteractive"
|
||||
import { UI } from "./ui"
|
||||
import { Env } from "../env"
|
||||
import { errorMessage } from "../util/error"
|
||||
|
||||
export type RunCommandInput = {
|
||||
server: ServerConnection.Resolved
|
||||
@@ -243,13 +244,6 @@ async function renderToolError(part: SessionMessageAssistantTool, directory: str
|
||||
UI.println(UI.Style.TEXT_NORMAL + "✗", UI.Style.TEXT_NORMAL + `${info.title} failed`)
|
||||
}
|
||||
|
||||
function errorMessage(error: unknown) {
|
||||
if (error instanceof Error) return error.message
|
||||
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string")
|
||||
return error.message
|
||||
return String(error)
|
||||
}
|
||||
|
||||
/** @internal Used by the V1 command boundary before a Session exists. */
|
||||
export function reportRunError(input: Pick<RunCommandInput, "format">, message: string, sessionID?: string) {
|
||||
process.exitCode = 1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cancel, isCancel, log, outro } from "@clack/prompts"
|
||||
import { Effect } from "effect"
|
||||
import { errorMessage } from "../util/error"
|
||||
|
||||
const cancelled = Symbol("cancelled")
|
||||
|
||||
@@ -38,11 +39,3 @@ export function handlePromptErrors<A, E, R>(effect: Effect.Effect<A, E, R>) {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
export function errorMessage(error: unknown) {
|
||||
if (error instanceof Error) return error.message
|
||||
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
||||
return error.message
|
||||
}
|
||||
return String(error)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export function errorMessage(error: unknown) {
|
||||
if (error instanceof Error) return error.message
|
||||
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
||||
return error.message
|
||||
}
|
||||
return String(error)
|
||||
}
|
||||
@@ -33,12 +33,14 @@ describe("debug config command", () => {
|
||||
},
|
||||
]
|
||||
let requested: URL | undefined
|
||||
let healthProbes = 0
|
||||
const authorization: Array<string | null> = []
|
||||
const server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(request) {
|
||||
const url = new URL(request.url)
|
||||
if (url.pathname === "/api/health") {
|
||||
healthProbes += 1
|
||||
return Response.json({ healthy: true, version: OPENCODE_VERSION, pid: process.pid })
|
||||
}
|
||||
requested = url
|
||||
@@ -60,6 +62,7 @@ describe("debug config command", () => {
|
||||
expect(requested?.pathname).toBe("/api/config")
|
||||
expect(requested?.searchParams.get("location[directory]")).toBe(project)
|
||||
expect(authorization).toEqual([`Basic ${btoa("opencode:secret")}`])
|
||||
expect(healthProbes).toBe(1)
|
||||
} finally {
|
||||
server.stop(true)
|
||||
await fs.rm(root, { recursive: true, force: true })
|
||||
|
||||
@@ -35,7 +35,9 @@ describe("CLI frontend import boundaries", () => {
|
||||
test("keeps run and Mini on separate evaluation graphs", async () => {
|
||||
const run = await bundleInputs("packages/cli/src/commands/handlers/run.ts")
|
||||
expect(run).toContain("packages/cli/src/run/run.ts")
|
||||
expect(run).toContain("packages/cli/src/util/error.ts")
|
||||
expect(run).toContain("packages/tui/src/mini/tool.ts")
|
||||
expect(run).not.toContain("packages/cli/src/ui/prompt.ts")
|
||||
expect(run).not.toContain("packages/tui/src/mini/runtime.ts")
|
||||
expect(run).not.toContain("packages/tui/src/mini/runtime.lifecycle.ts")
|
||||
expect(run).not.toContain("packages/tui/src/mini/footer.ts")
|
||||
|
||||
@@ -291,8 +291,7 @@ const themeContext = createSimpleContext({
|
||||
}, delay),
|
||||
)
|
||||
}
|
||||
let unsubscribeRefresh: (() => void) | undefined
|
||||
unsubscribeRefresh = themes.subscribeRefresh?.(refresh)
|
||||
const unsubscribeRefresh = themes.subscribeRefresh?.(refresh)
|
||||
|
||||
onCleanup(() => {
|
||||
renderer.off(CliRenderEvents.THEME_MODE, handle)
|
||||
|
||||
@@ -786,22 +786,8 @@ export class RunFooter implements FooterApi {
|
||||
return
|
||||
}
|
||||
|
||||
const patch: FooterPatch = {}
|
||||
|
||||
if ("variants" in result) {
|
||||
this.setVariants(result.variants ?? [])
|
||||
}
|
||||
|
||||
if ("variant" in result) {
|
||||
this.setCurrentVariant(result.variant)
|
||||
}
|
||||
|
||||
if (result.modelLabel) {
|
||||
patch.model = result.modelLabel
|
||||
}
|
||||
|
||||
this.patch(patch)
|
||||
this.setNotice(result.status ?? "variant updated")
|
||||
this.applySelectionResult(result)
|
||||
if (result.status === undefined) this.setNotice("variant updated")
|
||||
}
|
||||
|
||||
private handleModelSelect = (model: NonNullable<RunInput["model"]>): void => {
|
||||
@@ -827,26 +813,7 @@ export class RunFooter implements FooterApi {
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if ("variants" in result) {
|
||||
this.setVariants(result.variants ?? [])
|
||||
}
|
||||
|
||||
if ("variant" in result) {
|
||||
this.setCurrentVariant(result.variant)
|
||||
}
|
||||
|
||||
const patch: FooterPatch = {}
|
||||
if (result.modelLabel) {
|
||||
patch.model = result.modelLabel
|
||||
}
|
||||
|
||||
if (patch.model) {
|
||||
this.patch(patch)
|
||||
}
|
||||
if (result.status) {
|
||||
this.setNotice(result.status)
|
||||
}
|
||||
this.applySelectionResult(result)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
@@ -875,30 +842,18 @@ export class RunFooter implements FooterApi {
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if ("variants" in result) {
|
||||
this.setVariants(result.variants ?? [])
|
||||
}
|
||||
|
||||
if ("variant" in result) {
|
||||
this.setCurrentVariant(result.variant)
|
||||
}
|
||||
|
||||
const patch: FooterPatch = {}
|
||||
if (result.modelLabel) {
|
||||
patch.model = result.modelLabel
|
||||
}
|
||||
|
||||
if (patch.model) {
|
||||
this.patch(patch)
|
||||
}
|
||||
if (result.status) {
|
||||
this.setNotice(result.status)
|
||||
}
|
||||
this.applySelectionResult(result)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
private applySelectionResult(result: CycleResult): void {
|
||||
if ("variants" in result) this.setVariants(result.variants ?? [])
|
||||
if ("variant" in result) this.setCurrentVariant(result.variant)
|
||||
if (result.modelLabel) this.patch({ model: result.modelLabel })
|
||||
if (result.status) this.setNotice(result.status)
|
||||
}
|
||||
|
||||
private handleMiniSettingChange = async (change: MiniSettingChange): Promise<void> => {
|
||||
if (!this.options.miniSettings.update) {
|
||||
this.setNotice("settings are unavailable")
|
||||
|
||||
@@ -272,19 +272,21 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
return current.type === "composer" ? "prompt" : current.type
|
||||
})
|
||||
|
||||
const openCommand = () => {
|
||||
setRoute({ type: "command" })
|
||||
const openRoute = (next: FooterPromptRoute) => {
|
||||
setRoute(next)
|
||||
props.onSubagentSelect?.(undefined)
|
||||
}
|
||||
|
||||
const openCommand = () => {
|
||||
openRoute({ type: "command" })
|
||||
}
|
||||
|
||||
const openModel = () => {
|
||||
setRoute({ type: "model" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "model" })
|
||||
}
|
||||
|
||||
const openAgent = () => {
|
||||
setRoute({ type: "agent" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "agent" })
|
||||
}
|
||||
|
||||
const openSkillMenu = () => {
|
||||
@@ -292,18 +294,15 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
return
|
||||
}
|
||||
|
||||
setRoute({ type: "skill" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "skill" })
|
||||
}
|
||||
|
||||
const openVariant = () => {
|
||||
setRoute({ type: "variant" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "variant" })
|
||||
}
|
||||
|
||||
const openSettings = () => {
|
||||
setRoute({ type: "settings" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "settings" })
|
||||
}
|
||||
|
||||
const openSubagentMenu = () => {
|
||||
@@ -311,14 +310,12 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
return
|
||||
}
|
||||
|
||||
setRoute({ type: "subagent-menu" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "subagent-menu" })
|
||||
}
|
||||
|
||||
const openQueuedMenu = () => {
|
||||
if (queue().length === 0) return
|
||||
setRoute({ type: "queued-menu" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "queued-menu" })
|
||||
}
|
||||
|
||||
const closePanel = () => {
|
||||
@@ -347,8 +344,7 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
}
|
||||
|
||||
const closeTab = () => {
|
||||
setRoute({ type: "composer" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
openRoute({ type: "composer" })
|
||||
}
|
||||
|
||||
const cycleTab = (dir: -1 | 1) => {
|
||||
|
||||
@@ -34,6 +34,7 @@ import type {
|
||||
} from "./types"
|
||||
import { canonicalToolName, normalizeTool, toolOutputText, toolView } from "./tool"
|
||||
import { toolDisplayContent } from "../util/tool-display"
|
||||
import { isRecord } from "../util/record"
|
||||
|
||||
const CHILD_MESSAGE_LIMIT = 80
|
||||
const CHILD_FRAME_LIMIT = 80
|
||||
@@ -154,8 +155,7 @@ type DiscoveryJob = {
|
||||
}
|
||||
|
||||
function record(value: unknown): Record<string, unknown> | undefined {
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) return value as Record<string, unknown>
|
||||
return undefined
|
||||
return isRecord(value) ? value : undefined
|
||||
}
|
||||
|
||||
function text(value: unknown): string | undefined {
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
webSearchProviderLabel,
|
||||
} from "../util/tool-display"
|
||||
import { formatPath } from "../util/path-format"
|
||||
import { isRecord } from "../util/record"
|
||||
import type { RunEntryBody, StreamCommit, ToolSnapshot } from "./types"
|
||||
|
||||
export { canonicalToolName } from "../util/tool-display"
|
||||
@@ -138,11 +139,7 @@ type ToolRegistry = Record<ToolName, ToolRule>
|
||||
type AnyToolRule = ToolRule
|
||||
|
||||
function dict(v: unknown): ToolDict {
|
||||
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return { ...v }
|
||||
return isRecord(v) ? { ...v } : {}
|
||||
}
|
||||
|
||||
function props(frame: ToolFrame): ToolProps {
|
||||
|
||||
@@ -63,10 +63,7 @@ export function SubagentsTab(props: { sessionID: string }) {
|
||||
let wasActive = false
|
||||
let scroll: ScrollBoxRenderable | undefined
|
||||
|
||||
const selected = createMemo(() => {
|
||||
return store.selected
|
||||
})
|
||||
const selectedEntry = createMemo(() => entries()[selected()])
|
||||
const selectedEntry = createMemo(() => entries()[store.selected])
|
||||
|
||||
createEffect(() => {
|
||||
const active = composer.active("subagents")
|
||||
@@ -95,11 +92,7 @@ export function SubagentsTab(props: { sessionID: string }) {
|
||||
|
||||
function moveTo(next: number, center = false) {
|
||||
setStore("selected", next)
|
||||
scrollToSelection(center)
|
||||
}
|
||||
|
||||
function scrollToSelection(center: boolean) {
|
||||
scrollToIndex(selected(), center)
|
||||
scrollToIndex(next, center)
|
||||
}
|
||||
|
||||
function scrollToIndex(index: number, center: boolean) {
|
||||
@@ -209,7 +202,7 @@ export function SubagentsTab(props: { sessionID: string }) {
|
||||
>
|
||||
<For each={entries()}>
|
||||
{(entry, index) => {
|
||||
const active = createMemo(() => index() === selected())
|
||||
const active = createMemo(() => index() === store.selected)
|
||||
const status = createMemo(() => {
|
||||
if (entry.status === "running") return "Running"
|
||||
return ""
|
||||
|
||||
@@ -108,6 +108,7 @@ import type { SessionInbox } from "@opencode-ai/schema/session-inbox"
|
||||
import { generateThinkingSyntax } from "./thinking-syntax"
|
||||
import { createDelayedPresence } from "../../util/delayed-presence"
|
||||
import { SessionLocationMissing } from "./location-missing"
|
||||
import { isRecord } from "../../util/record"
|
||||
|
||||
addDefaultParsers(parsers.parsers)
|
||||
|
||||
@@ -131,7 +132,6 @@ const context = createContext<{
|
||||
terminal: { width: number; height: number }
|
||||
sessionID: string
|
||||
thinkingMode: () => ThinkingMode
|
||||
showThinking: () => boolean
|
||||
markdownMode: () => "source" | "rendered"
|
||||
groupExploration: () => boolean
|
||||
diffWrapMode: () => "word" | "none"
|
||||
@@ -228,7 +228,6 @@ export function Session(props: { verticalTabsWidth: number }) {
|
||||
const sidebar = createMemo(() => config.session?.sidebar ?? "auto")
|
||||
const [sidebarOpen, setSidebarOpen] = createSignal(false)
|
||||
const thinkingMode = createMemo<ThinkingMode>(() => config.session?.thinking ?? "hide")
|
||||
const showThinking = createMemo(() => true)
|
||||
const showScrollbar = createMemo(() => config.session?.scrollbar ?? false)
|
||||
const markdownMode = createMemo(() => config.session?.markdown ?? "rendered")
|
||||
const diffWrapMode = createMemo(() => config.diffs?.wrap ?? "word")
|
||||
@@ -996,7 +995,7 @@ export function Session(props: { verticalTabsWidth: number }) {
|
||||
try {
|
||||
const sessionData = session()
|
||||
if (!sessionData) return
|
||||
const transcript = formatSessionTranscript(sessionData, messages(), showThinking())
|
||||
const transcript = formatSessionTranscript(sessionData, messages(), true)
|
||||
await clipboard.write(transcript)
|
||||
toast.show({ message: "Session transcript copied to clipboard!", variant: "success" })
|
||||
} catch {
|
||||
@@ -1017,7 +1016,7 @@ export function Session(props: { verticalTabsWidth: number }) {
|
||||
const sessionData = session()
|
||||
if (!sessionData) return
|
||||
|
||||
const options = await DialogExportOptions.show(dialog, showThinking())
|
||||
const options = await DialogExportOptions.show(dialog, true)
|
||||
|
||||
if (options === null) return
|
||||
|
||||
@@ -1152,7 +1151,6 @@ export function Session(props: { verticalTabsWidth: number }) {
|
||||
},
|
||||
sessionID: route.sessionID,
|
||||
thinkingMode,
|
||||
showThinking,
|
||||
markdownMode,
|
||||
groupExploration,
|
||||
diffWrapMode,
|
||||
@@ -3624,8 +3622,7 @@ export function toolDisplay(tool: string) {
|
||||
}
|
||||
|
||||
function recordValue(value: unknown): Record<string, unknown> | undefined {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) return
|
||||
return value as Record<string, unknown>
|
||||
return isRecord(value) ? value : undefined
|
||||
}
|
||||
|
||||
function formatSessionTranscript(session: SessionInfo, messages: SessionMessageInfo[], thinking: boolean) {
|
||||
|
||||
Reference in New Issue
Block a user