diff --git a/packages/opencode/src/cli/cmd/run/footer.prompt.tsx b/packages/opencode/src/cli/cmd/run/footer.prompt.tsx index 5658d8d34d..0caae36d0e 100644 --- a/packages/opencode/src/cli/cmd/run/footer.prompt.tsx +++ b/packages/opencode/src/cli/cmd/run/footer.prompt.tsx @@ -283,9 +283,7 @@ export function createPromptState(input: PromptInput): PromptState { const [shell, setShell] = createSignal(false) const placeholder = createMemo(() => { if (shell()) { - return new StyledText([ - bg(input.theme().surface)(fg(input.theme().muted)('Run a command... "git status"')), - ]) + return new StyledText([bg(input.theme().surface)(fg(input.theme().muted)('Run a command... "git status"'))]) } if (!input.state().first) { @@ -1087,7 +1085,8 @@ export function createPromptState(input: PromptInput): PromptState { return } - const parsed = next.mode === "shell" || isNewCommand(next.text) ? undefined : parseSlashCommand(next.text, input.commands()) + const parsed = + next.mode === "shell" || isNewCommand(next.text) ? undefined : parseSlashCommand(next.text, input.commands()) if (parsed?.type === "pending") { input.onStatus("loading commands") return diff --git a/packages/opencode/src/cli/cmd/run/stream.transport.ts b/packages/opencode/src/cli/cmd/run/stream.transport.ts index 780032c1db..2977d03f69 100644 --- a/packages/opencode/src/cli/cmd/run/stream.transport.ts +++ b/packages/opencode/src/cli/cmd/run/stream.transport.ts @@ -515,14 +515,19 @@ function createLayer(input: StreamInput) { state.footerView = current } - const resolveShellAgent = Effect.fn("RunStreamTransport.resolveShellAgent")(function* (agent: string | undefined) { + const resolveShellAgent = Effect.fn("RunStreamTransport.resolveShellAgent")(function* ( + agent: string | undefined, + ) { if (agent) { return agent } const list = yield* Effect.promise(() => input.sdk.app.agents(input.directory ? { directory: input.directory } : undefined, { throwOnError: true }), - ).pipe(Effect.map((item) => item.data ?? []), Effect.orElseSucceed(() => [])) + ).pipe( + Effect.map((item) => item.data ?? []), + Effect.orElseSucceed(() => []), + ) const next = list.find((item) => item.mode !== "subagent" && item.hidden !== true)?.name if (next) { return next @@ -1023,105 +1028,108 @@ function createLayer(input: StreamInput) { ], } const command = next.prompt.command - const send = next.prompt.mode === "shell" - ? Effect.sync(() => { - input.trace?.write("send.shell", { - sessionID: input.sessionID, - command: next.prompt.text, - }) - }).pipe( - Effect.andThen( - resolveShellAgent(next.agent).pipe( - Effect.flatMap((agent) => + const send = + next.prompt.mode === "shell" + ? Effect.sync(() => { + input.trace?.write("send.shell", { + sessionID: input.sessionID, + command: next.prompt.text, + }) + }).pipe( + Effect.andThen( + resolveShellAgent(next.agent) + .pipe( + Effect.flatMap((agent) => + Effect.promise(() => + input.sdk.session.shell( + { + sessionID: input.sessionID, + agent, + model: next.model, + command: next.prompt.text, + }, + { signal: turn.signal, throwOnError: true }, + ), + ), + ), + ) + .pipe( + Effect.tap(() => + Effect.sync(() => { + input.trace?.write("send.shell.ok", { + sessionID: input.sessionID, + }) + item.armed = true + item.live = true + }), + ), + Effect.flatMap(() => Deferred.succeed(item.done, undefined).pipe(Effect.ignore)), + Effect.catch((error) => Deferred.fail(item.done, error).pipe(Effect.ignore)), + Effect.forkIn(scope, { startImmediately: true }), + Effect.asVoid, + ), + ), + ) + : command + ? Effect.sync(() => { + input.trace?.write("send.command", { sessionID: input.sessionID, command: command.name }) + }).pipe( + Effect.andThen( Effect.promise(() => - input.sdk.session.shell( + input.sdk.session.command( { sessionID: input.sessionID, - agent, - model: next.model, - command: next.prompt.text, + agent: next.agent, + model: next.model ? `${next.model.providerID}/${next.model.modelID}` : undefined, + variant: next.variant, + command: command.name, + arguments: command.arguments, + parts: [ + ...(next.includeFiles ? next.files : []), + ...next.prompt.parts.filter( + (item): item is Extract => item.type === "file", + ), + ], }, - { signal: turn.signal, throwOnError: true }, + { signal: turn.signal }, ), + ).pipe( + Effect.tap(() => + Effect.sync(() => { + input.trace?.write("send.command.ok", { + sessionID: input.sessionID, + command: command.name, + }) + item.armed = true + item.live = true + }), + ), + Effect.flatMap(() => Deferred.succeed(item.done, undefined).pipe(Effect.ignore)), + Effect.catch((error) => Deferred.fail(item.done, error).pipe(Effect.ignore)), + Effect.forkIn(scope, { startImmediately: true }), + Effect.asVoid, + ), + ), + ) + : Effect.sync(() => { + input.trace?.write("send.prompt", req) + }).pipe( + Effect.andThen( + Effect.promise(() => + input.sdk.session.promptAsync(req, { + signal: turn.signal, + }), ), ), - ).pipe( Effect.tap(() => Effect.sync(() => { - input.trace?.write("send.shell.ok", { + input.trace?.write("send.prompt.ok", { sessionID: input.sessionID, }) item.armed = true - item.live = true }), ), - Effect.flatMap(() => Deferred.succeed(item.done, undefined).pipe(Effect.ignore)), - Effect.catch((error) => Deferred.fail(item.done, error).pipe(Effect.ignore)), - Effect.forkIn(scope, { startImmediately: true }), - Effect.asVoid, - ), - ), - ) - : command - ? Effect.sync(() => { - input.trace?.write("send.command", { sessionID: input.sessionID, command: command.name }) - }).pipe( - Effect.andThen( - Effect.promise(() => - input.sdk.session.command( - { - sessionID: input.sessionID, - agent: next.agent, - model: next.model ? `${next.model.providerID}/${next.model.modelID}` : undefined, - variant: next.variant, - command: command.name, - arguments: command.arguments, - parts: [ - ...(next.includeFiles ? next.files : []), - ...next.prompt.parts.filter( - (item): item is Extract => item.type === "file", - ), - ], - }, - { signal: turn.signal }, - ), - ).pipe( - Effect.tap(() => - Effect.sync(() => { - input.trace?.write("send.command.ok", { - sessionID: input.sessionID, - command: command.name, - }) - item.armed = true - item.live = true - }), - ), - Effect.flatMap(() => Deferred.succeed(item.done, undefined).pipe(Effect.ignore)), - Effect.catch((error) => Deferred.fail(item.done, error).pipe(Effect.ignore)), - Effect.forkIn(scope, { startImmediately: true }), - Effect.asVoid, - ), - ), - ) - : Effect.sync(() => { - input.trace?.write("send.prompt", req) - }).pipe( - Effect.andThen( - Effect.promise(() => - input.sdk.session.promptAsync(req, { - signal: turn.signal, - }), - ), - ), - Effect.tap(() => - Effect.sync(() => { - input.trace?.write("send.prompt.ok", { - sessionID: input.sessionID, - }) - item.armed = true - }), - ), - ) + ) yield* send.pipe( Effect.flatMap(() => {