mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
refactor: resolve comments
This commit is contained in:
@@ -150,7 +150,9 @@ export async function handle(args: ArgumentsCamelCase) {
|
||||
const structured = json || args.output === "md"
|
||||
|
||||
if (!args.quiet && !structured) {
|
||||
UI.println(`${color(UI.Style.TEXT_INFO)}Starting roll call for models with prompt: "${args.prompt}"${color(UI.Style.TEXT_NORMAL)}`)
|
||||
UI.println(
|
||||
`${color(UI.Style.TEXT_INFO)}Starting roll call for models with prompt: "${args.prompt}"${color(UI.Style.TEXT_NORMAL)}`,
|
||||
)
|
||||
UI.println(
|
||||
`${color(UI.Style.TEXT_INFO)}Timeout per model: ${args.timeout}ms, Parallel calls: ${args.parallel}${color(UI.Style.TEXT_NORMAL)}`,
|
||||
)
|
||||
@@ -178,7 +180,8 @@ export async function handle(args: ArgumentsCamelCase) {
|
||||
)
|
||||
|
||||
if (models.length === 0) {
|
||||
if (!args.quiet && !structured) UI.println(`${color(UI.Style.TEXT_WARNING)}No models to test after filtering.${color(UI.Style.TEXT_NORMAL)}`)
|
||||
if (!args.quiet && !structured)
|
||||
UI.println(`${color(UI.Style.TEXT_WARNING)}No models to test after filtering.${color(UI.Style.TEXT_NORMAL)}`)
|
||||
if (json) console.log(JSON.stringify([], null, 2))
|
||||
if (args.output === "md") console.log(formatMarkdown([]))
|
||||
if (structured) return
|
||||
@@ -262,7 +265,12 @@ export async function handle(args: ArgumentsCamelCase) {
|
||||
})
|
||||
}
|
||||
|
||||
async function call(model: Provider.Model, prompt: string, timeout: number, start: number): Promise<Omit<Result, "model">> {
|
||||
async function call(
|
||||
model: Provider.Model,
|
||||
prompt: string,
|
||||
timeout: number,
|
||||
start: number,
|
||||
): Promise<Omit<Result, "model">> {
|
||||
try {
|
||||
const language = await Provider.getLanguage(model)
|
||||
const sessionID = randomUUID()
|
||||
@@ -305,7 +313,10 @@ async function call(model: Provider.Model, prompt: string, timeout: number, star
|
||||
}
|
||||
|
||||
function error(cause: unknown) {
|
||||
if (cause instanceof Error && (cause.name === "AbortError" || cause.message.includes("abort") || cause.message.includes("timeout"))) {
|
||||
if (
|
||||
cause instanceof Error &&
|
||||
(cause.name === "AbortError" || cause.message.includes("abort") || cause.message.includes("timeout"))
|
||||
) {
|
||||
return { type: "timeout", message: "The operation timed out." }
|
||||
}
|
||||
|
||||
|
||||
@@ -59,10 +59,7 @@ export function ModelInfoPanel(props: Props) {
|
||||
gap={1}
|
||||
flexShrink={0}
|
||||
>
|
||||
<scrollbox
|
||||
maxHeight={maxHeight()}
|
||||
paddingRight={1}
|
||||
>
|
||||
<scrollbox maxHeight={maxHeight()} paddingRight={1}>
|
||||
<box>
|
||||
<text fg={theme.text} attributes={TextAttributes.BOLD}>
|
||||
{m().name ?? m().id ?? "Model"}
|
||||
@@ -141,7 +138,7 @@ export function ModelInfoPanel(props: Props) {
|
||||
</box>
|
||||
</Show>
|
||||
<Show when={desc()}>
|
||||
<text fg={theme.textMuted}>{" "}</text>
|
||||
<text fg={theme.textMuted}> </text>
|
||||
<text fg={theme.textMuted} width={23}>
|
||||
{desc()}
|
||||
</text>
|
||||
|
||||
@@ -64,7 +64,10 @@ export namespace KiloCompactionChunks {
|
||||
export function needed(input: { cfg: Config.Info; model: Provider.Model; tokens: number }) {
|
||||
// Apply 1.3x multiplier to token estimate to compensate for Token.estimate
|
||||
// under-counting actual provider tokenizer counts by ~15-30%.
|
||||
return Math.ceil(input.tokens * 1.3) + model(input.model).limit.output > usable({ cfg: input.cfg, model: model(input.model) })
|
||||
return (
|
||||
Math.ceil(input.tokens * 1.3) + model(input.model).limit.output >
|
||||
usable({ cfg: input.cfg, model: model(input.model) })
|
||||
)
|
||||
}
|
||||
|
||||
export function replay(input: Input & { replay: Replay }) {
|
||||
@@ -161,10 +164,12 @@ export namespace KiloCompactionChunks {
|
||||
|
||||
function part(part: MessageV2.Part) {
|
||||
if (part.type === "text") return clip({ text: part.text, chars: TRANSCRIPT_MAX_CHARS, label: "Text" })
|
||||
if (part.type === "reasoning") return `[Reasoning]: ${clip({ text: part.text, chars: TRANSCRIPT_MAX_CHARS, label: "Reasoning" })}`
|
||||
if (part.type === "reasoning")
|
||||
return `[Reasoning]: ${clip({ text: part.text, chars: TRANSCRIPT_MAX_CHARS, label: "Reasoning" })}`
|
||||
if (part.type === "file") return `[File attachment]: ${part.filename ?? part.url} (${part.mime})`
|
||||
if (part.type === "agent") return `[Agent]: ${part.name}`
|
||||
if (part.type === "subtask") return `[Subtask ${part.agent}]: ${part.description}\n${clip({ text: part.prompt, chars: TRANSCRIPT_MAX_CHARS, label: "Subtask prompt" })}`
|
||||
if (part.type === "subtask")
|
||||
return `[Subtask ${part.agent}]: ${part.description}\n${clip({ text: part.prompt, chars: TRANSCRIPT_MAX_CHARS, label: "Subtask prompt" })}`
|
||||
if (part.type === "tool") {
|
||||
const head = `[Tool ${part.tool} ${part.state.status}]`
|
||||
if (part.state.status === "completed") {
|
||||
@@ -174,7 +179,8 @@ export namespace KiloCompactionChunks {
|
||||
`output: ${clip({ text: part.state.output, chars: TOOL_OUTPUT_MAX_CHARS, label: "Tool output" })}`,
|
||||
].join("\n")
|
||||
}
|
||||
if (part.state.status === "error") return `${head}\n${clip({ text: part.state.error, chars: TOOL_OUTPUT_MAX_CHARS, label: "Tool error" })}`
|
||||
if (part.state.status === "error")
|
||||
return `${head}\n${clip({ text: part.state.error, chars: TOOL_OUTPUT_MAX_CHARS, label: "Tool error" })}`
|
||||
return `${head}\ninput: ${clip({ text: JSON.stringify(part.state.input), chars: TOOL_OUTPUT_MAX_CHARS, label: "Tool input" })}`
|
||||
}
|
||||
if (part.type === "step-finish") return `[Step finished]: ${part.reason}`
|
||||
@@ -185,11 +191,12 @@ export namespace KiloCompactionChunks {
|
||||
function transcript(input: { messages: MessageV2.WithParts[] }) {
|
||||
return input.messages
|
||||
.map((msg, index) => {
|
||||
const body = msg.parts
|
||||
.map(part)
|
||||
.filter(Boolean)
|
||||
.join("\n\n")
|
||||
return [`<message index=\"${index + 1}\" role=\"${msg.info.role}\">`, body || "[no content]", "</message>"].join("\n")
|
||||
const body = msg.parts.map(part).filter(Boolean).join("\n\n")
|
||||
return [
|
||||
`<message index=\"${index + 1}\" role=\"${msg.info.role}\">`,
|
||||
body || "[no content]",
|
||||
"</message>",
|
||||
].join("\n")
|
||||
})
|
||||
.join("\n\n")
|
||||
}
|
||||
@@ -239,10 +246,7 @@ export namespace KiloCompactionChunks {
|
||||
...input.agent,
|
||||
options: {
|
||||
...opts,
|
||||
maxOutputTokens: Math.min(
|
||||
OUTPUT,
|
||||
typeof opts?.maxOutputTokens === "number" ? opts.maxOutputTokens : OUTPUT,
|
||||
),
|
||||
maxOutputTokens: Math.min(OUTPUT, typeof opts?.maxOutputTokens === "number" ? opts.maxOutputTokens : OUTPUT),
|
||||
},
|
||||
}
|
||||
const out = yield* Effect.gen(function* () {
|
||||
@@ -325,16 +329,15 @@ export namespace KiloCompactionChunks {
|
||||
const chunks = yield* split({ messages: input.messages, model: input.model, size })
|
||||
log.info("fallback", { chunks: chunks.length, concurrency: CONCURRENCY })
|
||||
|
||||
const partial = yield* Effect.forEach(
|
||||
chunks,
|
||||
(chunk) => summarize({ ...input, chunk, total: chunks.length }),
|
||||
{ concurrency: Math.min(CONCURRENCY, chunks.length) },
|
||||
)
|
||||
const partial = yield* Effect.forEach(chunks, (chunk) => summarize({ ...input, chunk, total: chunks.length }), {
|
||||
concurrency: Math.min(CONCURRENCY, chunks.length),
|
||||
})
|
||||
if (partial.some((item) => item.result !== "continue" || !item.output)) return "compact" as const
|
||||
|
||||
const final = chunks.length === 1 && (yield* large({ messages: chunks[0].messages, model: input.model, size }))
|
||||
? partial[0]
|
||||
: yield* reduce({ ...input, summaries: partial.map((item) => item.output!), depth: 0 })
|
||||
const final =
|
||||
chunks.length === 1 && (yield* large({ messages: chunks[0].messages, model: input.model, size }))
|
||||
? partial[0]
|
||||
: yield* reduce({ ...input, summaries: partial.map((item) => item.output!), depth: 0 })
|
||||
if (!final || final.result !== "continue" || !final.output) return "compact" as const
|
||||
|
||||
yield* input.updatePart({
|
||||
|
||||
@@ -440,7 +440,13 @@ export async function CodexAuthPlugin(input: PluginInput): Promise<Hooks> {
|
||||
if (!currentAuth.access || currentAuth.expires < Date.now()) {
|
||||
log.info("refreshing codex access token")
|
||||
// kilocode_change start
|
||||
await refreshCodexAuth({ input, getAuth, auth: currentAuth, refresh: refreshAccessToken, account: extractAccountId })
|
||||
await refreshCodexAuth({
|
||||
input,
|
||||
getAuth,
|
||||
auth: currentAuth,
|
||||
refresh: refreshAccessToken,
|
||||
account: extractAccountId,
|
||||
})
|
||||
// kilocode_change end
|
||||
}
|
||||
|
||||
|
||||
@@ -46,16 +46,9 @@ export const layer = Layer.effect(
|
||||
yield* plugin.init()
|
||||
yield* Effect.promise(() => KilocodeBootstrap.init()).pipe(Effect.forkDetach) // kilocode_change
|
||||
// kilocode_change start - shareNext removed from list, handled by KilocodeBootstrap
|
||||
yield* Effect.all(
|
||||
[
|
||||
lsp,
|
||||
format,
|
||||
file,
|
||||
fileWatcher,
|
||||
vcs,
|
||||
snapshot,
|
||||
].map((s) => Effect.forkDetach(s.init())),
|
||||
).pipe(Effect.withSpan("InstanceBootstrap.init"))
|
||||
yield* Effect.all([lsp, format, file, fileWatcher, vcs, snapshot].map((s) => Effect.forkDetach(s.init()))).pipe(
|
||||
Effect.withSpan("InstanceBootstrap.init"),
|
||||
)
|
||||
// kilocode_change end
|
||||
|
||||
const projectID = ctx.project.id
|
||||
|
||||
@@ -302,7 +302,10 @@ export const layer: Layer.Layer<
|
||||
// goes backwards through parts until there are PRUNE_PROTECT tokens worth of tool
|
||||
// calls, then erases output of older tool calls to free context space
|
||||
// kilocode_change start - preserve normal opt-in pruning, but allow payload/compaction cleanup by default
|
||||
const prune = Effect.fn("SessionCompaction.prune")(function* (input: { sessionID: SessionID; reason?: PruneReason }) {
|
||||
const prune = Effect.fn("SessionCompaction.prune")(function* (input: {
|
||||
sessionID: SessionID
|
||||
reason?: PruneReason
|
||||
}) {
|
||||
const cfg = yield* config.get()
|
||||
const reason = input.reason ?? "normal"
|
||||
if (cfg.compaction?.prune === false) return
|
||||
@@ -469,7 +472,10 @@ export const layer: Layer.Layer<
|
||||
// kilocode_change end
|
||||
|
||||
// kilocode_change start - fallback to chunked compaction when the first summary overflows
|
||||
const fallback = KiloCompactionChunks.eligible({ result, error: processor.message.error ?? processor.compactError?.() })
|
||||
const fallback = KiloCompactionChunks.eligible({
|
||||
result,
|
||||
error: processor.message.error ?? processor.compactError?.(),
|
||||
})
|
||||
? yield* KiloCompactionChunks.process({
|
||||
processors,
|
||||
session,
|
||||
|
||||
@@ -239,7 +239,6 @@ function preview(text: string) {
|
||||
return "...\n\n" + text.slice(-MAX_METADATA_LENGTH)
|
||||
}
|
||||
|
||||
|
||||
function tail(text: string, maxLines: number, maxBytes: number) {
|
||||
const lines = text.split("\n")
|
||||
if (lines.length <= maxLines && Buffer.byteLength(text, "utf-8") <= maxBytes) {
|
||||
|
||||
@@ -38,7 +38,8 @@ describe("Codex auth refresh", () => {
|
||||
name: "ProviderAuthError",
|
||||
data: {
|
||||
providerID: "openai",
|
||||
message: "Your ChatGPT sign-in expired or was revoked. Sign in with ChatGPT again to continue using Codex models.",
|
||||
message:
|
||||
"Your ChatGPT sign-in expired or was revoked. Sign in with ChatGPT again to continue using Codex models.",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -342,25 +342,24 @@ describe("Encoding.read / Encoding.readSync / Encoding.write", () => {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe("Encoding.write with existing parent directories", () => {
|
||||
test("creates parent and writes file", async () => {
|
||||
await tmp(async (dir) => {
|
||||
const filepath = path.join(dir, "subdir", "test.txt");
|
||||
await Encoding.write(filepath, "hello");
|
||||
const text = await fs.readFile(filepath, "utf8");
|
||||
expect(text).toBe("hello");
|
||||
});
|
||||
});
|
||||
const filepath = path.join(dir, "subdir", "test.txt")
|
||||
await Encoding.write(filepath, "hello")
|
||||
const text = await fs.readFile(filepath, "utf8")
|
||||
expect(text).toBe("hello")
|
||||
})
|
||||
})
|
||||
|
||||
test("writes into existing directory (Windows EEXIST resiliency)", async () => {
|
||||
await tmp(async (dir) => {
|
||||
const existing = path.join(dir, "exists");
|
||||
await fs.mkdir(existing, { recursive: true });
|
||||
const filepath = path.join(existing, "test.txt");
|
||||
await Encoding.write(filepath, "hello");
|
||||
const text = await fs.readFile(filepath, "utf8");
|
||||
expect(text).toBe("hello");
|
||||
});
|
||||
});
|
||||
});
|
||||
const existing = path.join(dir, "exists")
|
||||
await fs.mkdir(existing, { recursive: true })
|
||||
const filepath = path.join(existing, "test.txt")
|
||||
await Encoding.write(filepath, "hello")
|
||||
const text = await fs.readFile(filepath, "utf8")
|
||||
expect(text).toBe("hello")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -45,7 +45,12 @@ test("failedProviders returns empty array when no fetch has occurred", () => {
|
||||
test("getFailure returns undefined when fetch succeeds", async () => {
|
||||
stubbedResult = {
|
||||
models: {
|
||||
"test/model": { id: "test/model", name: "Test", cost: { input: 1, output: 2 }, limit: { context: 128000, output: 4096 } },
|
||||
"test/model": {
|
||||
id: "test/model",
|
||||
name: "Test",
|
||||
cost: { input: 1, output: 2 },
|
||||
limit: { context: 128000, output: 4096 },
|
||||
},
|
||||
},
|
||||
}
|
||||
ModelCache.clear("kilo")
|
||||
@@ -81,7 +86,12 @@ test("failure state is cleared when subsequent fetch succeeds", async () => {
|
||||
|
||||
stubbedResult = {
|
||||
models: {
|
||||
"test/model": { id: "test/model", name: "Test", cost: { input: 1, output: 2 }, limit: { context: 128000, output: 4096 } },
|
||||
"test/model": {
|
||||
id: "test/model",
|
||||
name: "Test",
|
||||
cost: { input: 1, output: 2 },
|
||||
limit: { context: 128000, output: 4096 },
|
||||
},
|
||||
},
|
||||
}
|
||||
ModelCache.clear("kilo")
|
||||
|
||||
@@ -209,7 +209,11 @@ function runtime(layer: Layer.Layer<LLM.Service>, context = 7_000) {
|
||||
Layer.provide(Plugin.defaultLayer),
|
||||
Layer.provide(status),
|
||||
Layer.provide(bus),
|
||||
Layer.provide(Layer.mock(Config.Service)({ get: () => Effect.succeed({ ...Config.Info.zod.parse({}), compaction: { reserved: 1_000 } }) })),
|
||||
Layer.provide(
|
||||
Layer.mock(Config.Service)({
|
||||
get: () => Effect.succeed({ ...Config.Info.zod.parse({}), compaction: { reserved: 1_000 } }),
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -234,7 +238,9 @@ function fakeRuntime() {
|
||||
Effect.gen(function* () {
|
||||
outputs.push(input.model.limit.output)
|
||||
calls.push(JSON.stringify(stream.messages))
|
||||
const text = stream.messages.some((msg) => JSON.stringify(msg).includes("Create a new anchored summary"))
|
||||
const text = stream.messages.some((msg) =>
|
||||
JSON.stringify(msg).includes("Create a new anchored summary"),
|
||||
)
|
||||
? "final summary"
|
||||
: calls.length === 1
|
||||
? "chunk one"
|
||||
@@ -332,7 +338,9 @@ describe("KiloCompactionChunks", () => {
|
||||
const chunks = await Effect.runPromise(KiloCompactionChunks.split({ messages, model, size: 2_000 }))
|
||||
|
||||
expect(chunks.length).toBeGreaterThan(1)
|
||||
expect(chunks.flatMap((chunk) => chunk.messages.map((msg) => msg.info.id))).toEqual(messages.map((msg) => msg.info.id))
|
||||
expect(chunks.flatMap((chunk) => chunk.messages.map((msg) => msg.info.id))).toEqual(
|
||||
messages.map((msg) => msg.info.id),
|
||||
)
|
||||
})
|
||||
|
||||
test("falls back to chunk workers after the first compaction overflows", async () => {
|
||||
@@ -365,7 +373,9 @@ describe("KiloCompactionChunks", () => {
|
||||
|
||||
const all = await svc.messages({ sessionID: session.id })
|
||||
const summaries = all.filter((msg) => msg.info.role === "assistant" && msg.info.summary)
|
||||
const parts = summaries.flatMap((msg) => msg.parts).filter((part): part is MessageV2.TextPart => part.type === "text")
|
||||
const parts = summaries
|
||||
.flatMap((msg) => msg.parts)
|
||||
.filter((part): part is MessageV2.TextPart => part.type === "text")
|
||||
|
||||
expect(result).toBe("continue")
|
||||
expect(calls.length).toBeGreaterThanOrEqual(1)
|
||||
@@ -445,7 +455,9 @@ describe("KiloCompactionChunks", () => {
|
||||
|
||||
const all = await svc.messages({ sessionID: session.id })
|
||||
const summaries = all.filter((msg) => msg.info.role === "assistant" && msg.info.summary)
|
||||
const parts = summaries.flatMap((msg) => msg.parts).filter((part): part is MessageV2.TextPart => part.type === "text")
|
||||
const parts = summaries
|
||||
.flatMap((msg) => msg.parts)
|
||||
.filter((part): part is MessageV2.TextPart => part.type === "text")
|
||||
|
||||
expect(result).toBe("continue")
|
||||
expect(calls.length).toBeGreaterThan(0)
|
||||
@@ -545,7 +557,13 @@ describe("KiloCompactionChunks", () => {
|
||||
const old = await user(session.id, "old context")
|
||||
await assistant(session.id, old.id, tmp.path, "old reply")
|
||||
const large = await user(session.id, "large replay " + "x".repeat(40_000))
|
||||
await SessionCompaction.create({ sessionID: session.id, agent: "build", model: ref, auto: true, overflow: true })
|
||||
await SessionCompaction.create({
|
||||
sessionID: session.id,
|
||||
agent: "build",
|
||||
model: ref,
|
||||
auto: true,
|
||||
overflow: true,
|
||||
})
|
||||
|
||||
const rt = liveRuntime(stub.layer)
|
||||
try {
|
||||
|
||||
@@ -58,11 +58,7 @@ describe("instruction markdown substitutions", () => {
|
||||
yield* write(path.join(dir, "subdir", "nested", "file.ts"), "const value = 1")
|
||||
|
||||
const svc = yield* Instruction.Service
|
||||
const results = yield* svc.resolve(
|
||||
[],
|
||||
path.join(dir, "subdir", "nested", "file.ts"),
|
||||
MessageID.ascending(),
|
||||
)
|
||||
const results = yield* svc.resolve([], path.join(dir, "subdir", "nested", "file.ts"), MessageID.ascending())
|
||||
|
||||
expect(results).toHaveLength(1)
|
||||
expect(results[0].content).toContain("file content")
|
||||
|
||||
@@ -68,9 +68,7 @@ describe("normalizeUrls", () => {
|
||||
})
|
||||
|
||||
test("comma after URL in a list is not consumed", () => {
|
||||
expect(normalizeUrls("check https://example.com, then continue")).toBe(
|
||||
"check https://example.com, then continue",
|
||||
)
|
||||
expect(normalizeUrls("check https://example.com, then continue")).toBe("check https://example.com, then continue")
|
||||
})
|
||||
|
||||
test("closing parenthesis after URL is not consumed", () => {
|
||||
|
||||
Reference in New Issue
Block a user