chore: generate

This commit is contained in:
opencode-agent[bot]
2026-06-08 19:43:41 +00:00
parent c06ad7c881
commit 537666149b
24 changed files with 848 additions and 876 deletions
+1 -3
View File
@@ -227,9 +227,7 @@ export const layer = Layer.effect(
yield* events.publish(Event.Refreshed, {})
}),
).pipe(
Effect.tapCause((cause) =>
Effect.logError("Failed to fetch models.dev", { cause: cause }),
),
Effect.tapCause((cause) => Effect.logError("Failed to fetch models.dev", { cause: cause })),
Effect.ignore,
)
})
+5 -1
View File
@@ -20,7 +20,11 @@ function formatter(id: string = runID) {
})
}
function flatten(input: Record<string, unknown>, prefix = "", seen = new WeakSet<object>()): Array<readonly [string, unknown]> {
function flatten(
input: Record<string, unknown>,
prefix = "",
seen = new WeakSet<object>(),
): Array<readonly [string, unknown]> {
if (seen.has(input)) return [[prefix, "[Circular]"]]
seen.add(input)
const entries = Object.entries(input)
+5 -1
View File
@@ -103,7 +103,11 @@ export const layer = Layer.effect(
(materializer) =>
materializer.run.pipe(
Effect.catchCause((cause) =>
Effect.logWarning("failed to materialize project reference", { name: materializer.name, repository: materializer.repository, cause }),
Effect.logWarning("failed to materialize project reference", {
name: materializer.name,
repository: materializer.repository,
cause,
}),
),
),
{ concurrency: 4, discard: true },
+10 -6
View File
@@ -154,7 +154,9 @@ export const layer = Layer.effect(
contextLimitLoader.providers(input.directory).pipe(
Effect.map((providers) => findContextLimit(providers, input.providerID, input.modelID)),
Effect.catch((error) =>
Effect.logError("failed to get providers for usage context limit", { error: error }).pipe(Effect.as(undefined)),
Effect.logError("failed to get providers for usage context limit", { error: error }).pipe(
Effect.as(undefined),
),
),
),
)
@@ -176,11 +178,13 @@ export const layer = Layer.effect(
readonly sessionID: string
readonly directory: string
}) {
const messages = yield* messageLoader.messages({ sessionID: input.sessionID, directory: input.directory }).pipe(
Effect.catch((error) =>
Effect.logError("failed to fetch messages for usage update", { error: error }).pipe(Effect.as(undefined)),
),
)
const messages = yield* messageLoader
.messages({ sessionID: input.sessionID, directory: input.directory })
.pipe(
Effect.catch((error) =>
Effect.logError("failed to fetch messages for usage update", { error: error }).pipe(Effect.as(undefined)),
),
)
if (!messages) return
const message = latestAssistantMessage(messages)
@@ -174,233 +174,233 @@ function queueSplash(
// scrollback commits and footer repaints happen in the same frame. After
// the entry splash, RunFooter takes over the footer region.
export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lifecycle> {
const source = resolveInteractiveStdin()
let unregisterKeymap: (() => void) | undefined
const source = resolveInteractiveStdin()
let unregisterKeymap: (() => void) | undefined
try {
const renderer = await createCliRenderer({
stdin: source.stdin,
targetFps: 30,
maxFps: 60,
useMouse: false,
autoFocus: false,
openConsoleOnError: false,
exitOnCtrlC: false,
useKittyKeyboard: { events: process.platform === "win32" },
screenMode: "split-footer",
footerHeight: FOOTER_HEIGHT,
externalOutputMode: "capture-stdout",
consoleMode: "disabled",
clearOnShutdown: false,
})
const theme = await resolveRunTheme(renderer)
renderer.setBackgroundColor(theme.background)
const keymap = createDefaultOpenTuiKeymap(renderer)
unregisterKeymap = registerOpencodeKeymap(keymap, renderer, input.tuiConfig)
const state: SplashState = {
entry: false,
exit: false,
}
const splash = splashInfo(input.sessionTitle, input.history)
const meta = splashMeta({
title: splash.title,
session_id: input.sessionID,
})
const labels = footerLabels({
agent: input.agent,
model: input.model,
variant: input.variant,
})
const footerTask = import("./footer")
const wrote = queueSplash(
renderer,
state,
"entry",
entrySplash({
...meta,
theme: theme.splash,
showSession: splash.showSession,
detail: directoryLabel(input.directory),
}),
)
await renderer.idle().catch(() => {})
const { RunFooter } = await footerTask
let closed = false
let sigintRegistered = false
const footer = new RunFooter(renderer, {
directory: input.directory,
findFiles: input.findFiles,
agents: input.agents,
resources: input.resources,
sessionID: input.getSessionID ?? (() => input.sessionID),
...labels,
model: input.model,
variant: input.variant,
first: input.first,
history: input.history,
theme,
wrote,
keymap,
tuiConfig: input.tuiConfig,
backgroundSubagents: input.backgroundSubagents,
diffStyle: input.tuiConfig.diff_style ?? "auto",
onPermissionReply: input.onPermissionReply,
onQuestionReply: input.onQuestionReply,
onQuestionReject: input.onQuestionReject,
onCycleVariant: input.onCycleVariant,
onModelSelect: input.onModelSelect,
onVariantSelect: input.onVariantSelect,
onInterrupt: input.onInterrupt,
onBackground: input.onBackground,
onEditorOpen: async ({ value }) => {
if (closed || renderer.isDestroyed) {
return
}
await renderer.idle().catch(() => {})
const ignore = () => {}
detachSigint()
process.on("SIGINT", ignore)
try {
return await openEditor({
value,
cwd: input.directory,
renderer,
stdin: source.stdin,
})
} finally {
process.off("SIGINT", ignore)
attachSigint()
}
},
onSubagentSelect: input.onSubagentSelect,
})
const sigint = () => {
footer.requestExit()
}
const attachSigint = () => {
if (closed || sigintRegistered) {
return
}
process.on("SIGINT", sigint)
sigintRegistered = true
}
const detachSigint = () => {
if (!sigintRegistered) {
return
}
process.off("SIGINT", sigint)
sigintRegistered = false
}
attachSigint()
const close = async (next: {
showExit: boolean
sessionTitle?: string
sessionID?: string
history?: RunPrompt[]
}) => {
if (closed) {
return
}
closed = true
detachSigint()
let wroteExit = false
try {
const renderer = await createCliRenderer({
stdin: source.stdin,
targetFps: 30,
maxFps: 60,
useMouse: false,
autoFocus: false,
openConsoleOnError: false,
exitOnCtrlC: false,
useKittyKeyboard: { events: process.platform === "win32" },
screenMode: "split-footer",
footerHeight: FOOTER_HEIGHT,
externalOutputMode: "capture-stdout",
consoleMode: "disabled",
clearOnShutdown: false,
})
const theme = await resolveRunTheme(renderer)
renderer.setBackgroundColor(theme.background)
const keymap = createDefaultOpenTuiKeymap(renderer)
unregisterKeymap = registerOpencodeKeymap(keymap, renderer, input.tuiConfig)
const state: SplashState = {
entry: false,
exit: false,
await footer.idle().catch(() => {})
const show = renderer.isDestroyed ? false : next.showExit
if (!renderer.isDestroyed && show) {
const sessionID = next.sessionID || input.getSessionID?.() || input.sessionID
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history ?? input.history)
wroteExit = queueSplash(
renderer,
state,
"exit",
exitSplash({
...splashMeta({
title: splash.title,
session_id: sessionID,
}),
theme: footer.currentTheme().splash,
}),
)
await renderer.idle().catch(() => {})
}
const splash = splashInfo(input.sessionTitle, input.history)
const meta = splashMeta({
title: splash.title,
session_id: input.sessionID,
})
const labels = footerLabels({
agent: input.agent,
model: input.model,
variant: input.variant,
})
const footerTask = import("./footer")
const wrote = queueSplash(
renderer,
state,
"entry",
} finally {
footer.close()
await footer.idle().catch(() => {})
footer.destroy()
unregisterKeymap?.()
shutdown(renderer)
if (!wroteExit) {
process.stdout.write("\n")
}
source.cleanup?.()
}
}
return {
footer,
refreshTheme() {
footer.refreshTheme()
},
onResize(fn) {
let width = renderer.terminalWidth
let height = renderer.terminalHeight
const resize = () => {
if (width === renderer.terminalWidth && height === renderer.terminalHeight) {
return
}
width = renderer.terminalWidth
height = renderer.terminalHeight
fn()
}
renderer.on(CliRenderEvents.RESIZE, resize)
return () => renderer.off(CliRenderEvents.RESIZE, resize)
},
async resetForReplay(next) {
if (closed || renderer.isDestroyed || footer.isClosed) {
throw new Error("runtime closed")
}
await footer.idle()
if (closed || renderer.isDestroyed || footer.isClosed) {
throw new Error("runtime closed")
}
footer.resetForReplay(true)
renderer.resetSplitFooterForReplay({ clearSavedLines: true })
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history)
renderer.writeToScrollback(
entrySplash({
...meta,
theme: theme.splash,
...splashMeta({
title: splash.title,
session_id: next.sessionID ?? input.getSessionID?.() ?? input.sessionID,
}),
theme: footer.currentTheme().splash,
showSession: splash.showSession,
detail: directoryLabel(input.directory),
}),
)
await renderer.idle().catch(() => {})
const { RunFooter } = await footerTask
let closed = false
let sigintRegistered = false
const footer = new RunFooter(renderer, {
directory: input.directory,
findFiles: input.findFiles,
agents: input.agents,
resources: input.resources,
sessionID: input.getSessionID ?? (() => input.sessionID),
...labels,
model: input.model,
variant: input.variant,
first: input.first,
history: input.history,
theme,
wrote,
keymap,
tuiConfig: input.tuiConfig,
backgroundSubagents: input.backgroundSubagents,
diffStyle: input.tuiConfig.diff_style ?? "auto",
onPermissionReply: input.onPermissionReply,
onQuestionReply: input.onQuestionReply,
onQuestionReject: input.onQuestionReject,
onCycleVariant: input.onCycleVariant,
onModelSelect: input.onModelSelect,
onVariantSelect: input.onVariantSelect,
onInterrupt: input.onInterrupt,
onBackground: input.onBackground,
onEditorOpen: async ({ value }) => {
if (closed || renderer.isDestroyed) {
return
}
await renderer.idle().catch(() => {})
const ignore = () => {}
detachSigint()
process.on("SIGINT", ignore)
try {
return await openEditor({
value,
cwd: input.directory,
renderer,
stdin: source.stdin,
})
} finally {
process.off("SIGINT", ignore)
attachSigint()
}
},
onSubagentSelect: input.onSubagentSelect,
})
const sigint = () => {
footer.requestExit()
}
const attachSigint = () => {
if (closed || sigintRegistered) {
return
}
process.on("SIGINT", sigint)
sigintRegistered = true
}
const detachSigint = () => {
if (!sigintRegistered) {
return
}
process.off("SIGINT", sigint)
sigintRegistered = false
}
attachSigint()
const close = async (next: {
showExit: boolean
sessionTitle?: string
sessionID?: string
history?: RunPrompt[]
}) => {
if (closed) {
return
}
closed = true
detachSigint()
let wroteExit = false
try {
await footer.idle().catch(() => {})
const show = renderer.isDestroyed ? false : next.showExit
if (!renderer.isDestroyed && show) {
const sessionID = next.sessionID || input.getSessionID?.() || input.sessionID
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history ?? input.history)
wroteExit = queueSplash(
renderer,
state,
"exit",
exitSplash({
...splashMeta({
title: splash.title,
session_id: sessionID,
}),
theme: footer.currentTheme().splash,
}),
)
await renderer.idle().catch(() => {})
}
} finally {
footer.close()
await footer.idle().catch(() => {})
footer.destroy()
unregisterKeymap?.()
shutdown(renderer)
if (!wroteExit) {
process.stdout.write("\n")
}
source.cleanup?.()
}
}
return {
footer,
refreshTheme() {
footer.refreshTheme()
},
onResize(fn) {
let width = renderer.terminalWidth
let height = renderer.terminalHeight
const resize = () => {
if (width === renderer.terminalWidth && height === renderer.terminalHeight) {
return
}
width = renderer.terminalWidth
height = renderer.terminalHeight
fn()
}
renderer.on(CliRenderEvents.RESIZE, resize)
return () => renderer.off(CliRenderEvents.RESIZE, resize)
},
async resetForReplay(next) {
if (closed || renderer.isDestroyed || footer.isClosed) {
throw new Error("runtime closed")
}
await footer.idle()
if (closed || renderer.isDestroyed || footer.isClosed) {
throw new Error("runtime closed")
}
footer.resetForReplay(true)
renderer.resetSplitFooterForReplay({ clearSavedLines: true })
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history)
renderer.writeToScrollback(
entrySplash({
...splashMeta({
title: splash.title,
session_id: next.sessionID ?? input.getSessionID?.() ?? input.sessionID,
}),
theme: footer.currentTheme().splash,
showSession: splash.showSession,
detail: directoryLabel(input.directory),
}),
)
renderer.requestRender()
},
close,
}
} catch (error) {
unregisterKeymap?.()
source.cleanup?.()
throw error
}
renderer.requestRender()
},
close,
}
} catch (error) {
unregisterKeymap?.()
source.cleanup?.()
throw error
}
}
+573 -573
View File
@@ -179,608 +179,608 @@ async function resolveExitTitle(
// Files only attach on the first prompt turn -- after that, includeFiles
// flips to false so subsequent turns don't re-send attachments.
async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDeps = {}): Promise<void> {
const start = performance.now()
const log = trace()
const tuiConfigTask = resolveRunTuiConfig()
const ctx = await input.boot()
const modelTask = resolveModelInfo(ctx.sdk, ctx.directory, ctx.model)
const sessionTask =
ctx.resume === true
? resolveSessionInfo(ctx.sdk, ctx.sessionID, ctx.model)
: Promise.resolve({
first: true,
history: [],
variant: undefined,
})
const savedTask = resolveSavedVariant(ctx.model)
const [tuiConfig, session, savedVariant] = await Promise.all([tuiConfigTask, sessionTask, savedTask])
const state: RuntimeState = {
shown: !session.first,
aborting: false,
model: ctx.model,
providers: [],
variants: [],
limits: {},
activeVariant: resolveVariant(ctx.variant, session.variant, savedVariant, []),
sessionID: ctx.sessionID,
history: [...session.history],
localRows: [],
sessionTitle: ctx.sessionTitle,
agent: ctx.agent,
}
const ensureSession = () => {
if (!input.resolveSession || state.sessionID) {
return Promise.resolve()
}
if (state.session) {
return state.session
}
state.session = input.resolveSession(ctx).then((next) => {
state.sessionID = next.sessionID
state.sessionTitle = next.sessionTitle ?? state.sessionTitle
state.agent = next.agent
const start = performance.now()
const log = trace()
const tuiConfigTask = resolveRunTuiConfig()
const ctx = await input.boot()
const modelTask = resolveModelInfo(ctx.sdk, ctx.directory, ctx.model)
const sessionTask =
ctx.resume === true
? resolveSessionInfo(ctx.sdk, ctx.sessionID, ctx.model)
: Promise.resolve({
first: true,
history: [],
variant: undefined,
})
return state.session
const savedTask = resolveSavedVariant(ctx.model)
const [tuiConfig, session, savedVariant] = await Promise.all([tuiConfigTask, sessionTask, savedTask])
const state: RuntimeState = {
shown: !session.first,
aborting: false,
model: ctx.model,
providers: [],
variants: [],
limits: {},
activeVariant: resolveVariant(ctx.variant, session.variant, savedVariant, []),
sessionID: ctx.sessionID,
history: [...session.history],
localRows: [],
sessionTitle: ctx.sessionTitle,
agent: ctx.agent,
}
const ensureSession = () => {
if (!input.resolveSession || state.sessionID) {
return Promise.resolve()
}
if (state.session) {
return state.session
}
state.session = input.resolveSession(ctx).then((next) => {
state.sessionID = next.sessionID
state.sessionTitle = next.sessionTitle ?? state.sessionTitle
state.agent = next.agent
})
return state.session
}
const shell = await (deps.createRuntimeLifecycle ?? createRuntimeLifecycle)({
directory: ctx.directory,
findFiles: (query) =>
ctx.sdk.find
.files({ query, directory: ctx.directory })
.then((x) => x.data ?? [])
.catch(() => []),
agents: [],
resources: [],
sessionID: state.sessionID,
sessionTitle: state.sessionTitle,
getSessionID: () => state.sessionID,
first: session.first,
history: session.history,
agent: state.agent,
model: state.model,
variant: state.activeVariant,
tuiConfig,
backgroundSubagents: input.backgroundSubagents,
onPermissionReply: async (next) => {
if (state.demo?.permission(next)) {
return
}
const shell = await (deps.createRuntimeLifecycle ?? createRuntimeLifecycle)({
directory: ctx.directory,
findFiles: (query) =>
ctx.sdk.find
.files({ query, directory: ctx.directory })
.then((x) => x.data ?? [])
.catch(() => []),
agents: [],
resources: [],
sessionID: state.sessionID,
sessionTitle: state.sessionTitle,
getSessionID: () => state.sessionID,
first: session.first,
history: session.history,
agent: state.agent,
model: state.model,
log?.write("send.permission.reply", next)
await ctx.sdk.permission.reply(next)
},
onQuestionReply: async (next) => {
if (state.demo?.questionReply(next)) {
return
}
await ctx.sdk.question.reply(next)
},
onQuestionReject: async (next) => {
if (state.demo?.questionReject(next)) {
return
}
await ctx.sdk.question.reject(next)
},
onCycleVariant: () => {
if (!state.model || state.variants.length === 0) {
return {
status: "no variants available",
}
}
state.activeVariant = cycleVariant(state.activeVariant, state.variants)
saveVariant(state.model, state.activeVariant)
return {
status: state.activeVariant ? `variant ${state.activeVariant}` : "variant default",
modelLabel: formatModelLabel(state.model, state.activeVariant, state.providers),
variant: state.activeVariant,
tuiConfig,
backgroundSubagents: input.backgroundSubagents,
onPermissionReply: async (next) => {
if (state.demo?.permission(next)) {
return
}
}
},
onModelSelect: async (model) => {
if (state.model?.providerID === model.providerID && state.model.modelID === model.modelID) {
return
}
log?.write("send.permission.reply", next)
await ctx.sdk.permission.reply(next)
},
onQuestionReply: async (next) => {
if (state.demo?.questionReply(next)) {
return
}
state.model = model
state.activeVariant = undefined
state.variants = variantsFor(state.providers, model)
const switching = resolveSavedVariant(model).then((saved) => {
const current = state.model
if (!current || current.providerID !== model.providerID || current.modelID !== model.modelID) {
return
}
await ctx.sdk.question.reply(next)
},
onQuestionReject: async (next) => {
if (state.demo?.questionReject(next)) {
return
}
await ctx.sdk.question.reject(next)
},
onCycleVariant: () => {
if (!state.model || state.variants.length === 0) {
return {
status: "no variants available",
}
}
state.activeVariant = cycleVariant(state.activeVariant, state.variants)
saveVariant(state.model, state.activeVariant)
return {
status: state.activeVariant ? `variant ${state.activeVariant}` : "variant default",
modelLabel: formatModelLabel(state.model, state.activeVariant, state.providers),
variant: state.activeVariant,
}
},
onModelSelect: async (model) => {
if (state.model?.providerID === model.providerID && state.model.modelID === model.modelID) {
return
}
state.model = model
state.activeVariant = undefined
state.variants = variantsFor(state.providers, model)
const switching = resolveSavedVariant(model).then((saved) => {
const current = state.model
if (!current || current.providerID !== model.providerID || current.modelID !== model.modelID) {
return
}
state.activeVariant = resolveVariant(ctx.variant, undefined, saved, state.variants)
})
state.switching = switching
await switching
if (state.switching === switching) {
state.switching = undefined
}
const current = state.model
if (!current || current.providerID !== model.providerID || current.modelID !== model.modelID) {
return
}
return {
modelLabel: formatModelLabel(model, state.activeVariant, state.providers),
status: `model ${model.modelID}`,
variant: state.activeVariant,
variants: state.variants,
}
},
onVariantSelect: async (variant) => {
if (!state.model || state.variants.length === 0) {
return {
status: "no variants available",
}
}
if (variant && !state.variants.includes(variant)) {
return {
status: `variant ${variant} unavailable`,
}
}
state.activeVariant = variant
saveVariant(state.model, state.activeVariant)
return {
status: state.activeVariant ? `variant ${state.activeVariant}` : "variant default",
modelLabel: formatModelLabel(state.model, state.activeVariant, state.providers),
variant: state.activeVariant,
variants: state.variants,
}
},
onInterrupt: () => {
if (!hasSession(input, state) || state.aborting) {
return
}
state.aborting = true
void ctx.sdk.session
.abort({
sessionID: state.sessionID,
})
.catch(() => {})
.finally(() => {
state.aborting = false
})
},
onBackground: () => {
if (!hasSession(input, state)) return
void ctx.sdk.experimental.session.background({ sessionID: state.sessionID }).catch(() => {})
},
onSubagentSelect: (sessionID) => {
state.selectSubagent?.(sessionID)
log?.write("subagent.select", {
sessionID,
})
},
state.activeVariant = resolveVariant(ctx.variant, undefined, saved, state.variants)
})
const footer = shell.footer
const rememberLocal = (commit: StreamCommit, after?: LocalReplayAnchor) => {
state.localRows = [...state.localRows, { commit, after }].slice(-LOCAL_REPLAY_ROW_LIMIT)
state.switching = switching
await switching
if (state.switching === switching) {
state.switching = undefined
}
const loadCatalog = async (): Promise<void> => {
if (footer.isClosed) {
return
const current = state.model
if (!current || current.providerID !== model.providerID || current.modelID !== model.modelID) {
return
}
return {
modelLabel: formatModelLabel(model, state.activeVariant, state.providers),
status: `model ${model.modelID}`,
variant: state.activeVariant,
variants: state.variants,
}
},
onVariantSelect: async (variant) => {
if (!state.model || state.variants.length === 0) {
return {
status: "no variants available",
}
}
const [agents, resources, commands] = await Promise.all([
ctx.sdk.app
.agents({ directory: ctx.directory })
.then((x) => x.data ?? [])
.catch(() => []),
ctx.sdk.experimental.resource
.list({ directory: ctx.directory })
.then((x) => Object.values(x.data ?? {}))
.catch(() => []),
ctx.sdk.command
.list({ directory: ctx.directory })
.then((x) => x.data ?? [])
.catch(() => []),
])
if (footer.isClosed) {
return
if (variant && !state.variants.includes(variant)) {
return {
status: `variant ${variant} unavailable`,
}
footer.event({
type: "catalog",
agents,
resources,
commands,
})
}
void footer
.idle()
.then(loadCatalog)
.catch(() => {})
if (Flag.OPENCODE_SHOW_TTFD) {
footer.append({
kind: "system",
text: `startup ${Math.max(0, Math.round(performance.now() - start))}ms`,
phase: "final",
source: "system",
})
state.activeVariant = variant
saveVariant(state.model, state.activeVariant)
return {
status: state.activeVariant ? `variant ${state.activeVariant}` : "variant default",
modelLabel: formatModelLabel(state.model, state.activeVariant, state.providers),
variant: state.activeVariant,
variants: state.variants,
}
},
onInterrupt: () => {
if (!hasSession(input, state) || state.aborting) {
return
}
if (input.demo) {
await ensureSession()
state.demo = createRunDemo({
footer,
state.aborting = true
void ctx.sdk.session
.abort({
sessionID: state.sessionID,
thinking: input.thinking,
limits: () => state.limits,
})
}
if (input.afterPaint) {
void Promise.resolve(input.afterPaint(ctx)).catch(() => {})
}
void modelTask.then((info) => {
state.providers = info.providers
state.variants = variantsFor(state.providers, state.model)
state.limits = info.limits
const next = resolveVariant(ctx.variant, session.variant, savedVariant, state.variants)
if (next !== state.activeVariant) {
state.activeVariant = next
}
if (footer.isClosed) {
return
}
footer.event({ type: "models", providers: info.providers })
footer.event({ type: "variants", variants: state.variants, current: state.activeVariant })
if (!state.model) {
return
}
footer.event({
type: "model",
model: formatModelLabel(state.model, state.activeVariant, state.providers),
.catch(() => {})
.finally(() => {
state.aborting = false
})
},
onBackground: () => {
if (!hasSession(input, state)) return
void ctx.sdk.experimental.session.background({ sessionID: state.sessionID }).catch(() => {})
},
onSubagentSelect: (sessionID) => {
state.selectSubagent?.(sessionID)
log?.write("subagent.select", {
sessionID,
})
},
})
const footer = shell.footer
const rememberLocal = (commit: StreamCommit, after?: LocalReplayAnchor) => {
state.localRows = [...state.localRows, { commit, after }].slice(-LOCAL_REPLAY_ROW_LIMIT)
}
const streamTask = deps.streamTransport ?? import("./stream.transport")
const ensureStream = () => {
if (state.stream) {
return state.stream
}
const loadCatalog = async (): Promise<void> => {
if (footer.isClosed) {
return
}
// Share eager prewarm and first-turn boot through one in-flight promise,
// but clear it if transport creation fails so a later prompt can retry.
const next = (async () => {
await ensureSession()
if (footer.isClosed) {
throw new Error("runtime closed")
}
const [agents, resources, commands] = await Promise.all([
ctx.sdk.app
.agents({ directory: ctx.directory })
.then((x) => x.data ?? [])
.catch(() => []),
ctx.sdk.experimental.resource
.list({ directory: ctx.directory })
.then((x) => Object.values(x.data ?? {}))
.catch(() => []),
ctx.sdk.command
.list({ directory: ctx.directory })
.then((x) => x.data ?? [])
.catch(() => []),
])
if (footer.isClosed) {
return
}
const mod = await streamTask
if (footer.isClosed) {
throw new Error("runtime closed")
}
footer.event({
type: "catalog",
agents,
resources,
commands,
})
}
const handle = await mod.createSessionTransport({
sdk: ctx.sdk,
directory: ctx.directory,
sessionID: state.sessionID,
thinking: input.thinking,
replay: input.replay,
replayLimit: input.replayLimit,
limits: () => state.limits,
providers: () => state.providers,
footer,
trace: log,
})
if (footer.isClosed) {
await handle.close()
throw new Error("runtime closed")
}
void footer
.idle()
.then(loadCatalog)
.catch(() => {})
state.selectSubagent = (sessionID) => handle.selectSubagent(sessionID)
return { mod, handle }
})()
state.stream = next
void next.catch(() => {
if (state.stream === next) {
state.stream = undefined
}
})
return next
if (Flag.OPENCODE_SHOW_TTFD) {
footer.append({
kind: "system",
text: `startup ${Math.max(0, Math.round(performance.now() - start))}ms`,
phase: "final",
source: "system",
})
}
if (input.demo) {
await ensureSession()
state.demo = createRunDemo({
footer,
sessionID: state.sessionID,
thinking: input.thinking,
limits: () => state.limits,
})
}
if (input.afterPaint) {
void Promise.resolve(input.afterPaint(ctx)).catch(() => {})
}
void modelTask.then((info) => {
state.providers = info.providers
state.variants = variantsFor(state.providers, state.model)
state.limits = info.limits
const next = resolveVariant(ctx.variant, session.variant, savedVariant, state.variants)
if (next !== state.activeVariant) {
state.activeVariant = next
}
if (footer.isClosed) {
return
}
footer.event({ type: "models", providers: info.providers })
footer.event({ type: "variants", variants: state.variants, current: state.activeVariant })
if (!state.model) {
return
}
footer.event({
type: "model",
model: formatModelLabel(state.model, state.activeVariant, state.providers),
})
})
const streamTask = deps.streamTransport ?? import("./stream.transport")
const ensureStream = () => {
if (state.stream) {
return state.stream
}
// Share eager prewarm and first-turn boot through one in-flight promise,
// but clear it if transport creation fails so a later prompt can retry.
const next = (async () => {
await ensureSession()
if (footer.isClosed) {
throw new Error("runtime closed")
}
let resizeTimer: ReturnType<typeof setTimeout> | undefined
const offResize = shell.onResize(() => {
if (resizeTimer) {
clearTimeout(resizeTimer)
}
const mod = await streamTask
if (footer.isClosed) {
throw new Error("runtime closed")
}
resizeTimer = setTimeout(() => {
resizeTimer = undefined
if (footer.isClosed) {
return
}
const handle = await mod.createSessionTransport({
sdk: ctx.sdk,
directory: ctx.directory,
sessionID: state.sessionID,
thinking: input.thinking,
replay: input.replay,
replayLimit: input.replayLimit,
limits: () => state.limits,
providers: () => state.providers,
footer,
trace: log,
})
if (footer.isClosed) {
await handle.close()
throw new Error("runtime closed")
}
shell.refreshTheme()
if (!input.replay || !state.stream) {
return
}
state.selectSubagent = (sessionID) => handle.selectSubagent(sessionID)
return { mod, handle }
})()
state.stream = next
void next.catch(() => {
if (state.stream === next) {
state.stream = undefined
}
})
return next
}
void state.stream
.then((item) =>
item.handle.replayOnResize({
localRows: () => state.localRows,
reset: () =>
shell.resetForReplay({
sessionTitle: state.sessionTitle,
sessionID: state.sessionID,
history: state.history,
}),
let resizeTimer: ReturnType<typeof setTimeout> | undefined
const offResize = shell.onResize(() => {
if (resizeTimer) {
clearTimeout(resizeTimer)
}
resizeTimer = setTimeout(() => {
resizeTimer = undefined
if (footer.isClosed) {
return
}
shell.refreshTheme()
if (!input.replay || !state.stream) {
return
}
void state.stream
.then((item) =>
item.handle.replayOnResize({
localRows: () => state.localRows,
reset: () =>
shell.resetForReplay({
sessionTitle: state.sessionTitle,
sessionID: state.sessionID,
history: state.history,
}),
)
.catch(() => {})
}, RESIZE_DELAY)
})
}),
)
.catch(() => {})
}, RESIZE_DELAY)
})
const runQueue = async () => {
let includeFiles = true
if (state.demo) {
await state.demo.start()
const runQueue = async () => {
let includeFiles = true
if (state.demo) {
await state.demo.start()
}
const mod = await import("./runtime.queue")
const createSession = input.createSession
await mod.runPromptQueue({
footer,
initialInput: input.initialInput,
trace: log,
onSend: (prompt) => {
state.shown = true
state.history.push(prompt)
if (prompt.mode !== "shell") {
rememberLocal({
kind: "user",
text: prompt.text,
phase: "start",
source: "system",
messageID: prompt.messageID,
})
}
const mod = await import("./runtime.queue")
const createSession = input.createSession
await mod.runPromptQueue({
footer,
initialInput: input.initialInput,
trace: log,
onSend: (prompt) => {
state.shown = true
state.history.push(prompt)
if (prompt.mode !== "shell") {
rememberLocal({
kind: "user",
text: prompt.text,
},
onNewSession: createSession
? async () => {
try {
await state.switching?.catch(() => {})
const created = await createSession(ctx, {
agent: state.agent,
model: state.model,
variant: state.activeVariant,
})
await footer.idle().catch(() => {})
await state.stream?.then((item) => item.handle.close()).catch(() => {})
state.stream = undefined
state.session = undefined
state.selectSubagent = undefined
state.shown = false
state.sessionID = created.sessionID
state.sessionTitle = created.sessionTitle
state.agent = created.agent ?? state.agent
state.history = []
state.localRows = []
includeFiles = true
state.demo = input.demo
? createRunDemo({
footer,
sessionID: state.sessionID,
thinking: input.thinking,
limits: () => state.limits,
})
: undefined
log?.write("session.new", {
sessionID: state.sessionID,
})
footer.event({
type: "stream.subagent",
state: {
tabs: [],
details: {},
permissions: [],
questions: [],
},
})
footer.event({ type: "stream.view", view: { type: "prompt" } })
footer.event({
type: "stream.patch",
patch: {
phase: "idle",
duration: "",
usage: "",
first: true,
},
})
footer.append({
kind: "system",
text: `new session ${state.sessionID}`,
phase: "final",
source: "system",
})
await state.demo?.start()
} catch (error) {
footer.event({
type: "stream.patch",
patch: {
phase: "idle",
status: "failed to start new session",
},
})
const commit = {
kind: "error",
text: error instanceof Error ? error.message : String(error),
phase: "start",
source: "system",
messageID: prompt.messageID,
})
messageID: MessageID.ascending(),
} as const
rememberLocal(commit)
footer.append(commit)
}
},
onNewSession: createSession
? async () => {
try {
await state.switching?.catch(() => {})
const created = await createSession(ctx, {
agent: state.agent,
model: state.model,
variant: state.activeVariant,
})
await footer.idle().catch(() => {})
await state.stream?.then((item) => item.handle.close()).catch(() => {})
state.stream = undefined
state.session = undefined
state.selectSubagent = undefined
state.shown = false
state.sessionID = created.sessionID
state.sessionTitle = created.sessionTitle
state.agent = created.agent ?? state.agent
state.history = []
state.localRows = []
includeFiles = true
state.demo = input.demo
? createRunDemo({
footer,
sessionID: state.sessionID,
thinking: input.thinking,
limits: () => state.limits,
})
: undefined
log?.write("session.new", {
sessionID: state.sessionID,
})
footer.event({
type: "stream.subagent",
state: {
tabs: [],
details: {},
permissions: [],
questions: [],
},
})
footer.event({ type: "stream.view", view: { type: "prompt" } })
footer.event({
type: "stream.patch",
patch: {
phase: "idle",
duration: "",
usage: "",
first: true,
},
})
footer.append({
kind: "system",
text: `new session ${state.sessionID}`,
phase: "final",
source: "system",
})
await state.demo?.start()
} catch (error) {
footer.event({
type: "stream.patch",
patch: {
phase: "idle",
status: "failed to start new session",
},
})
const commit = {
kind: "error",
text: error instanceof Error ? error.message : String(error),
phase: "start",
source: "system",
messageID: MessageID.ascending(),
} as const
rememberLocal(commit)
footer.append(commit)
}
}
: undefined,
run: async (prompt, signal) => {
if (state.demo && (await state.demo.prompt(prompt, signal))) {
return
}
await state.switching?.catch(() => {})
let outputAnchor: LocalReplayAnchor | undefined
try {
const next = await ensureStream()
await next.handle.runPromptTurn({
agent: state.agent,
model: state.model,
variant: state.activeVariant,
prompt,
files: input.files,
includeFiles,
onVisibleOutput: (anchor) => {
outputAnchor = anchor
},
signal,
})
if (prompt.messageID) {
state.localRows = state.localRows.filter(
(row) => row.commit.kind !== "user" || row.commit.messageID !== prompt.messageID,
)
}
includeFiles = false
} catch (error) {
if (signal.aborted || footer.isClosed) {
return
}
const text =
(await state.stream?.then((item) => item.mod).catch(() => undefined))?.formatUnknownError(error) ??
(error instanceof Error ? error.message : String(error))
const commit = {
kind: "error",
text,
phase: "start",
source: "system",
messageID: prompt.messageID,
} as const
rememberLocal(commit, outputAnchor)
footer.append(commit)
}
},
})
}
try {
const eager = eagerStream(input, ctx)
if (eager) {
if (input.replay && state.shown) {
// Replay commits immutable scrollback rows, so wait for provider names
// before bootstrapping existing session history.
await modelTask
}
await ensureStream()
: undefined,
run: async (prompt, signal) => {
if (state.demo && (await state.demo.prompt(prompt, signal))) {
return
}
if (!eager && input.resolveSession) {
queueMicrotask(() => {
if (footer.isClosed) {
return
}
void ensureStream().catch(() => {})
})
}
await state.switching?.catch(() => {})
let outputAnchor: LocalReplayAnchor | undefined
try {
await runQueue()
} finally {
if (resizeTimer) {
clearTimeout(resizeTimer)
const next = await ensureStream()
await next.handle.runPromptTurn({
agent: state.agent,
model: state.model,
variant: state.activeVariant,
prompt,
files: input.files,
includeFiles,
onVisibleOutput: (anchor) => {
outputAnchor = anchor
},
signal,
})
if (prompt.messageID) {
state.localRows = state.localRows.filter(
(row) => row.commit.kind !== "user" || row.commit.messageID !== prompt.messageID,
)
}
includeFiles = false
} catch (error) {
if (signal.aborted || footer.isClosed) {
return
}
offResize()
await state.stream?.then((item) => item.handle.close()).catch(() => {})
}
} finally {
const title = await resolveExitTitle(ctx, input, state)
await shell.close({
showExit: state.shown && hasSession(input, state),
sessionTitle: title,
sessionID: state.sessionID,
history: state.history,
})
const text =
(await state.stream?.then((item) => item.mod).catch(() => undefined))?.formatUnknownError(error) ??
(error instanceof Error ? error.message : String(error))
const commit = {
kind: "error",
text,
phase: "start",
source: "system",
messageID: prompt.messageID,
} as const
rememberLocal(commit, outputAnchor)
footer.append(commit)
}
},
})
}
try {
const eager = eagerStream(input, ctx)
if (eager) {
if (input.replay && state.shown) {
// Replay commits immutable scrollback rows, so wait for provider names
// before bootstrapping existing session history.
await modelTask
}
await ensureStream()
}
if (!eager && input.resolveSession) {
queueMicrotask(() => {
if (footer.isClosed) {
return
}
void ensureStream().catch(() => {})
})
}
try {
await runQueue()
} finally {
if (resizeTimer) {
clearTimeout(resizeTimer)
}
offResize()
await state.stream?.then((item) => item.handle.close()).catch(() => {})
}
} finally {
const title = await resolveExitTitle(ctx, input, state)
await shell.close({
showExit: state.shown && hasSession(input, state),
sessionTitle: title,
sessionID: state.sessionID,
history: state.history,
})
}
}
// Local in-process mode. Creates an SDK client backed by a direct fetch to
// the in-process server, so no external HTTP server is needed.
export async function runInteractiveLocalMode(input: RunLocalInput): Promise<void> {
const sdk = createOpencodeClient({
baseUrl: "http://opencode.internal",
fetch: input.fetch,
const sdk = createOpencodeClient({
baseUrl: "http://opencode.internal",
fetch: input.fetch,
directory: input.directory,
})
let session: Promise<ResolvedSession> | undefined
return runInteractiveRuntime({
files: input.files,
initialInput: input.initialInput,
thinking: input.thinking,
backgroundSubagents: input.backgroundSubagents,
replay: input.replay,
replayLimit: input.replayLimit,
demo: input.demo,
resolveSession: () => {
if (session) {
return session
}
session = Promise.all([input.resolveAgent(), input.session(sdk)]).then(([agent, next]) => {
if (!next?.id) {
throw new Error("Session not found")
}
void input.share(sdk, next.id).catch(() => {})
return {
sessionID: next.id,
sessionTitle: next.title,
agent,
}
})
return session
},
createSession: createSessionResolver(input.createSession),
boot: async () => {
return {
sdk,
directory: input.directory,
})
let session: Promise<ResolvedSession> | undefined
return runInteractiveRuntime({
files: input.files,
initialInput: input.initialInput,
thinking: input.thinking,
backgroundSubagents: input.backgroundSubagents,
replay: input.replay,
replayLimit: input.replayLimit,
demo: input.demo,
resolveSession: () => {
if (session) {
return session
}
session = Promise.all([input.resolveAgent(), input.session(sdk)]).then(([agent, next]) => {
if (!next?.id) {
throw new Error("Session not found")
}
void input.share(sdk, next.id).catch(() => {})
return {
sessionID: next.id,
sessionTitle: next.title,
agent,
}
})
return session
},
createSession: createSessionResolver(input.createSession),
boot: async () => {
return {
sdk,
directory: input.directory,
sessionID: "",
sessionTitle: undefined,
resume: false,
agent: input.agent,
model: input.model,
variant: input.variant,
}
},
})
sessionID: "",
sessionTitle: undefined,
resume: false,
agent: input.agent,
model: input.model,
variant: input.variant,
}
},
})
}
// Attach mode. Uses the caller-provided SDK client directly.
@@ -789,26 +789,26 @@ export async function runInteractiveMode(
deps?: RunRuntimeDeps,
): Promise<void> {
return runInteractiveRuntime(
{
files: input.files,
initialInput: input.initialInput,
thinking: input.thinking,
backgroundSubagents: input.backgroundSubagents,
replay: input.replay,
replayLimit: input.replayLimit,
demo: input.demo,
boot: async () => ({
sdk: input.sdk,
directory: input.directory,
sessionID: input.sessionID,
sessionTitle: input.sessionTitle,
resume: input.resume,
agent: input.agent,
model: input.model,
variant: input.variant,
}),
createSession: createSessionResolver(input.createSession),
},
deps,
{
files: input.files,
initialInput: input.initialInput,
thinking: input.thinking,
backgroundSubagents: input.backgroundSubagents,
replay: input.replay,
replayLimit: input.replayLimit,
demo: input.demo,
boot: async () => ({
sdk: input.sdk,
directory: input.directory,
sessionID: input.sessionID,
sessionTitle: input.sessionTitle,
resume: input.resume,
agent: input.agent,
model: input.model,
variant: input.variant,
}),
createSession: createSessionResolver(input.createSession),
},
deps,
)
}
@@ -73,7 +73,6 @@ function fromRow(row: typeof WorkspaceTable.$inferSelect): Info {
}
}
export const CreateInput = Schema.Struct({
id: Schema.optional(WorkspaceV2.ID),
type: Info.fields.type,
@@ -341,7 +340,6 @@ export const layer = Layer.effect(
)
: {}
const response = yield* http.execute(
HttpClientRequest.post(route(url, "/sync/history"), {
headers: new Headers(headers),
@@ -360,7 +358,6 @@ export const layer = Layer.effect(
const history = (yield* response.json) as HistoryEvent[]
yield* Effect.forEach(
history,
(event) =>
@@ -575,7 +572,6 @@ export const layer = Layer.effect(
const sessionWarp = Effect.fn("Workspace.sessionWarp")(function* (input: SessionWarpInput) {
return yield* Effect.gen(function* () {
const current = yield* db
.select({ workspaceID: SessionTable.workspace_id })
.from(SessionTable)
@@ -590,10 +586,7 @@ export const layer = Layer.effect(
if (target.type === "remote") {
yield* syncHistory(previous, target.url, target.headers).pipe(
Effect.catch((error) =>
Effect.sync(() => {
}),
),
Effect.catch((error) => Effect.sync(() => {})),
)
} else {
yield* prompt.cancel(input.sessionID)
@@ -679,7 +672,6 @@ export const layer = Layer.effect(
const batches = Iterable.chunksOf(rows, 10)
const total = Iterable.size(batches)
yield* Effect.forEach(
batches,
(events, i) =>
@@ -704,7 +696,6 @@ export const layer = Layer.effect(
body,
})
}
}),
{ discard: true },
)
@@ -727,7 +718,6 @@ export const layer = Layer.effect(
}
yield* session.setWorkspace({ sessionID: input.sessionID, workspaceID: input.workspaceID })
})
})
@@ -827,9 +817,7 @@ export const layer = Layer.effect(
Effect.gen(function* () {
yield* WorkspaceAdapterRuntime.remove(info)
}),
() =>
Effect.sync(() => {
}),
() => Effect.sync(() => {}),
)
yield* db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run().pipe(Effect.orDie)
+6 -6
View File
@@ -94,12 +94,12 @@ export const layer = Layer.effect(
.pipe(
Effect.catch((error) =>
Effect.logError("failed to format file", {
error: "spawn failed",
command: cmd,
...item.environment,
file: filepath,
cause: errorMessage(error.cause ?? error),
}).pipe(Effect.as(undefined)),
error: "spawn failed",
command: cmd,
...item.environment,
file: filepath,
cause: errorMessage(error.cause ?? error),
}).pipe(Effect.as(undefined)),
),
)
if (result && result.exitCode !== 0) {
-1
View File
@@ -75,7 +75,6 @@ const cli = yargs(args)
process.env.AGENT = "1"
process.env.OPENCODE = "1"
process.env.OPENCODE_PID = String(process.pid)
})
.usage("")
.completion("completion", "generate shell completion script")
-9
View File
@@ -206,7 +206,6 @@ export const ESLint: Info = {
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm"
await Process.run([npmCmd, "install"], { cwd: finalPath })
await Process.run([npmCmd, "run", "compile"], { cwd: finalPath })
}
const proc = spawn("node", [serverPath, "--stdio"], {
@@ -572,7 +571,6 @@ export const ElixirLS: Info = {
await Process.run(["mix", "deps.get"], { cwd, env })
await Process.run(["mix", "compile"], { cwd, env })
await Process.run(["mix", "elixir_ls.release2", "-o", "release"], { cwd, env })
}
}
@@ -676,7 +674,6 @@ export const Zls: Info = {
if (platform !== "win32") {
await fs.chmod(bin, 0o755).catch(() => {})
}
}
return {
@@ -775,7 +772,6 @@ async function installRoslynLanguageServer(disableLspDownload: boolean) {
if (global) {
return global
}
}
async function roslynLanguageServerGlobalPath() {
@@ -1062,7 +1058,6 @@ export const Clangd: Info = {
await fs.unlink(path.join(Global.Path.bin, "clangd")).catch(() => {})
await fs.symlink(bin, path.join(Global.Path.bin, "clangd")).catch(() => {})
return {
process: spawn(bin, args, {
cwd: root,
@@ -1507,7 +1502,6 @@ export const LuaLS: Info = {
})
if (!ok) return
}
}
return {
@@ -1682,7 +1676,6 @@ export const TerraformLS: Info = {
if (platform !== "win32") {
await fs.chmod(bin, 0o755).catch(() => {})
}
}
return {
@@ -1768,7 +1761,6 @@ export const TexLab: Info = {
if (platform !== "win32") {
await fs.chmod(bin, 0o755).catch(() => {})
}
}
return {
@@ -1948,7 +1940,6 @@ export const Tinymist: Info = {
if (platform !== "win32") {
await fs.chmod(bin, 0o755).catch(() => {})
}
}
return {
-2
View File
@@ -439,7 +439,6 @@ export const layer = Layer.effect(
return DISABLED_RESULT
}
const { client: mcpClient, status } =
mcp.type === "remote"
? yield* connectRemote(key, mcp as ConfigMCPV1.Info & { type: "remote" })
@@ -844,7 +843,6 @@ export const layer = Layer.effect(
return yield* storeClient(s, mcpName, client, listed, mcpConfig.timeout)
}
const callbackPromise = McpOAuthCallback.waitForCallback(result.oauthState, mcpName)
yield* Effect.tryPromise(() => open(result.authorizationUrl)).pipe(
@@ -2,7 +2,6 @@ import { createConnection } from "net"
import { createServer } from "http"
import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider"
// Current callback server configuration (may differ from defaults if custom redirectUri is used)
let currentPort = OAUTH_CALLBACK_PORT
let currentPath = OAUTH_CALLBACK_PATH
@@ -85,7 +84,6 @@ function handleRequest(req: import("http").IncomingMessage, res: import("http").
const error = url.searchParams.get("error")
const errorDescription = url.searchParams.get("error_description")
// Enforce state parameter presence
if (!state) {
const errorMsg = "Missing required state parameter - potential CSRF attack"
@@ -8,7 +8,6 @@ import type {
import { Effect } from "effect"
import { McpAuth } from "./auth"
const OAUTH_CALLBACK_PORT = 19876
const OAUTH_CALLBACK_PATH = "/mcp/oauth/callback"
@@ -4,7 +4,6 @@ import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { createServer } from "http"
import open from "open"
const DO_OAUTH_CLIENT_ID = "b1a6c5158156caac821fd1b30253ca8acb52454a48fa744420e41889cb589f82"
const DO_AUTHORIZE_URL = "https://cloud.digitalocean.com/v1/oauth/authorize"
const DO_API_BASE = "https://api.digitalocean.com"
@@ -6,7 +6,6 @@ import { setTimeout as sleep } from "node:timers/promises"
import { CopilotModels } from "./models"
import { MessageV2 } from "@/session/message-v2"
const CLIENT_ID = "Ov23li8tweQw6odWQebz"
const API_VERSION = "2026-06-01"
const UTILITY_MODELS = ["gpt-5.4-nano", "gpt-4.1", "gpt-4o", "gpt-4o-mini"]
+4 -9
View File
@@ -30,7 +30,6 @@ import { RuntimeFlags } from "@/effect/runtime-flags"
import { EventV2Bridge } from "@/event-v2-bridge"
import { InstallationChannel } from "@opencode-ai/core/installation/version"
type State = {
hooks: Hooks[]
}
@@ -163,8 +162,7 @@ export const layer = Layer.effect(
for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {
const init = yield* Effect.tryPromise({
try: () => plugin(input),
catch: (err) => {
},
catch: (err) => {},
}).pipe(Effect.option)
if (init._tag === "Some") hooks.push(init.value)
}
@@ -179,10 +177,8 @@ export const layer = Layer.effect(
items: plugins,
kind: "server",
report: {
start(candidate) {
},
missing(candidate, _retry, message) {
},
start(candidate) {},
missing(candidate, _retry, message) {},
error(candidate, _retry, stage, error, resolved) {
const spec = candidate.plan.spec
const cause = error instanceof Error ? (error.cause ?? error) : error
@@ -260,8 +256,7 @@ export const layer = Layer.effect(
(hook) =>
Effect.tryPromise({
try: () => Promise.resolve(hook.dispose?.()),
catch: (error) => {
},
catch: (error) => {},
}).pipe(Effect.ignore),
{ discard: true },
),
+1 -3
View File
@@ -6,7 +6,6 @@ import { setTimeout as sleep } from "node:timers/promises"
import { createServer } from "http"
import { OpenAIWebSocketPool } from "./ws-pool"
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
const ISSUER = "https://auth.openai.com"
const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
@@ -314,8 +313,7 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
function stopOAuthServer() {
if (oauthServer) {
oauthServer.close(() => {
})
oauthServer.close(() => {})
oauthServer = undefined
}
}
@@ -5,7 +5,6 @@ import { OpenAIWebSocket } from "./ws"
export const TITLE_HEADER = "x-opencode-title"
export interface CreateWebSocketFetchOptions {
httpFetch?: typeof globalThis.fetch
url?: string
-1
View File
@@ -3,7 +3,6 @@ import { OAUTH_DUMMY_KEY } from "../auth"
import { createServer } from "http"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
// Public Grok-CLI OAuth client. xAI's auth server rejects loopback OAuth from
// non-allowlisted clients, so we reuse the Grok-CLI client_id that xAI ships
// for desktop OAuth flows. Source of truth: hermes-agent PR #26534.
+1 -4
View File
@@ -1306,7 +1306,6 @@ export const layer = Layer.effect(
get: (key: string) => env.get(key),
}
function mergeProvider(providerID: ProviderV2.ID, provider: Partial<Info>) {
const existing = providers[providerID]
if (existing) {
@@ -1540,8 +1539,7 @@ export const layer = Layer.effect(
providers[gitlab].models[modelID] = model
}
}
} catch (e) {
}
} catch (e) {}
})
}
@@ -1592,7 +1590,6 @@ export const layer = Layer.effect(
delete providers[providerID]
continue
}
}
return {
+7 -7
View File
@@ -138,9 +138,7 @@ export const layer = Layer.effect(
s.queue.set(sessionID, next)
yield* flush(sessionID).pipe(
Effect.delay(1000),
Effect.catchCause((cause) =>
Effect.logError("share flush failed", { sessionID: sessionID, cause: cause }),
),
Effect.catchCause((cause) => Effect.logError("share flush failed", { sessionID: sessionID, cause: cause })),
Effect.forkIn(s.scope),
)
})
@@ -263,7 +261,11 @@ export const layer = Layer.effect(
)
if (res.status >= 400) {
yield* Effect.logWarning("failed to sync share", { sessionID: sessionID, shareID: share.id, status: res.status })
yield* Effect.logWarning("failed to sync share", {
sessionID: sessionID,
shareID: share.id,
status: res.status,
})
}
})
@@ -325,9 +327,7 @@ export const layer = Layer.effect(
const s = yield* InstanceState.get(state)
s.shared.set(sessionID, result)
yield* full(sessionID).pipe(
Effect.catchCause((cause) =>
Effect.logError("share full sync failed", { sessionID: sessionID, cause: cause }),
),
Effect.catchCause((cause) => Effect.logError("share full sync failed", { sessionID: sessionID, cause: cause })),
Effect.forkIn(s.scope),
)
return result
+2 -5
View File
@@ -39,9 +39,7 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | Htt
Effect.flatMap((res) => res.arrayBuffer),
Effect.flatMap((body) => fs.writeWithDirs(dest, new Uint8Array(body))),
Effect.as(true),
Effect.catch((err) =>
Effect.logError("failed to download", { url: url, error: err }).pipe(Effect.as(false)),
),
Effect.catch((err) => Effect.logError("failed to download", { url: url, error: err }).pipe(Effect.as(false))),
)
})
@@ -66,8 +64,7 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | Htt
const missing = data.skills.filter((skill) => !skill.files.includes("SKILL.md"))
yield* Effect.forEach(
missing,
(skill) =>
Effect.logWarning("skill entry missing SKILL.md", { url: index, skill: skill.name }),
(skill) => Effect.logWarning("skill entry missing SKILL.md", { url: index, skill: skill.name }),
{ discard: true },
)
const list = data.skills.filter((skill) => skill.files.includes("SKILL.md"))
+8 -2
View File
@@ -122,7 +122,11 @@ const add = Effect.fnUntraced(function* (state: State, match: string, events: Ev
if (!isSkillFrontmatter(md.data)) return
if (state.skills[md.data.name]) {
yield* Effect.logWarning("duplicate skill name", { name: md.data.name, existing: state.skills[md.data.name].location, duplicate: match })
yield* Effect.logWarning("duplicate skill name", {
name: md.data.name,
existing: state.skills[md.data.name].location,
duplicate: match,
})
}
state.dirs.add(path.dirname(match))
@@ -153,7 +157,9 @@ const scan = Effect.fnUntraced(function* (
}).pipe(
Effect.catch((error) => {
if (!opts?.scope) return Effect.die(error)
return Effect.logError(`failed to scan ${opts.scope} skills`, { dir: root, error: error }).pipe(Effect.as([] as string[]))
return Effect.logError(`failed to scan ${opts.scope} skills`, { dir: root, error: error }).pipe(
Effect.as([] as string[]),
)
}),
)
+2 -2
View File
@@ -564,8 +564,8 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | AppProcess.Serv
yield* Effect.logInfo(
"git cat-file --batch failed during snapshot diff, falling back to per-file git show",
{
stderr: batch.stderr.toString("utf8"),
refs: refs.length,
stderr: batch.stderr.toString("utf8"),
refs: refs.length,
},
)
return