diff --git a/packages/core/src/kilocode/global.ts b/packages/core/src/kilocode/global.ts index eb5b5d06b55..b57d06f7ae9 100644 --- a/packages/core/src/kilocode/global.ts +++ b/packages/core/src/kilocode/global.ts @@ -12,7 +12,10 @@ import fs from "fs/promises" */ export async function ensureRealDir(p: string) { await fs.mkdir(p, { recursive: true }) - const ok = await fs.stat(p).then(() => true).catch(() => false) + const ok = await fs + .stat(p) + .then(() => true) + .catch(() => false) if (!ok) { await fs.rm(p, { force: true }) await fs.mkdir(p, { recursive: true }) diff --git a/packages/kilo-docs/components/FlowDiagram/diagrams/wanted-lifecycle.ts b/packages/kilo-docs/components/FlowDiagram/diagrams/wanted-lifecycle.ts index fa94cff9ba0..54c7574bec2 100644 --- a/packages/kilo-docs/components/FlowDiagram/diagrams/wanted-lifecycle.ts +++ b/packages/kilo-docs/components/FlowDiagram/diagrams/wanted-lifecycle.ts @@ -134,7 +134,6 @@ const edges: Edge[] = [ type: "smoothstep", style: { strokeWidth: 2, stroke: "#888", strokeDasharray: "5 3" }, }, - ] export const wantedLifecycle: DiagramDefinition = { diff --git a/packages/kilo-docs/components/FlowDiagram/index.tsx b/packages/kilo-docs/components/FlowDiagram/index.tsx index dc1924c5838..d6b32190b8b 100644 --- a/packages/kilo-docs/components/FlowDiagram/index.tsx +++ b/packages/kilo-docs/components/FlowDiagram/index.tsx @@ -9,11 +9,7 @@ import { diagrams } from "./diagrams" * re-renders of FlowDiagram (otherwise React would unmount/remount it * on every parent render and tear down the ResizeObserver each time). */ -function FitOnResize({ - useReactFlow, -}: { - useReactFlow: typeof import("@xyflow/react").useReactFlow -}) { +function FitOnResize({ useReactFlow }: { useReactFlow: typeof import("@xyflow/react").useReactFlow }) { const { fitView } = useReactFlow() const containerRef = useRef(null) diff --git a/packages/kilo-docs/markdoc/partials/cli-commands-table.md b/packages/kilo-docs/markdoc/partials/cli-commands-table.md index 6243af9f0a8..e2ae23c4c5e 100644 --- a/packages/kilo-docs/markdoc/partials/cli-commands-table.md +++ b/packages/kilo-docs/markdoc/partials/cli-commands-table.md @@ -14,6 +14,7 @@ | `kilo uninstall` | uninstall kilo and remove all related files | | `kilo serve` | starts a headless kilo server | | `kilo models [provider]` | list all available models | +| `kilo roll-call ` | batch-test text models matching a filter for connectivity and latency | | `kilo stats` | show token usage and cost statistics | | `kilo export [sessionID]` | export session data as JSON | | `kilo import ` | import session data from JSON file or URL | diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md b/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md index 09c5ad62c8c..98096c72ae8 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md @@ -165,25 +165,25 @@ Positionals: message message to send [string] [default: []] Options: - --help Show help [boolean] - --version Show version number [boolean] - --command the command to run, use message for args [string] - -c, --continue continue the last session [boolean] - -s, --session session id to continue [string] - --fork fork the session before continuing (requires --continue or --session) [boolean] - --share share the session [boolean] - -m, --model model to use in the format of provider/model [string] - --agent agent to use [string] - --format format: default (formatted) or json (raw JSON events) [string] [choices: "default", "json"] [default: "default"] - -f, --file file(s) to attach to message [array] - --title title for the session (uses truncated prompt if no value provided) [string] - --attach attach to a running opencode server (e.g., http://localhost:4096) [string] - -p, --password basic auth password (defaults to KILO_SERVER_PASSWORD) [string] - --dir directory to run in, path on remote server if attaching [string] - --port port for the local server (defaults to random port if no value provided) [number] - --variant model variant (provider-specific reasoning effort, e.g., high, max, minimal) [string] - --thinking show thinking blocks [boolean] [default: false] - --auto auto-approve all permissions (for autonomous/pipeline usage) [boolean] [default: false] + --help Show help [boolean] + --version Show version number [boolean] + --command the command to run, use message for args [string] + -c, --continue continue the last session [boolean] + -s, --session session id to continue [string] + --fork fork the session before continuing (requires --continue or --session) [boolean] + --share share the session [boolean] + -m, --model model to use in the format of provider/model [string] + --agent agent to use [string] + --format format: default (formatted) or json (raw JSON events) [string] [choices: "default", "json"] [default: "default"] + -f, --file file(s) to attach to message [array] + --title title for the session (uses truncated prompt if no value provided) [string] + --attach attach to a running opencode server (e.g., http://localhost:4096) [string] + -p, --password basic auth password (defaults to KILO_SERVER_PASSWORD) [string] + --dir directory to run in, path on remote server if attaching [string] + --port port for the local server (defaults to random port if no value provided) [number] + --variant model variant (provider-specific reasoning effort, e.g., high, max, minimal) [string] + --thinking show thinking blocks [boolean] [default: false] + --auto auto-approve all permissions (for autonomous/pipeline usage) [boolean] [default: false] ``` ## kilo debug @@ -669,6 +669,25 @@ Options: --refresh refresh the models cache from models.dev [boolean] ``` +## kilo roll-call + +``` +batch-test text models matching a filter for connectivity and latency + +Positionals: + filter regex to filter models by provider/modelID (required) [string] + +Options: + --help Show help [boolean] + --version Show version number [boolean] + --prompt Prompt to send to each model [string] [default: "Hello"] + --timeout Timeout for each model call in milliseconds [number] [default: 25000] + --parallel Number of parallel model calls [number] [default: 5] + --verbose Show verbose output [boolean] [default: false] + --quiet Suppress progress and decoration [boolean] [default: false] + --output Output format (table, json, or md) [string] [choices: "table", "json", "md"] [default: "table"] +``` + ## kilo stats ``` diff --git a/packages/kilo-docs/source-links.md b/packages/kilo-docs/source-links.md index 7bfc0a61504..8802e2af684 100644 --- a/packages/kilo-docs/source-links.md +++ b/packages/kilo-docs/source-links.md @@ -1,7 +1,7 @@ # Source Code Links - + - @@ -30,6 +30,8 @@ - +- + - - @@ -83,6 +85,7 @@ - + - - @@ -177,6 +180,8 @@ - +- + - - diff --git a/packages/kilo-gateway/test/api/models.test.ts b/packages/kilo-gateway/test/api/models.test.ts index c9224687459..579f0621bc0 100644 --- a/packages/kilo-gateway/test/api/models.test.ts +++ b/packages/kilo-gateway/test/api/models.test.ts @@ -91,11 +91,12 @@ test("returns error with kind=http on non-auth HTTP error (e.g. 500)", async () test("returns models without error on success", async () => { const orig = globalThis.fetch - stubFetch(async () => - new Response(VALID_RESPONSE, { - status: 200, - headers: { "content-type": "application/json" }, - }), + stubFetch( + async () => + new Response(VALID_RESPONSE, { + status: 200, + headers: { "content-type": "application/json" }, + }), ) const result = await fetchKiloModels({}) @@ -108,11 +109,12 @@ test("returns models without error on success", async () => { test("returns error with kind=schema when response body is invalid JSON", async () => { const orig = globalThis.fetch - stubFetch(async () => - new Response("not valid json{{{{", { - status: 200, - headers: { "content-type": "application/json" }, - }), + stubFetch( + async () => + new Response("not valid json{{{{", { + status: 200, + headers: { "content-type": "application/json" }, + }), ) const result = await fetchKiloModels({}) diff --git a/packages/opencode/src/kilocode/cli/cmd/roll-call.ts b/packages/opencode/src/kilocode/cli/cmd/roll-call.ts index 318952f1e69..4275eb5f900 100644 --- a/packages/opencode/src/kilocode/cli/cmd/roll-call.ts +++ b/packages/opencode/src/kilocode/cli/cmd/roll-call.ts @@ -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> { +async function call( + model: Provider.Model, + prompt: string, + timeout: number, + start: number, +): Promise> { 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." } } diff --git a/packages/opencode/src/kilocode/components/model-info-panel.tsx b/packages/opencode/src/kilocode/components/model-info-panel.tsx index c228eb7e1f2..50a127e62de 100644 --- a/packages/opencode/src/kilocode/components/model-info-panel.tsx +++ b/packages/opencode/src/kilocode/components/model-info-panel.tsx @@ -59,10 +59,7 @@ export function ModelInfoPanel(props: Props) { gap={1} flexShrink={0} > - + {m().name ?? m().id ?? "Model"} @@ -141,7 +138,7 @@ export function ModelInfoPanel(props: Props) { - {" "} + {desc()} diff --git a/packages/opencode/src/kilocode/session/compaction-chunks.ts b/packages/opencode/src/kilocode/session/compaction-chunks.ts index 991ad718201..90b9968b1f7 100644 --- a/packages/opencode/src/kilocode/session/compaction-chunks.ts +++ b/packages/opencode/src/kilocode/session/compaction-chunks.ts @@ -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 [``, body || "[no content]", ""].join("\n") + const body = msg.parts.map(part).filter(Boolean).join("\n\n") + return [ + ``, + body || "[no content]", + "", + ].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({ diff --git a/packages/opencode/src/plugin/codex.ts b/packages/opencode/src/plugin/codex.ts index fc3701618ac..1e28dcfe813 100644 --- a/packages/opencode/src/plugin/codex.ts +++ b/packages/opencode/src/plugin/codex.ts @@ -440,7 +440,13 @@ export async function CodexAuthPlugin(input: PluginInput): Promise { 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 } diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts index 8ffb780aef9..9dd9d210d79 100644 --- a/packages/opencode/src/project/bootstrap.ts +++ b/packages/opencode/src/project/bootstrap.ts @@ -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 diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index 81811af3d39..05ba2775c7f 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -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, diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index df171c24ad6..4380f6067a1 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -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) { diff --git a/packages/opencode/test/kilocode/codex-auth-refresh.test.ts b/packages/opencode/test/kilocode/codex-auth-refresh.test.ts index 33d4ed7b3c3..28ea37c41ae 100644 --- a/packages/opencode/test/kilocode/codex-auth-refresh.test.ts +++ b/packages/opencode/test/kilocode/codex-auth-refresh.test.ts @@ -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.", }, }) }) diff --git a/packages/opencode/test/kilocode/encoding.test.ts b/packages/opencode/test/kilocode/encoding.test.ts index c7f55e58572..499305702b8 100644 --- a/packages/opencode/test/kilocode/encoding.test.ts +++ b/packages/opencode/test/kilocode/encoding.test.ts @@ -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") + }) + }) +}) diff --git a/packages/opencode/test/kilocode/provider-list-failed-state.test.ts b/packages/opencode/test/kilocode/provider-list-failed-state.test.ts index 13c53b246e5..e7f44ad2d7d 100644 --- a/packages/opencode/test/kilocode/provider-list-failed-state.test.ts +++ b/packages/opencode/test/kilocode/provider-list-failed-state.test.ts @@ -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") diff --git a/packages/opencode/test/kilocode/session-compaction-chunks.test.ts b/packages/opencode/test/kilocode/session-compaction-chunks.test.ts index e48d59f6c47..bfaa776b549 100644 --- a/packages/opencode/test/kilocode/session-compaction-chunks.test.ts +++ b/packages/opencode/test/kilocode/session-compaction-chunks.test.ts @@ -209,7 +209,11 @@ function runtime(layer: Layer.Layer, 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 { diff --git a/packages/opencode/test/kilocode/session/instruction-substitution.test.ts b/packages/opencode/test/kilocode/session/instruction-substitution.test.ts index abe9e777343..aed4f1f990d 100644 --- a/packages/opencode/test/kilocode/session/instruction-substitution.test.ts +++ b/packages/opencode/test/kilocode/session/instruction-substitution.test.ts @@ -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") diff --git a/packages/opencode/test/kilocode/util/url.test.ts b/packages/opencode/test/kilocode/util/url.test.ts index 876ce571bcc..0af1f37b36b 100644 --- a/packages/opencode/test/kilocode/util/url.test.ts +++ b/packages/opencode/test/kilocode/util/url.test.ts @@ -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", () => { diff --git a/packages/ui/src/components/provider-icons/sprite.svg b/packages/ui/src/components/provider-icons/sprite.svg index e72d2525207..7c848819997 100644 --- a/packages/ui/src/components/provider-icons/sprite.svg +++ b/packages/ui/src/components/provider-icons/sprite.svg @@ -579,6 +579,120 @@ d="M13.48 17.46L14.64 18.62C14.69 18.67 14.71 18.75 14.68 18.82L12.87 23.41C12.73 23.76 12.38 24 12 24C11.62 24 11.27 23.76 11.13 23.41L8.52 16.80L4.31 21.02C4.24 21.09 4.12 21.09 4.05 21.02L2.98 19.95C2.91 19.88 2.91 19.76 2.98 19.69L8.18 14.49C8.35 14.32 8.58 14.21 8.81 14.19C9.23 14.17 9.60 14.42 9.74 14.78L12.00 20.51L13.18 17.52C13.23 17.40 13.39 17.37 13.49 17.46H13.48ZM19.69 2.98L15.48 7.20L12.87 0.59C12.71 0.17 12.26 -0.08 11.79 0.02C11.48 0.09 11.23 0.33 11.12 0.63L9.32 5.18C9.29 5.25 9.31 5.33 9.36 5.38L10.52 6.54C10.61 6.63 10.77 6.60 10.82 6.47L12 3.49L14.26 9.21C14.37 9.51 14.63 9.72 14.94 9.79C15.00 9.80 15.07 9.81 15.13 9.81C15.38 9.81 15.62 9.71 15.79 9.53L21.02 4.31C21.09 4.24 21.09 4.12 21.02 4.04L19.96 2.98C19.88 2.91 19.76 2.91 19.69 2.98L19.69 2.98ZM6.47 13.17L3.49 12.00L9.21 9.74C9.58 9.59 9.83 9.23 9.81 8.81C9.79 8.57 9.68 8.35 9.51 8.18L4.31 2.98C4.24 2.91 4.12 2.91 4.05 2.98L2.98 4.05C2.91 4.12 2.91 4.24 2.98 4.31L7.20 8.52L0.59 11.13C0.24 11.27 0 11.62 0 12.00C0 12.38 0.24 12.73 0.59 12.87L5.18 14.68C5.25 14.71 5.33 14.69 5.38 14.64L6.54 13.48C6.64 13.39 6.60 13.23 6.48 13.17H6.47ZM23.41 11.13L18.82 9.32C18.75 9.29 18.67 9.31 18.62 9.36L17.46 10.52C17.36 10.61 17.40 10.77 17.52 10.82L20.51 12.00L14.78 14.26C14.42 14.40 14.17 14.77 14.19 15.19C14.21 15.42 14.32 15.65 14.49 15.82L19.69 21.02C19.76 21.09 19.88 21.09 19.95 21.02L21.02 19.95C21.09 19.88 21.09 19.76 21.02 19.69L16.80 15.48L23.41 12.87C23.76 12.73 24 12.38 24 12.00C24 11.62 23.76 11.27 23.41 11.13V11.13Z" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { const ours = { scripts: { "dev-setup": "kilo dev-setup", - "postinstall": "bun run --cwd packages/opencode fix-node-pty && bun run script/setup-git.ts", - "extension": "bun --cwd packages/kilo-vscode script/launch.ts", + postinstall: "bun run --cwd packages/opencode fix-node-pty && bun run script/setup-git.ts", + extension: "bun --cwd packages/kilo-vscode script/launch.ts", }, } const pkg: Record = { @@ -25,7 +25,7 @@ test("fixScripts preserves Kilo-only root scripts from base", () => { test("fixScripts removes upstream-only dead scripts from root", () => { const pkg: Record = { scripts: { - "dev": "bun run --cwd packages/opencode src/index.ts", + dev: "bun run --cwd packages/opencode src/index.ts", "dev:desktop": "bun --cwd packages/desktop-electron dev", "dev:web": "bun --cwd packages/app dev", "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev", diff --git a/script/upstream/transforms/transform-package-json.ts b/script/upstream/transforms/transform-package-json.ts index 501786e1453..e65432cd219 100644 --- a/script/upstream/transforms/transform-package-json.ts +++ b/script/upstream/transforms/transform-package-json.ts @@ -247,7 +247,12 @@ const DELETE_UPSTREAM_CATALOG: Record = { * Re-apply Kilo-specific scripts on top of the upstream-shaped scripts block, * and prune upstream-only scripts that target packages Kilo doesn't ship. */ -export function fixScripts(pkg: Record, path: string, ours: Record | null, changes: string[]): void { +export function fixScripts( + pkg: Record, + path: string, + ours: Record | null, + changes: string[], +): void { const theirs = (pkg.scripts as Record | undefined) || {} const oursScripts = (ours?.scripts as Record | undefined) || {}