Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions packages/core/src/kilocode/pty/smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { KiloPtyTermination } from "./termination"
import { spawn } from "#pty"

const TIMEOUT = 15_000
const RENDER_TIMEOUT = 60_000

export function marker(output: string) {
const text = output
.replace(/\x1b\](?:[^\x07\x1b]|\x1b(?!\\))*(?:\x07|\x1b\\)/g, "")
.replace(/\x1b[P^_](?:[^\x1b]|\x1b(?!\\))*\x1b\\/g, "")
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
.replace(/\x1b[@-_]/g, "")
return text.split(/\r?\n/).some((line) => line.trim() === "KILO_PTY_READY")
}

async function render() {
const proc = spawn(process.execPath, ["--pure"], {
Expand Down Expand Up @@ -34,15 +44,18 @@ async function render() {
state.exited = true
ready.reject(new Error(`TUI exited before rendering (code ${event.exitCode}): ${JSON.stringify(state.output)}`))
})
const timeout = AbortSignal.timeout(TIMEOUT)
const timeout = AbortSignal.timeout(RENDER_TIMEOUT)

try {
await Promise.race([
ready.promise,
new Promise<never>((_, reject) =>
timeout.addEventListener(
"abort",
() => reject(new Error(`TUI produced no rendered frame within ${TIMEOUT}ms: ${JSON.stringify(state.output)}`)),
() =>
reject(
new Error(`TUI produced no rendered frame within ${RENDER_TIMEOUT}ms: ${JSON.stringify(state.output)}`),
),
{ once: true },
),
),
Expand All @@ -67,7 +80,7 @@ export async function smoke() {
const exited = Promise.withResolvers<number>()
const data = proc.onData((chunk) => {
state.output += chunk
if (/(?:^|[\r\n])KILO_PTY_READY(?:\r?\n|$)/.test(state.output)) output.resolve()
Comment thread
WebReflection marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, if it's multiline I think the /m at the end of the regex is needed, I would even go /gm to be sure on multiple lines.

If this output is meant to be a "maybe with new lines around" situation, but the only thing that should be there is KILO_PTY_READY, then I'd use state.output.trim() === "KILO_PTY_READY" instead and remove the regex complexity.

if (marker(state.output)) output.resolve()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this should be an else if as if already resolved in previous if there's no point in re-resolving and checking marker at all.

})
const exit = proc.onExit((event) => {
state.exited = true
Expand Down
8 changes: 7 additions & 1 deletion packages/core/test/kilocode/pty-durability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,13 @@ describe("durable PTY registry", () => {
const info = yield* Effect.scoped(
Effect.gen(function* () {
const pty = yield* Pty.Service
return yield* pty.create({ command: "/bin/sh", args: ["-c", "exit 7"], cwd: dir.path })
return yield* pty.create({ command: "/bin/sh", cwd: dir.path })
}).pipe(Effect.provide(locations.get(target))),
)
yield* Effect.scoped(
Effect.gen(function* () {
const pty = yield* Pty.Service
yield* pty.write(info.id, "exit 7\r")
}).pipe(Effect.provide(locations.get(target))),
)
const exited = yield* Queue.take(queue).pipe(Effect.timeout("5 seconds"))
Expand Down
21 changes: 21 additions & 0 deletions packages/core/test/kilocode/pty-smoke.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, expect, test } from "bun:test"
import { marker } from "../../src/kilocode/pty/smoke"

describe("PTY smoke output", () => {
test("detects a marker after PowerShell formatting", () => {
const output =
"\x1b[93mecho KILO_PTY_READY\r\n\x1b[mKILO_PTY_READY\r\n\x1b]0;Administrator: PowerShell\x07PS> "

expect(marker(output)).toBe(true)
})

test("does not accept the echoed command", () => {
expect(marker("\x1b[93mecho KILO_PTY_READY\r\n\x1b[mPS> ")).toBe(false)
})

test("detects a marker around OSC and DCS sequences", () => {
const output = "\x1b]133;A\x07\x1bP+q4d73\x1b\\KILO_PTY_READY\r\n"

expect(marker(output)).toBe(true)
})
})
Loading