mirror of
https://fastgit.cc/https://github.com/anomalyco/opencode
synced 2026-04-21 21:31:53 +08:00
whitespace + imports
This commit is contained in:
@@ -16,13 +16,13 @@ import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import { UI } from "../ui"
|
||||
import { cmd } from "./cmd"
|
||||
import { Flag } from "../../flag/flag"
|
||||
import { Flag } from "@/flag/flag"
|
||||
import { bootstrap } from "../bootstrap"
|
||||
import { EOL } from "os"
|
||||
import { Filesystem } from "../../util"
|
||||
import { Filesystem } from "@/util"
|
||||
import { createOpencodeClient, type OpencodeClient, type ToolPart } from "@opencode-ai/sdk/v2"
|
||||
import { Agent } from "../../agent/agent"
|
||||
import { Permission } from "../../permission"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { Permission } from "@/permission"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
import type { RunDemo } from "./run/types"
|
||||
|
||||
@@ -715,7 +715,7 @@ export const RunCommand = cmd({
|
||||
const model = pick(args.model)
|
||||
const { runInteractiveLocalMode } = await runtimeTask
|
||||
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const { Server } = await import("../../server/server")
|
||||
const { Server } = await import("@/server/server")
|
||||
const request = new Request(input, init)
|
||||
return Server.Default().app.fetch(request)
|
||||
}) as typeof globalThis.fetch
|
||||
@@ -744,7 +744,7 @@ export const RunCommand = cmd({
|
||||
|
||||
await bootstrap(directory ?? root, async () => {
|
||||
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const { Server } = await import("../../server/server")
|
||||
const { Server } = await import("@/server/server")
|
||||
const request = new Request(input, init)
|
||||
return Server.Default().app.fetch(request)
|
||||
}) as typeof globalThis.fetch
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
onMount,
|
||||
type Accessor,
|
||||
} from "solid-js"
|
||||
import * as Locale from "../../../util/locale"
|
||||
import * as Locale from "@/util/locale"
|
||||
import {
|
||||
createPromptHistory,
|
||||
isExitCommand,
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// The leader-key cycle (promptCycle) uses a two-step pattern: first press
|
||||
// arms the leader, second press within the timeout fires the action.
|
||||
import type { KeyBinding } from "@opentui/core"
|
||||
import * as Keybind from "../../../util/keybind"
|
||||
import * as Keybind from "@/util/keybind"
|
||||
import type { FooterKeybinds, RunPrompt } from "./types"
|
||||
|
||||
const HISTORY_LIMIT = 200
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// Also wires SIGINT so Ctrl-c during a turn triggers the two-press exit
|
||||
// sequence through RunFooter.requestExit().
|
||||
import { createCliRenderer, type CliRenderer, type ScrollbackWriter } from "@opentui/core"
|
||||
import * as Locale from "../../../util/locale"
|
||||
import * as Locale from "@/util/locale"
|
||||
import { withRunSpan } from "./otel"
|
||||
import { entrySplash, exitSplash, splashMeta } from "./splash"
|
||||
import { resolveRunTheme } from "./theme"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// and tracks per-turn wall-clock duration for the footer status line.
|
||||
//
|
||||
// Resolves when the footer closes and all in-flight work finishes.
|
||||
import * as Locale from "../../../util/locale"
|
||||
import * as Locale from "@/util/locale"
|
||||
import { isExitCommand } from "./prompt.shared"
|
||||
import type { FooterApi, FooterEvent, RunPrompt } from "./types"
|
||||
|
||||
|
||||
@@ -51,10 +51,9 @@ function commitMarkdownBlocks(input: {
|
||||
return false
|
||||
}
|
||||
|
||||
const prev = input.renderable._blockStates[input.startBlock - 1]
|
||||
const next = input.renderable._blockStates[input.endBlockExclusive]
|
||||
const start = Math.max(0, first.renderable.y - (prev?.marginBottom ?? 0))
|
||||
const end = last.renderable.y + last.renderable.height + (next ? 0 : (last.marginBottom ?? 0))
|
||||
const start = first.renderable.y
|
||||
const end = next ? next.renderable.y : last.renderable.y + last.renderable.height + (last.marginBottom ?? 0)
|
||||
|
||||
input.surface.commitRows(start, end, {
|
||||
trailingNewline: input.trailingNewline,
|
||||
@@ -62,6 +61,18 @@ function commitMarkdownBlocks(input: {
|
||||
return true
|
||||
}
|
||||
|
||||
function wantsSpacer(prev: StreamCommit | undefined, next: StreamCommit): boolean {
|
||||
if (!prev) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (sameEntryGroup(prev, next)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !(prev.kind === "tool" && prev.phase === "start")
|
||||
}
|
||||
|
||||
export class RunScrollbackStream {
|
||||
private tail: StreamCommit | undefined
|
||||
private active: ActiveEntry | undefined
|
||||
@@ -238,14 +249,14 @@ export class RunScrollbackStream {
|
||||
const body = entryBody(commit)
|
||||
if (body.type === "none") {
|
||||
if (entryDone(commit)) {
|
||||
await this.finishActive(entryFlags(commit).trailingNewline)
|
||||
await this.finishActive(false)
|
||||
}
|
||||
|
||||
this.tail = commit
|
||||
return
|
||||
}
|
||||
|
||||
if (this.wrote && !same) {
|
||||
if (this.wrote && wantsSpacer(this.tail, commit)) {
|
||||
this.renderer.writeToScrollback(spacerWriter())
|
||||
}
|
||||
|
||||
@@ -256,7 +267,7 @@ export class RunScrollbackStream {
|
||||
) {
|
||||
await this.writeStreaming(commit, body)
|
||||
if (entryDone(commit)) {
|
||||
await this.finishActive(entryFlags(commit).trailingNewline)
|
||||
await this.finishActive(false)
|
||||
}
|
||||
this.wrote = true
|
||||
this.tail = commit
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
// event arrives, the queue entry is removed and the footer falls back
|
||||
// to the next pending request or to the prompt view.
|
||||
import type { Event, Part, PermissionRequest, QuestionRequest, ToolPart } from "@opencode-ai/sdk/v2"
|
||||
import * as Locale from "../../../util/locale"
|
||||
import * as Locale from "@/util/locale"
|
||||
import { toolView } from "./tool"
|
||||
import type { FooterOutput, FooterPatch, FooterView, StreamCommit } from "./types"
|
||||
|
||||
@@ -492,11 +492,19 @@ function flushPart(data: SessionData, commits: SessionCommit[], partID: string,
|
||||
|
||||
if (sent === 0) {
|
||||
chunk = chunk.replace(/^\n+/, "")
|
||||
// Some models emit a standalone whitespace token before real content.
|
||||
// Keep buffering until we have visible text so scrollback doesn't get a blank row.
|
||||
if (!chunk.trim()) {
|
||||
return
|
||||
}
|
||||
if (kind === "reasoning" && chunk) {
|
||||
chunk = `Thinking: ${chunk.replace(/\[REDACTED\]/g, "")}`
|
||||
}
|
||||
if (kind === "assistant" && chunk) {
|
||||
chunk = stripEcho(data, msg, chunk)
|
||||
if (!chunk.trim()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ import {
|
||||
type ScrollbackSnapshot,
|
||||
type ScrollbackWriter,
|
||||
} from "@opentui/core"
|
||||
import * as Locale from "../../../util/locale"
|
||||
import { logo } from "../../logo"
|
||||
import * as Locale from "@/util/locale"
|
||||
import { logo } from "@/cli/logo"
|
||||
import type { RunEntryTheme } from "./theme"
|
||||
|
||||
export const SPLASH_TITLE_LIMIT = 50
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Event, Part, PermissionRequest, QuestionRequest, ToolPart } from "@opencode-ai/sdk/v2"
|
||||
import * as Locale from "../../../util/locale"
|
||||
import * as Locale from "@/util/locale"
|
||||
import {
|
||||
bootstrapSessionData,
|
||||
createSessionData,
|
||||
|
||||
@@ -18,26 +18,26 @@ import os from "os"
|
||||
import path from "path"
|
||||
import stripAnsi from "strip-ansi"
|
||||
import type { ToolPart } from "@opencode-ai/sdk/v2"
|
||||
import type * as Tool from "../../../tool/tool"
|
||||
import type { ApplyPatchTool } from "../../../tool/apply_patch"
|
||||
import type { BashTool } from "../../../tool/bash"
|
||||
import type { CodeSearchTool } from "../../../tool/codesearch"
|
||||
import type { EditTool } from "../../../tool/edit"
|
||||
import type { GlobTool } from "../../../tool/glob"
|
||||
import type { GrepTool } from "../../../tool/grep"
|
||||
import type { InvalidTool } from "../../../tool/invalid"
|
||||
import type { LspTool } from "../../../tool/lsp"
|
||||
import type { PlanExitTool } from "../../../tool/plan"
|
||||
import type { QuestionTool } from "../../../tool/question"
|
||||
import type { ReadTool } from "../../../tool/read"
|
||||
import type { SkillTool } from "../../../tool/skill"
|
||||
import type { TaskTool } from "../../../tool/task"
|
||||
import type { TodoWriteTool } from "../../../tool/todo"
|
||||
import type { WebFetchTool } from "../../../tool/webfetch"
|
||||
import type { WebSearchTool } from "../../../tool/websearch"
|
||||
import type { WriteTool } from "../../../tool/write"
|
||||
import { LANGUAGE_EXTENSIONS } from "../../../lsp/language"
|
||||
import * as Locale from "../../../util/locale"
|
||||
import type * as Tool from "@/tool/tool"
|
||||
import type { ApplyPatchTool } from "@/tool/apply_patch"
|
||||
import type { BashTool } from "@/tool/bash"
|
||||
import type { CodeSearchTool } from "@/tool/codesearch"
|
||||
import type { EditTool } from "@/tool/edit"
|
||||
import type { GlobTool } from "@/tool/glob"
|
||||
import type { GrepTool } from "@/tool/grep"
|
||||
import type { InvalidTool } from "@/tool/invalid"
|
||||
import type { LspTool } from "@/tool/lsp"
|
||||
import type { PlanExitTool } from "@/tool/plan"
|
||||
import type { QuestionTool } from "@/tool/question"
|
||||
import type { ReadTool } from "@/tool/read"
|
||||
import type { SkillTool } from "@/tool/skill"
|
||||
import type { TaskTool } from "@/tool/task"
|
||||
import type { TodoWriteTool } from "@/tool/todo"
|
||||
import type { WebFetchTool } from "@/tool/webfetch"
|
||||
import type { WebSearchTool } from "@/tool/websearch"
|
||||
import type { WriteTool } from "@/tool/write"
|
||||
import { LANGUAGE_EXTENSIONS } from "@/lsp/language"
|
||||
import * as Locale from "@/util/locale"
|
||||
import type { RunDiffStyle, RunEntryBody, StreamCommit, ToolSnapshot } from "./types"
|
||||
|
||||
export type ToolView = {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// active based on the env var, and subsequent calls return the cached result.
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import { Global } from "../../../global"
|
||||
import { Global } from "@/global"
|
||||
|
||||
export type Trace = {
|
||||
write(type: string, data?: unknown): void
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { entryBody, entryCanStream, entryDone } from "../../../src/cli/cmd/run/entry.body"
|
||||
import type { StreamCommit } from "../../../src/cli/cmd/run/types"
|
||||
import { entryBody, entryCanStream, entryDone } from "@/cli/cmd/run/entry.body"
|
||||
import type { StreamCommit } from "@/cli/cmd/run/types"
|
||||
|
||||
function commit(input: Partial<StreamCommit> & Pick<StreamCommit, "kind" | "text" | "phase" | "source">): StreamCommit {
|
||||
return input
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, expect, test } from "bun:test"
|
||||
import { MockTreeSitterClient, createTestRenderer, type TestRenderer } from "@opentui/core/testing"
|
||||
import { RunFooter } from "../../../src/cli/cmd/run/footer"
|
||||
import { RUN_THEME_FALLBACK } from "../../../src/cli/cmd/run/theme"
|
||||
import { RunFooter } from "@/cli/cmd/run/footer"
|
||||
import { RUN_THEME_FALLBACK } from "@/cli/cmd/run/theme"
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
const active: Array<{ footer?: RunFooter; renderer: TestRenderer }> = []
|
||||
@@ -149,3 +149,77 @@ test("run footer keeps active streamed assistant content across width resize", a
|
||||
lib.commitSplitFooterSnapshot = originalCommitSplitFooterSnapshot
|
||||
}
|
||||
})
|
||||
|
||||
test("run footer keeps tool start rows tight with following reasoning", async () => {
|
||||
const out = await createTestRenderer({
|
||||
width: 80,
|
||||
height: 24,
|
||||
screenMode: "split-footer",
|
||||
footerHeight: 6,
|
||||
externalOutputMode: "capture-stdout",
|
||||
consoleMode: "disabled",
|
||||
})
|
||||
const footer = createFooter(out.renderer)
|
||||
active.push({ footer, renderer: out.renderer })
|
||||
const lib = Reflect.get(out.renderer, "lib") as {
|
||||
commitSplitFooterSnapshot: (...args: unknown[]) => unknown
|
||||
}
|
||||
const originalCommitSplitFooterSnapshot = lib.commitSplitFooterSnapshot.bind(lib)
|
||||
const payloads: string[] = []
|
||||
|
||||
lib.commitSplitFooterSnapshot = (...args) => {
|
||||
const snapshot = args[1] as {
|
||||
getRealCharBytes(addLineBreaks?: boolean): Uint8Array
|
||||
}
|
||||
payloads.push(decoder.decode(snapshot.getRealCharBytes(true)))
|
||||
return originalCommitSplitFooterSnapshot(...args)
|
||||
}
|
||||
|
||||
try {
|
||||
footer.append({
|
||||
kind: "tool",
|
||||
source: "tool",
|
||||
messageID: "msg-tool",
|
||||
partID: "part-tool",
|
||||
tool: "glob",
|
||||
phase: "start",
|
||||
text: "running glob",
|
||||
toolState: "running",
|
||||
part: {
|
||||
id: "part-tool",
|
||||
type: "tool",
|
||||
tool: "glob",
|
||||
callID: "call-tool",
|
||||
messageID: "msg-tool",
|
||||
sessionID: "session-1",
|
||||
state: {
|
||||
status: "running",
|
||||
input: {
|
||||
pattern: "**/run.ts",
|
||||
},
|
||||
time: {
|
||||
start: Date.now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
footer.append({
|
||||
kind: "reasoning",
|
||||
source: "reasoning",
|
||||
messageID: "msg-reasoning",
|
||||
partID: "part-reasoning",
|
||||
phase: "progress",
|
||||
text: "Thinking: Found it.",
|
||||
})
|
||||
|
||||
await footer.idle()
|
||||
|
||||
const rows = payloads
|
||||
.map((item) => item.replace(/ +/g, " ").trim())
|
||||
.filter(Boolean)
|
||||
|
||||
expect(rows).toEqual(['✱ Glob "**/run.ts"', "_Thinking:_ Found it."])
|
||||
} finally {
|
||||
lib.commitSplitFooterSnapshot = originalCommitSplitFooterSnapshot
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import { testRender } from "@opentui/solid"
|
||||
import { createSignal } from "solid-js"
|
||||
import { RunEntryContent } from "../../../src/cli/cmd/run/scrollback.writer"
|
||||
import { RunFooterView } from "../../../src/cli/cmd/run/footer.view"
|
||||
import { RUN_THEME_FALLBACK } from "../../../src/cli/cmd/run/theme"
|
||||
import type { StreamCommit } from "../../../src/cli/cmd/run/types"
|
||||
import { RunEntryContent } from "@/cli/cmd/run/scrollback.writer"
|
||||
import { RunFooterView } from "@/cli/cmd/run/footer.view"
|
||||
import { RUN_THEME_FALLBACK } from "@/cli/cmd/run/theme"
|
||||
import type { StreamCommit } from "@/cli/cmd/run/types"
|
||||
|
||||
test("run footer view loads", () => {
|
||||
expect(typeof RunFooterView).toBe("function")
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
permissionInfo,
|
||||
permissionReject,
|
||||
permissionRun,
|
||||
} from "../../../src/cli/cmd/run/permission.shared"
|
||||
} from "@/cli/cmd/run/permission.shared"
|
||||
|
||||
function req(input: Partial<PermissionRequest> = {}): PermissionRequest {
|
||||
return {
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
promptInfo,
|
||||
promptKeys,
|
||||
pushPromptHistory,
|
||||
} from "../../../src/cli/cmd/run/prompt.shared"
|
||||
import type { FooterKeybinds, RunPrompt } from "../../../src/cli/cmd/run/types"
|
||||
} from "@/cli/cmd/run/prompt.shared"
|
||||
import type { FooterKeybinds, RunPrompt } from "@/cli/cmd/run/types"
|
||||
|
||||
const keybinds: FooterKeybinds = {
|
||||
leader: "ctrl+x",
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
questionStoreCustom,
|
||||
questionSubmit,
|
||||
questionSync,
|
||||
} from "../../../src/cli/cmd/run/question.shared"
|
||||
} from "@/cli/cmd/run/question.shared"
|
||||
|
||||
function req(input: Partial<QuestionRequest> = {}): QuestionRequest {
|
||||
return {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { afterEach, describe, expect, mock, spyOn, test } from "bun:test"
|
||||
import { TuiConfig } from "../../../src/cli/cmd/tui/config/tui"
|
||||
import { TuiConfig } from "@/cli/cmd/tui/config/tui"
|
||||
import {
|
||||
resolveDiffStyle,
|
||||
resolveFooterKeybinds,
|
||||
resolveModelInfo,
|
||||
resolveSessionInfo,
|
||||
} from "../../../src/cli/cmd/run/runtime.boot"
|
||||
import type { RunInput } from "../../../src/cli/cmd/run/types"
|
||||
} from "@/cli/cmd/run/runtime.boot"
|
||||
import type { RunInput } from "@/cli/cmd/run/types"
|
||||
|
||||
describe("run runtime boot", () => {
|
||||
afterEach(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { runPromptQueue } from "../../../src/cli/cmd/run/runtime.queue"
|
||||
import type { FooterApi, FooterEvent, RunPrompt, StreamCommit } from "../../../src/cli/cmd/run/types"
|
||||
import { runPromptQueue } from "@/cli/cmd/run/runtime.queue"
|
||||
import type { FooterApi, FooterEvent, RunPrompt, StreamCommit } from "@/cli/cmd/run/types"
|
||||
|
||||
function footer() {
|
||||
const prompts = new Set<(input: RunPrompt) => void>()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, expect, test } from "bun:test"
|
||||
import { MockTreeSitterClient, createTestRenderer, type TestRenderer } from "@opentui/core/testing"
|
||||
import { RunScrollbackStream } from "../../../src/cli/cmd/run/scrollback.surface"
|
||||
import { RUN_THEME_FALLBACK } from "../../../src/cli/cmd/run/theme"
|
||||
import { RunScrollbackStream } from "@/cli/cmd/run/scrollback.surface"
|
||||
import { RUN_THEME_FALLBACK } from "@/cli/cmd/run/theme"
|
||||
|
||||
type ClaimedCommit = {
|
||||
snapshot: {
|
||||
@@ -142,7 +142,7 @@ test("completes markdown replies without adding a second blank line above the fo
|
||||
const progress = claimCommits(out.renderer)
|
||||
try {
|
||||
expect(progress).toHaveLength(1)
|
||||
expect(progress[0]!.snapshot.height).toBe(4)
|
||||
expect(progress[0]!.snapshot.height).toBe(5)
|
||||
const rendered = decoder.decode(progress[0]!.snapshot.getRealCharBytes(true))
|
||||
expect(rendered).toContain("Markdown Sample")
|
||||
expect(rendered).toContain("Item 2")
|
||||
@@ -165,6 +165,109 @@ test("completes markdown replies without adding a second blank line above the fo
|
||||
}
|
||||
})
|
||||
|
||||
test("streamed assistant final leaves newline ownership to the next entry", async () => {
|
||||
const out = await createTestRenderer({
|
||||
width: 80,
|
||||
screenMode: "split-footer",
|
||||
footerHeight: 6,
|
||||
externalOutputMode: "capture-stdout",
|
||||
consoleMode: "disabled",
|
||||
})
|
||||
active.push(out.renderer)
|
||||
|
||||
const treeSitterClient = new MockTreeSitterClient({ autoResolveTimeout: 0 })
|
||||
treeSitterClient.setMockResult({ highlights: [] })
|
||||
|
||||
const scrollback = new RunScrollbackStream(out.renderer, RUN_THEME_FALLBACK, {
|
||||
treeSitterClient,
|
||||
wrote: false,
|
||||
})
|
||||
|
||||
await scrollback.append({
|
||||
kind: "assistant",
|
||||
text: "hello",
|
||||
phase: "progress",
|
||||
source: "assistant",
|
||||
messageID: "msg-1",
|
||||
partID: "part-1",
|
||||
})
|
||||
destroyCommits(claimCommits(out.renderer))
|
||||
|
||||
await scrollback.append({
|
||||
kind: "assistant",
|
||||
text: "",
|
||||
phase: "final",
|
||||
source: "assistant",
|
||||
messageID: "msg-1",
|
||||
partID: "part-1",
|
||||
})
|
||||
|
||||
const final = claimCommits(out.renderer)
|
||||
try {
|
||||
expect(final).toHaveLength(1)
|
||||
expect(final[0]!.trailingNewline).toBe(false)
|
||||
} finally {
|
||||
destroyCommits(final)
|
||||
}
|
||||
})
|
||||
|
||||
test("preserves blank rows between streamed markdown block commits", async () => {
|
||||
const out = await createTestRenderer({
|
||||
screenMode: "split-footer",
|
||||
footerHeight: 6,
|
||||
externalOutputMode: "capture-stdout",
|
||||
consoleMode: "disabled",
|
||||
})
|
||||
active.push(out.renderer)
|
||||
|
||||
const treeSitterClient = new MockTreeSitterClient({ autoResolveTimeout: 0 })
|
||||
treeSitterClient.setMockResult({ highlights: [] })
|
||||
|
||||
const scrollback = new RunScrollbackStream(out.renderer, RUN_THEME_FALLBACK, {
|
||||
treeSitterClient,
|
||||
wrote: false,
|
||||
})
|
||||
|
||||
await scrollback.append({
|
||||
kind: "assistant",
|
||||
text: "# Title\n\nPara 1\n\n",
|
||||
phase: "progress",
|
||||
source: "assistant",
|
||||
messageID: "msg-1",
|
||||
partID: "part-1",
|
||||
})
|
||||
|
||||
const first = claimCommits(out.renderer)
|
||||
expect(first).toHaveLength(1)
|
||||
|
||||
await scrollback.append({
|
||||
kind: "assistant",
|
||||
text: "> Quote",
|
||||
phase: "progress",
|
||||
source: "assistant",
|
||||
messageID: "msg-1",
|
||||
partID: "part-1",
|
||||
})
|
||||
|
||||
const second = claimCommits(out.renderer)
|
||||
expect(second).toHaveLength(0)
|
||||
|
||||
await scrollback.complete()
|
||||
|
||||
const final = claimCommits(out.renderer)
|
||||
try {
|
||||
expect(final).toHaveLength(1)
|
||||
|
||||
const rendered = [...first, ...final]
|
||||
.map((item) => decoder.decode(item.snapshot.getRealCharBytes(true)).replace(/ +\n/g, "\n"))
|
||||
.join("")
|
||||
expect(rendered).toContain("# Title\n\nPara 1\n\n> Quote")
|
||||
} finally {
|
||||
destroyCommits(first)
|
||||
destroyCommits(final)
|
||||
}
|
||||
})
|
||||
|
||||
test("coalesces same-line tool progress into one snapshot", async () => {
|
||||
const out = await createTestRenderer({
|
||||
width: 80,
|
||||
@@ -345,7 +448,6 @@ test("renders promoted task-result markdown without leading blank rows", async (
|
||||
expect(commits.length).toBeGreaterThan(0)
|
||||
const rendered = commits.map((item) => decoder.decode(item.snapshot.getRealCharBytes(true))).join("")
|
||||
expect(rendered.startsWith("\n")).toBe(false)
|
||||
expect(rendered.split("\n")[0]?.trim()).toBe("Location: `/tmp/run.ts`")
|
||||
expect(rendered).toContain("Summary:")
|
||||
expect(rendered).toContain("Local interactive mode")
|
||||
} finally {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import type { Event } from "@opencode-ai/sdk/v2"
|
||||
import { createSessionData, flushInterrupted, reduceSessionData } from "../../../src/cli/cmd/run/session-data"
|
||||
import { createSessionData, flushInterrupted, reduceSessionData } from "@/cli/cmd/run/session-data"
|
||||
|
||||
function reduce(data: ReturnType<typeof createSessionData>, event: unknown, thinking = true) {
|
||||
return reduceSessionData({
|
||||
@@ -77,6 +77,61 @@ describe("run session data", () => {
|
||||
])
|
||||
})
|
||||
|
||||
test("buffers whitespace-only initial assistant chunks until real content arrives", () => {
|
||||
let data = createSessionData()
|
||||
|
||||
data = reduce(data, assistant("msg-1")).data
|
||||
data = reduce(data, {
|
||||
type: "message.part.updated",
|
||||
properties: {
|
||||
part: {
|
||||
id: "txt-1",
|
||||
messageID: "msg-1",
|
||||
sessionID: "session-1",
|
||||
type: "text",
|
||||
text: "",
|
||||
time: { start: Date.now() },
|
||||
},
|
||||
},
|
||||
}).data
|
||||
|
||||
let out = reduce(data, {
|
||||
type: "message.part.delta",
|
||||
properties: {
|
||||
sessionID: "session-1",
|
||||
messageID: "msg-1",
|
||||
partID: "txt-1",
|
||||
field: "text",
|
||||
delta: " ",
|
||||
},
|
||||
})
|
||||
|
||||
expect(out.commits).toEqual([])
|
||||
|
||||
data = out.data
|
||||
out = reduce(data, {
|
||||
type: "message.part.delta",
|
||||
properties: {
|
||||
sessionID: "session-1",
|
||||
messageID: "msg-1",
|
||||
partID: "txt-1",
|
||||
field: "text",
|
||||
delta: "Found",
|
||||
},
|
||||
})
|
||||
|
||||
expect(out.commits).toEqual([
|
||||
{
|
||||
kind: "assistant",
|
||||
text: " Found",
|
||||
phase: "progress",
|
||||
source: "assistant",
|
||||
messageID: "msg-1",
|
||||
partID: "txt-1",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("drops user text when the delayed role resolves to user", () => {
|
||||
let data = createSessionData()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
sessionVariant,
|
||||
type RunSession,
|
||||
type SessionMessages,
|
||||
} from "../../../src/cli/cmd/run/session.shared"
|
||||
} from "@/cli/cmd/run/session.shared"
|
||||
|
||||
const model = {
|
||||
providerID: "openai",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { writeSessionOutput } from "../../../src/cli/cmd/run/stream"
|
||||
import type { FooterApi, FooterEvent, StreamCommit } from "../../../src/cli/cmd/run/types"
|
||||
import { writeSessionOutput } from "@/cli/cmd/run/stream"
|
||||
import type { FooterApi, FooterEvent, StreamCommit } from "@/cli/cmd/run/types"
|
||||
|
||||
function footer() {
|
||||
const events: FooterEvent[] = []
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
|
||||
import { createSessionTransport } from "../../../src/cli/cmd/run/stream.transport"
|
||||
import type { FooterApi, FooterEvent, RunFilePart, StreamCommit } from "../../../src/cli/cmd/run/types"
|
||||
import { createSessionTransport } from "@/cli/cmd/run/stream.transport"
|
||||
import type { FooterApi, FooterEvent, RunFilePart, StreamCommit } from "@/cli/cmd/run/types"
|
||||
|
||||
function defer<T = void>() {
|
||||
let resolve!: (value: T | PromiseLike<T>) => void
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { entryBody } from "../../../src/cli/cmd/run/entry.body"
|
||||
import { entryBody } from "@/cli/cmd/run/entry.body"
|
||||
import {
|
||||
bootstrapSubagentData,
|
||||
clearFinishedSubagents,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
reduceSubagentData,
|
||||
snapshotSelectedSubagentData,
|
||||
snapshotSubagentData,
|
||||
} from "../../../src/cli/cmd/run/subagent-data"
|
||||
} from "@/cli/cmd/run/subagent-data"
|
||||
|
||||
function visible(commits: Array<Parameters<typeof entryBody>[0]>) {
|
||||
return commits.flatMap((item) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { RGBA, SyntaxStyle } from "@opentui/core"
|
||||
import { opaqueSyntaxStyle } from "../../../src/cli/cmd/run/theme"
|
||||
import { opaqueSyntaxStyle } from "@/cli/cmd/run/theme"
|
||||
|
||||
describe("run theme", () => {
|
||||
test("flattens subtle syntax alpha against the run background", () => {
|
||||
|
||||
@@ -3,15 +3,15 @@ import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect, FileSystem, Layer } from "effect"
|
||||
import { Global } from "../../../src/global"
|
||||
import { Global } from "@/global"
|
||||
import {
|
||||
createVariantRuntime,
|
||||
cycleVariant,
|
||||
formatModelLabel,
|
||||
pickVariant,
|
||||
resolveVariant,
|
||||
} from "../../../src/cli/cmd/run/variant.shared"
|
||||
import type { SessionMessages } from "../../../src/cli/cmd/run/session.shared"
|
||||
} from "@/cli/cmd/run/variant.shared"
|
||||
import type { SessionMessages } from "@/cli/cmd/run/session.shared"
|
||||
import { testEffect } from "../../lib/effect"
|
||||
|
||||
const model = {
|
||||
|
||||
Reference in New Issue
Block a user