From 6b074b694812c73229816cf2443de3d9b7344ed2 Mon Sep 17 00:00:00 2001 From: cmanu Date: Thu, 23 Jul 2026 10:47:19 -0700 Subject: [PATCH 01/25] feat(charts): Added charting capabilities to kilo-ui storybook and VS code ext --- bun.lock | 5 + packages/kilo-ui/package.json | 4 +- packages/kilo-ui/src/components/chart.css | 14 ++ packages/kilo-ui/src/components/chart.tsx | 133 ++++++++++++++++ .../kilo-ui/src/components/message-part.tsx | 6 + .../src/stories/tool-call-lab.stories.tsx | 150 ++++++++++++++++++ packages/opencode/src/kilocode/soul.txt | 82 ++++++++++ packages/opencode/src/kilocode/tool/chart.ts | 56 +++++++ .../opencode/src/kilocode/tool/registry.ts | 11 +- ...l-registry-indexing-import-failure.test.ts | 1 + .../kilocode/tool-registry-indexing.test.ts | 10 ++ ...l-registry-semantic-import-failure.test.ts | 1 + 12 files changed, 469 insertions(+), 4 deletions(-) create mode 100644 packages/kilo-ui/src/components/chart.css create mode 100644 packages/kilo-ui/src/components/chart.tsx create mode 100644 packages/opencode/src/kilocode/tool/chart.ts diff --git a/bun.lock b/bun.lock index d87df9a65a3..ac91ac4f1a1 100644 --- a/bun.lock +++ b/bun.lock @@ -362,6 +362,7 @@ "@solid-primitives/media": "2.3.3", "@solid-primitives/resize-observer": "2.1.5", "@solid-primitives/rootless": "1.5.2", + "chart.js": "4.5.1", "diff": "catalog:", "lucide-solid": "0.576.0", "motion": "12.34.5", @@ -1553,6 +1554,8 @@ "@kobalte/utils": ["@kobalte/utils@0.9.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.2.14", "@solid-primitives/keyed": "^1.2.0", "@solid-primitives/map": "^0.4.7", "@solid-primitives/media": "^2.2.4", "@solid-primitives/props": "^3.1.8", "@solid-primitives/refs": "^1.0.5", "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { "solid-js": "^1.8.8" } }, "sha512-eeU60A3kprIiBDAfv9gUJX1tXGLuZiKMajUfSQURAF2pk4ZoMYiqIzmrMBvzcxP39xnYttgTyQEVLwiTZnrV4w=="], + "@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="], + "@kwsites/file-exists": ["@kwsites/file-exists@1.1.1", "", { "dependencies": { "debug": "^4.1.1" } }, "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw=="], "@kwsites/promise-deferred": ["@kwsites/promise-deferred@1.1.1", "", {}, "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="], @@ -2855,6 +2858,8 @@ "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], + "chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="], + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], diff --git a/packages/kilo-ui/package.json b/packages/kilo-ui/package.json index f79ab1a4ecc..68b6edeea73 100644 --- a/packages/kilo-ui/package.json +++ b/packages/kilo-ui/package.json @@ -6,6 +6,7 @@ "exports": { "./font": "./src/components/font.tsx", "./button": "./src/components/button.tsx", + "./chart": "./src/components/chart.tsx", "./code": "./src/components/code.tsx", "./diff": "./src/components/diff.tsx", "./diff-ssr": "./src/components/diff-ssr.tsx", @@ -127,6 +128,7 @@ "motion": "12.34.5", "motion-dom": "12.34.3", "motion-utils": "12.29.2", - "strip-ansi": "7.1.2" + "strip-ansi": "7.1.2", + "chart.js": "4.5.1" } } diff --git a/packages/kilo-ui/src/components/chart.css b/packages/kilo-ui/src/components/chart.css new file mode 100644 index 00000000000..c9752f9a0e6 --- /dev/null +++ b/packages/kilo-ui/src/components/chart.css @@ -0,0 +1,14 @@ +[data-component="chart-container"] { + padding: 12px; +} + +[data-slot="chart-render"] { + width: 100%; + max-height: 300px; +} + +[data-slot="chart-error"] { + padding: 12px; + color: var(--text-weak); + font-size: var(--kilo-font-size-12, 12px); +} diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx new file mode 100644 index 00000000000..f8ec69cd370 --- /dev/null +++ b/packages/kilo-ui/src/components/chart.tsx @@ -0,0 +1,133 @@ +/** @jsxImportSource solid-js */ +import { createSignal, onCleanup, onMount } from "solid-js" +import { BasicTool } from "./basic-tool" +import type { ToolProps } from "./message-part" + +function getThemeColors() { + const style = getComputedStyle(document.documentElement) + const get = (v: string, fallback: string) => style.getPropertyValue(v).trim() || fallback + return { + text: get("--text-base", "#FAFAFA"), + textWeak: get("--text-weak", "#A3A3A3"), + border: get("--border-weak-base", "#FFFFFF1A"), + surface: get("--surface-raised-base", "#202020"), + series: ["#3B82F6", "#00BAA9", "#22C55E", "#F97316", "#A855F7", "#EF4444"], + } +} + +type ChartConfig = { + type: string + data: { + labels?: string[] + datasets: { + label?: string + data: number[] | { x: number | string; y: number }[] + backgroundColor?: string | string[] + borderColor?: string | string[] + [key: string]: unknown + }[] + } + options?: Record +} + +export function ChartTool(props: ToolProps) { + let canvas: HTMLCanvasElement | undefined + const [error, setError] = createSignal() + + onMount(async () => { + if (!canvas) return + + let config: ChartConfig + try { + config = JSON.parse(props.output ?? "{}") + } catch { + setError("Could not parse chart config") + return + } + + if (!config || typeof config !== "object" || !config.type || !config.data) { + setError("Invalid chart config — must include type and data") + return + } + + try { + const { Chart, registerables } = await import("chart.js") + Chart.register(...registerables) + + const colors = getThemeColors() + + const isPolar = config.type === "pie" || config.type === "doughnut" || config.type === "polarArea" + + // apply kilo series colors to datasets that don't have their own colors + config.data.datasets = config.data.datasets.map((dataset, i) => { + if (dataset.backgroundColor) return dataset + // pie/doughnut need one color per slice + if (isPolar) { + const data = dataset.data as unknown[] + return { + ...dataset, + backgroundColor: data.map((_, j) => colors.series[j % colors.series.length]), + borderColor: colors.series.map(c => c), + } + } + return { + backgroundColor: colors.series[i % colors.series.length], + borderColor: colors.series[i % colors.series.length], + ...dataset, + } + }) + + const chart = new Chart(canvas!, { + type: config.type as any, + data: config.data as any, + options: { + responsive: true, + maintainAspectRatio: true, + plugins: { + legend: { + labels: { color: colors.textWeak }, + }, + }, + scales: { + x: { + ticks: { color: colors.textWeak }, + grid: { color: colors.border }, + border: { color: colors.border }, + }, + y: { + ticks: { color: colors.textWeak }, + grid: { color: colors.border }, + border: { color: colors.border }, + }, + }, + ...config.options, + }, + }) + + onCleanup(() => chart.destroy()) + } catch (e) { + setError(e instanceof Error ? e.message : "Failed to render chart") + } + }) + + return ( + +
+ {error() ? ( +
{error()}
+ ) : ( + + )} +
+
+ ) +} diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx index 131874b5649..1bbc8443c49 100644 --- a/packages/kilo-ui/src/components/message-part.tsx +++ b/packages/kilo-ui/src/components/message-part.tsx @@ -3014,3 +3014,9 @@ ToolRegistry.register({ ) }, }) + +import { ChartTool } from "./chart" +ToolRegistry.register({ + name: "chart", + render: ChartTool, +}) diff --git a/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx index 77248db697f..b49354f2d0b 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx @@ -1227,6 +1227,156 @@ export const SearchPreviews: Story = { +
+
+ Chart renderers + + Chart.js chart tool rendered with production ChartTool component. + +
+
+
+ Bar chart + +
+
+ Line plot + +
+
+ Scatter plot + +
+
+ Temporal (time series) + +
+
+ Pie chart + +
+
+ Doughnut chart + +
+
+ Radar chart + +
+
+ Bubble chart + +
+
+
diff --git a/packages/opencode/src/kilocode/soul.txt b/packages/opencode/src/kilocode/soul.txt index fec5f23985c..93060aac172 100644 --- a/packages/opencode/src/kilocode/soul.txt +++ b/packages/opencode/src/kilocode/soul.txt @@ -13,3 +13,85 @@ You are Kilo, a highly skilled software engineer with extensive knowledge in man # Code - When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices. + +# Data Visualization + +The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response. + +Use the `chart` tool when: +- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.) +- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions) + +Use mermaid fenced code blocks (` ```mermaid `) when: +- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram +- Visualizing relationships, processes, or structure — not data values + +Mermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed. + +Do not use either for: code, text, or data that is already clear in prose or table form. + +The `chart` tool input accepts: +- `title` (string) — short label shown in the tool header +- `description` (string, optional) — subtitle shown below the title +- `spec` (string) — a Chart.js config object as a JSON string + +The `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples: + +Bar chart: +```json +{ + "type": "bar", + "data": { + "labels": ["A", "B", "C"], + "datasets": [{ "label": "Value", "data": [10, 20, 15] }] + } +} +``` + +Line chart: +```json +{ + "type": "line", + "data": { + "labels": ["Jan", "Feb", "Mar", "Apr"], + "datasets": [{ "label": "Value", "data": [10, 28, 19, 45], "fill": false }] + } +} +``` + +Scatter plot: +```json +{ + "type": "scatter", + "data": { + "datasets": [{ + "label": "Points", + "data": [{ "x": 1, "y": 5 }, { "x": 2, "y": 8 }, { "x": 3, "y": 3 }] + }] + } +} +``` + +Time series: +```json +{ + "type": "line", + "data": { + "labels": ["2024-01", "2024-02", "2024-03", "2024-04"], + "datasets": [{ "label": "Value", "data": [120, 145, 132, 178], "fill": true }] + } +} +``` + +Pie chart: +```json +{ + "type": "pie", + "data": { + "labels": ["A", "B", "C"], + "datasets": [{ "data": [30, 50, 20] }] + } +} +``` + +You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height. diff --git a/packages/opencode/src/kilocode/tool/chart.ts b/packages/opencode/src/kilocode/tool/chart.ts new file mode 100644 index 00000000000..a40530c16ec --- /dev/null +++ b/packages/opencode/src/kilocode/tool/chart.ts @@ -0,0 +1,56 @@ +// kilocode_change - new file +import { Effect, Schema } from "effect" +import * as Tool from "../../tool/tool" + +const Parameters = Schema.Struct({ + title: Schema.String.annotate({ + description: "Short label for the chart shown in the tool header", + }), + description: Schema.optional(Schema.String).annotate({ + description: "Optional subtitle shown below the title", + }), + spec: Schema.String.annotate({ + description: "A valid Vega-Lite v6 JSON specification string describing the chart to render", + }), +}) + +type Meta = { + title: string + description?: string + error?: string +} + +export const ChartTool = Tool.define( + "chart", + Effect.gen(function* () { + return { + description: + "Render a data visualization chart using a Vega-Lite v6 JSON spec. Use this when the user asks to visualize data as a chart, graph, or plot. The spec is rendered inline in the session as an interactive SVG chart.", + parameters: Parameters, + execute: (params: Schema.Schema.Type, ctx: Tool.Context) => + Effect.gen(function* () { + yield* ctx.metadata({ + title: params.title, + metadata: { title: params.title, description: params.description } as Meta, + }) + + let spec: unknown + try { + spec = JSON.parse(params.spec) + } catch { + return { + title: params.title, + output: `Invalid chart spec: could not parse JSON. Please provide a valid Vega-Lite v6 JSON string.`, + metadata: { title: params.title, description: params.description, error: "invalid-json" } as Meta, + } + } + + return { + title: params.title, + output: JSON.stringify(spec), + metadata: { title: params.title, description: params.description } as Meta, + } + }).pipe(Effect.orDie), + } + }), +) diff --git a/packages/opencode/src/kilocode/tool/registry.ts b/packages/opencode/src/kilocode/tool/registry.ts index 0a366cb1bd9..a273c768de7 100644 --- a/packages/opencode/src/kilocode/tool/registry.ts +++ b/packages/opencode/src/kilocode/tool/registry.ts @@ -4,6 +4,7 @@ import { RecallTool } from "../../tool/recall" import { AgentManagerModelsTool } from "./agent-manager-models" import { AgentManagerTool } from "./agent-manager" import { BackgroundProcessTool } from "./background-process" +import { ChartTool } from "./chart" import { GenerateImageTool } from "./generate-image" import { InteractiveTerminalTool } from "./interactive-terminal" import { NotebookEditTool, NotebookExecuteTool, NotebookReadTool } from "./notebook-host" @@ -72,15 +73,16 @@ export namespace KiloToolRegistry { const save = yield* MemorySaveTool const manager = yield* AgentManagerTool.pipe(Effect.provideService(AgentManager.Service, host ?? unavailable)) const process = yield* BackgroundProcessTool + const chart = yield* ChartTool const image = yield* GenerateImageTool const terminal = yield* InteractiveTerminalTool - if (!notebook) return { codebase, recall, managerModels, memory, save, manager, process, image, terminal } + if (!notebook) return { codebase, recall, managerModels, memory, save, manager, process, chart, image, terminal } const tools = yield* Effect.all({ notebookRead: NotebookReadTool, notebookEdit: NotebookEditTool, notebookExecute: NotebookExecuteTool, }).pipe(Effect.provideService(Notebook.Service, notebook)) - return { codebase, recall, managerModels, memory, save, manager, process, image, terminal, ...tools } + return { codebase, recall, managerModels, memory, save, manager, process, chart, image, terminal, ...tools } }) } @@ -95,6 +97,7 @@ export namespace KiloToolRegistry { save: Tool.Info manager: Tool.Info process: Tool.Info + chart: Tool.Info image: Tool.Info terminal?: Tool.Info notebookRead?: Tool.Info @@ -113,6 +116,7 @@ export namespace KiloToolRegistry { save: Tool.init(tools.save), manager: Tool.init(tools.manager), process: Tool.init(tools.process), + chart: Tool.init(tools.chart), image: Tool.init(tools.image), }) const terminal = tools.terminal ? yield* Tool.init(tools.terminal) : undefined @@ -183,6 +187,7 @@ export namespace KiloToolRegistry { save: Tool.Def manager: Tool.Def process: Tool.Def + chart: Tool.Def image: Tool.Def terminal?: Tool.Def notebookRead?: Tool.Def @@ -198,9 +203,9 @@ export namespace KiloToolRegistry { tools.memory, tools.save, tools.recall, + tools.chart, ...(Flag.KILO_CLIENT === "cli" || Flag.KILO_CLIENT === "vscode" ? [tools.process] : []), ...(Flag.KILO_CLIENT === "cli" && tools.terminal ? [tools.terminal] : []), - // Agent Manager tools are useful only when the extension can create and display their sessions. ...(Flag.KILO_CLIENT === "vscode" ? [tools.managerModels, tools.manager] : []), ...(Flag.KILO_CLIENT === "vscode" && cfg.experimental?.native_notebook_tools === true && diff --git a/packages/opencode/test/kilocode/tool-registry-indexing-import-failure.test.ts b/packages/opencode/test/kilocode/tool-registry-indexing-import-failure.test.ts index 44488ea1133..6f8a0301cd5 100644 --- a/packages/opencode/test/kilocode/tool-registry-indexing-import-failure.test.ts +++ b/packages/opencode/test/kilocode/tool-registry-indexing-import-failure.test.ts @@ -42,6 +42,7 @@ function infos() { save: info("kilo_memory_save"), manager: info("agent_manager"), process: info("background_process"), + chart: info("chart"), image: info("generate_image"), notebookRead: info("notebook_read"), notebookEdit: info("notebook_edit"), diff --git a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts index d38f709b1cb..e55d6fd521c 100644 --- a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts +++ b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts @@ -339,6 +339,7 @@ describe("kilocode tool registry indexing", () => { save: def("kilo_memory_save"), manager: def("agent_manager"), process: def("background_process"), + chart: def("chart"), image: def("generate_image"), terminal: def("interactive_terminal"), notebookRead: def("notebook_read"), @@ -353,6 +354,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", "background_process", "interactive_terminal", ]) @@ -363,6 +365,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", "background_process", "interactive_terminal", ], @@ -378,6 +381,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", "background_process", "interactive_terminal", ]) @@ -390,6 +394,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", "background_process", "agent_manager_models", "agent_manager", @@ -405,6 +410,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", "background_process", "agent_manager_models", "agent_manager", @@ -416,6 +422,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", "background_process", "agent_manager_models", "agent_manager", @@ -427,6 +434,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", ]) process.env["KILO_CLIENT"] = "run" @@ -435,6 +443,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", ]) process.env["KILO_CLIENT"] = "acp" @@ -443,6 +452,7 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", + "chart", ]) } finally { if (prev === undefined) delete process.env["KILO_CLIENT"] diff --git a/packages/opencode/test/kilocode/tool-registry-semantic-import-failure.test.ts b/packages/opencode/test/kilocode/tool-registry-semantic-import-failure.test.ts index 19a824de518..f85a5c8a02b 100644 --- a/packages/opencode/test/kilocode/tool-registry-semantic-import-failure.test.ts +++ b/packages/opencode/test/kilocode/tool-registry-semantic-import-failure.test.ts @@ -54,6 +54,7 @@ function infos() { save: info("kilo_memory_save"), manager: info("agent_manager"), process: info("background_process"), + chart: info("chart"), image: info("generate_image"), notebookRead: info("notebook_read"), notebookEdit: info("notebook_edit"), From 15978b2c51f646c96ddb2be77314043579751dd8 Mon Sep 17 00:00:00 2001 From: cmanu Date: Fri, 24 Jul 2026 13:15:43 -0700 Subject: [PATCH 02/25] feat(charts): update description --- packages/opencode/src/kilocode/tool/chart.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/kilocode/tool/chart.ts b/packages/opencode/src/kilocode/tool/chart.ts index a40530c16ec..4ba6ba9c9c3 100644 --- a/packages/opencode/src/kilocode/tool/chart.ts +++ b/packages/opencode/src/kilocode/tool/chart.ts @@ -10,7 +10,7 @@ const Parameters = Schema.Struct({ description: "Optional subtitle shown below the title", }), spec: Schema.String.annotate({ - description: "A valid Vega-Lite v6 JSON specification string describing the chart to render", + description: "A valid Chart.js v4 JSON configuration string describing the chart to render", }), }) @@ -25,7 +25,7 @@ export const ChartTool = Tool.define( Effect.gen(function* () { return { description: - "Render a data visualization chart using a Vega-Lite v6 JSON spec. Use this when the user asks to visualize data as a chart, graph, or plot. The spec is rendered inline in the session as an interactive SVG chart.", + "Render a data visualization chart using a Chart.js v4 config. Use this when the user asks to visualize data as a chart, graph, or plot. The config is rendered inline in the session.", parameters: Parameters, execute: (params: Schema.Schema.Type, ctx: Tool.Context) => Effect.gen(function* () { @@ -40,7 +40,7 @@ export const ChartTool = Tool.define( } catch { return { title: params.title, - output: `Invalid chart spec: could not parse JSON. Please provide a valid Vega-Lite v6 JSON string.`, + output: `Invalid chart spec: could not parse JSON. Please provide a valid Chart.js v4 JSON string.`, metadata: { title: params.title, description: params.description, error: "invalid-json" } as Meta, } } From 86ae68ebf0666185fc3e5a96a91f8e3a2a7174df Mon Sep 17 00:00:00 2001 From: "kilo-maintainer[bot]" Date: Fri, 24 Jul 2026 23:26:35 +0000 Subject: [PATCH 03/25] chore: update kilo-vscode visual regression baselines --- .../labs-tool-call-lab/search-previews-chromium-linux.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png index c64a23bfc5f..207fc838fd7 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b1eb6cf15d5c2dc66c769a91fcf09bcf25bc2e8d17696ad0e9ab4181ed3038c -size 658647 +oid sha256:353669eb1f2902306221eebc7a0186e9517e586f8d5e5b6c5ba60196622810ec +size 766293 From 8b26aca387367bdbbde2c30d5f70063c71c2ddb2 Mon Sep 17 00:00:00 2001 From: cmanu Date: Fri, 24 Jul 2026 16:31:52 -0700 Subject: [PATCH 04/25] fix(charts): format to make prettier --- packages/kilo-ui/src/components/chart.tsx | 8 +- .../src/stories/tool-call-lab.stories.tsx | 307 +++++++++++------- 2 files changed, 193 insertions(+), 122 deletions(-) diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index f8ec69cd370..df57daeec9d 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -67,7 +67,7 @@ export function ChartTool(props: ToolProps) { return { ...dataset, backgroundColor: data.map((_, j) => colors.series[j % colors.series.length]), - borderColor: colors.series.map(c => c), + borderColor: colors.series.map((c) => c), } } return { @@ -122,11 +122,7 @@ export function ChartTool(props: ToolProps) { defaultOpen={props.defaultOpen ?? true} >
- {error() ? ( -
{error()}
- ) : ( - - )} + {error() ?
{error()}
: }
) diff --git a/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx index b49354f2d0b..b5f8d714d1d 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx @@ -1237,143 +1237,218 @@ export const SearchPreviews: Story = {
Bar chart - +
Line plot - +
Scatter plot - +
Temporal (time series) - +
Pie chart - +
Doughnut chart - +
Radar chart - +
Bubble chart - +
From 2fefaa888cd9e3e618c464c502672adbf0526ac2 Mon Sep 17 00:00:00 2001 From: cmanu Date: Mon, 27 Jul 2026 08:22:07 -0700 Subject: [PATCH 05/25] fix(charts): Cassettes re-recorded --- .../kilocode/session/native-anthropic-tool-loop.json | 6 +++--- .../kilocode/session/native-openai-oauth-tool-loop.json | 6 +++--- .../recordings/kilocode/session/native-zen-tool-loop.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json index f7159ddf26b..4d2f8e25d88 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json @@ -21,7 +21,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"input_schema\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"max_tokens\":32000,\"temperature\":0}" + "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" }, "response": { "status": 200, @@ -39,7 +39,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\",\"cache_control\":{\"type\":\"ephemeral\"}}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01A8pEqifk2HVQfq1ZDNP6iY\",\"name\":\"get_weather\",\"input\":{\"city\":{}}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_01A8pEqifk2HVQfq1ZDNP6iY\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"input_schema\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"max_tokens\":32000,\"temperature\":0}" + "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}, {\"role\": \"assistant\", \"content\": [{\"type\": \"tool_use\", \"id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"name\": \"get_weather\", \"input\": {\"city\": {}}}]}, {\"role\": \"user\", \"content\": [{\"type\": \"tool_result\", \"tool_use_id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"content\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" }, "response": { "status": 200, @@ -50,4 +50,4 @@ } } ] -} +} \ No newline at end of file diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json index 5f966e73aee..d3d7e932527 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json @@ -22,7 +22,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"instructions\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-openai-oauth-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -38,7 +38,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"function_call\",\"call_id\":\"call_uoEsDnHNhxMLpCUy6hqEyHme\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_uoEsDnHNhxMLpCUy6hqEyHme\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-openai-oauth-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -47,4 +47,4 @@ } } ] -} +} \ No newline at end of file diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json index cf535b73a05..736f922bc8e 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json @@ -22,7 +22,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.2-codex\",\"input\":[{\"role\":\"system\",\"content\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-opencode-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"max_output_tokens\":32000,\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -40,7 +40,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.2-codex\",\"input\":[{\"role\":\"system\",\"content\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"function_call\",\"call_id\":\"call_DfI0RwTrlaizfnQ9zkJC8rks\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_DfI0RwTrlaizfnQ9zkJC8rks\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-opencode-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"max_output_tokens\":32000,\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -51,4 +51,4 @@ } } ] -} +} \ No newline at end of file From 2a0d94f86c2b2d11c3ade2c72fda7f5d4a98c45b Mon Sep 17 00:00:00 2001 From: cmanu Date: Mon, 27 Jul 2026 10:52:06 -0700 Subject: [PATCH 06/25] fix(charts): Address kilo code bot cmts: Move soul.txt to system prompt; Add unit tests for vs code only feature. --- packages/kilo-ui/src/components/chart.tsx | 2 +- packages/kilo-ui/src/styles/index.css | 1 + packages/opencode/src/kilocode/soul.txt | 82 ----------------- .../opencode/src/kilocode/system-prompt.ts | 91 ++++++++++++++++++- .../opencode/src/kilocode/tool/registry.ts | 2 +- .../session/native-anthropic-tool-loop.json | 4 +- .../native-openai-oauth-tool-loop.json | 4 +- .../session/native-zen-tool-loop.json | 4 +- .../test/kilocode/chart-tool-gating.test.ts | 42 +++++++++ 9 files changed, 141 insertions(+), 91 deletions(-) create mode 100644 packages/opencode/test/kilocode/chart-tool-gating.test.ts diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index df57daeec9d..a100f64e36c 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -67,7 +67,7 @@ export function ChartTool(props: ToolProps) { return { ...dataset, backgroundColor: data.map((_, j) => colors.series[j % colors.series.length]), - borderColor: colors.series.map((c) => c), + borderColor: data.map((_, j) => colors.series[j % colors.series.length]), } } return { diff --git a/packages/kilo-ui/src/styles/index.css b/packages/kilo-ui/src/styles/index.css index c325eb00628..e972bb85e2f 100644 --- a/packages/kilo-ui/src/styles/index.css +++ b/packages/kilo-ui/src/styles/index.css @@ -10,6 +10,7 @@ @import "../components/auto-approve-bar.css"; @import "../components/button.css"; @import "../components/card.css"; +@import "../components/chart.css"; @import "../components/chat-input.css"; @import "../components/checkbox.css"; @import "../components/code.css"; diff --git a/packages/opencode/src/kilocode/soul.txt b/packages/opencode/src/kilocode/soul.txt index 93060aac172..fec5f23985c 100644 --- a/packages/opencode/src/kilocode/soul.txt +++ b/packages/opencode/src/kilocode/soul.txt @@ -13,85 +13,3 @@ You are Kilo, a highly skilled software engineer with extensive knowledge in man # Code - When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices. - -# Data Visualization - -The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response. - -Use the `chart` tool when: -- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.) -- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions) - -Use mermaid fenced code blocks (` ```mermaid `) when: -- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram -- Visualizing relationships, processes, or structure — not data values - -Mermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed. - -Do not use either for: code, text, or data that is already clear in prose or table form. - -The `chart` tool input accepts: -- `title` (string) — short label shown in the tool header -- `description` (string, optional) — subtitle shown below the title -- `spec` (string) — a Chart.js config object as a JSON string - -The `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples: - -Bar chart: -```json -{ - "type": "bar", - "data": { - "labels": ["A", "B", "C"], - "datasets": [{ "label": "Value", "data": [10, 20, 15] }] - } -} -``` - -Line chart: -```json -{ - "type": "line", - "data": { - "labels": ["Jan", "Feb", "Mar", "Apr"], - "datasets": [{ "label": "Value", "data": [10, 28, 19, 45], "fill": false }] - } -} -``` - -Scatter plot: -```json -{ - "type": "scatter", - "data": { - "datasets": [{ - "label": "Points", - "data": [{ "x": 1, "y": 5 }, { "x": 2, "y": 8 }, { "x": 3, "y": 3 }] - }] - } -} -``` - -Time series: -```json -{ - "type": "line", - "data": { - "labels": ["2024-01", "2024-02", "2024-03", "2024-04"], - "datasets": [{ "label": "Value", "data": [120, 145, 132, 178], "fill": true }] - } -} -``` - -Pie chart: -```json -{ - "type": "pie", - "data": { - "labels": ["A", "B", "C"], - "datasets": [{ "data": [30, 50, 20] }] - } -} -``` - -You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height. diff --git a/packages/opencode/src/kilocode/system-prompt.ts b/packages/opencode/src/kilocode/system-prompt.ts index 9e2e393471c..4c0d0c88ecf 100644 --- a/packages/opencode/src/kilocode/system-prompt.ts +++ b/packages/opencode/src/kilocode/system-prompt.ts @@ -1,6 +1,7 @@ // kilocode_change - new file import { Global } from "@opencode-ai/core/global" +import { Flag } from "@opencode-ai/core/flag/flag" import { Effect } from "effect" import { staticEnvLines, type EditorContext } from "@/kilocode/editor-context" import { KiloMemory } from "@kilocode/kilo-memory/effect" @@ -13,6 +14,93 @@ import * as Log from "@opencode-ai/core/util/log" const log = Log.create({ service: "kilocode.system-prompt" }) export namespace KilocodeSystemPrompt { + export function chartInstructions(): string | undefined { + if (Flag.KILO_CLIENT !== "vscode") return undefined + return [ + "# Data Visualization", + "", + "The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.", + "", + "Use the `chart` tool when:", + "- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)", + "- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)", + "", + "Use mermaid fenced code blocks (` ```mermaid `) when:", + "- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram", + "- Visualizing relationships, processes, or structure — not data values", + "", + "Mermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.", + "", + "Do not use either for: code, text, or data that is already clear in prose or table form.", + "", + "The `chart` tool input accepts:", + "- `title` (string) — short label shown in the tool header", + "- `description` (string, optional) — subtitle shown below the title", + "- `spec` (string) — a Chart.js config object as a JSON string", + "", + "The `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:", + "", + "Bar chart:", + "```json", + '{', + ' "type": "bar",', + ' "data": {', + ' "labels": ["A", "B", "C"],', + ' "datasets": [{ "label": "Value", "data": [10, 20, 15] }]', + ' }', + '}', + "```", + "", + "Line chart:", + "```json", + '{', + ' "type": "line",', + ' "data": {', + ' "labels": ["Jan", "Feb", "Mar", "Apr"],', + ' "datasets": [{ "label": "Value", "data": [10, 28, 19, 45], "fill": false }]', + ' }', + '}', + "```", + "", + "Scatter plot:", + "```json", + '{', + ' "type": "scatter",', + ' "data": {', + ' "datasets": [{', + ' "label": "Points",', + ' "data": [{ "x": 1, "y": 5 }, { "x": 2, "y": 8 }, { "x": 3, "y": 3 }]', + ' }]', + ' }', + '}', + "```", + "", + "Time series:", + "```json", + '{', + ' "type": "line",', + ' "data": {', + ' "labels": ["2024-01", "2024-02", "2024-03", "2024-04"],', + ' "datasets": [{ "label": "Value", "data": [120, 145, 132, 178], "fill": true }]', + ' }', + '}', + "```", + "", + "Pie chart:", + "```json", + '{', + ' "type": "pie",', + ' "data": {', + ' "labels": ["A", "B", "C"],', + ' "datasets": [{ "data": [30, 50, 20] }]', + ' }', + '}', + "```", + "", + "You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.", + ].join("\n") + } + export function environment(input: { ctx: InstanceContext; model: Provider.Model; editor?: EditorContext }) { return [ [ @@ -27,7 +115,8 @@ export namespace KilocodeSystemPrompt { ...staticEnvLines(input.editor), ``, ].join("\n"), - ] + chartInstructions(), + ].filter((x): x is string => x !== undefined) } export function memoryBlocks(input: { diff --git a/packages/opencode/src/kilocode/tool/registry.ts b/packages/opencode/src/kilocode/tool/registry.ts index 09f0bcf5478..d7ed5be757d 100644 --- a/packages/opencode/src/kilocode/tool/registry.ts +++ b/packages/opencode/src/kilocode/tool/registry.ts @@ -214,7 +214,7 @@ export namespace KiloToolRegistry { tools.memory, tools.save, tools.recall, - tools.chart, + ...(Flag.KILO_CLIENT === "vscode" ? [tools.chart] : []), ...(Flag.KILO_CLIENT === "cli" || Flag.KILO_CLIENT === "vscode" ? [tools.process] : []), ...(Flag.KILO_CLIENT === "cli" && tools.terminal ? [tools.terminal] : []), ...(Flag.KILO_CLIENT === "vscode" ? [tools.managerModels, tools.manager] : []), diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json index 4d2f8e25d88..dc52ade7b54 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json @@ -21,7 +21,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" + "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" }, "response": { "status": 200, @@ -39,7 +39,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}, {\"role\": \"assistant\", \"content\": [{\"type\": \"tool_use\", \"id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"name\": \"get_weather\", \"input\": {\"city\": {}}}]}, {\"role\": \"user\", \"content\": [{\"type\": \"tool_result\", \"tool_use_id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"content\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" + "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}, {\"role\": \"assistant\", \"content\": [{\"type\": \"tool_use\", \"id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"name\": \"get_weather\", \"input\": {\"city\": {}}}]}, {\"role\": \"user\", \"content\": [{\"type\": \"tool_result\", \"tool_use_id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"content\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" }, "response": { "status": 200, diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json index d3d7e932527..690a1d4e84e 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json @@ -22,7 +22,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -38,7 +38,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json index 736f922bc8e..2e9f9fc605e 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json @@ -22,7 +22,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -40,7 +40,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\n\\n# Data Visualization\\n\\nThe `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.\\n\\nUse the `chart` tool when:\\n- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)\\n- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)\\n\\nUse mermaid fenced code blocks (` ```mermaid `) when:\\n- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram\\n- Visualizing relationships, processes, or structure — not data values\\n\\nMermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.\\n\\nDo not use either for: code, text, or data that is already clear in prose or table form.\\n\\nThe `chart` tool input accepts:\\n- `title` (string) — short label shown in the tool header\\n- `description` (string, optional) — subtitle shown below the title\\n- `spec` (string) — a Chart.js config object as a JSON string\\n\\nThe `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:\\n\\nBar chart:\\n```json\\n{\\n \\\"type\\\": \\\"bar\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 20, 15] }]\\n }\\n}\\n```\\n\\nLine chart:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"Jan\\\", \\\"Feb\\\", \\\"Mar\\\", \\\"Apr\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [10, 28, 19, 45], \\\"fill\\\": false }]\\n }\\n}\\n```\\n\\nScatter plot:\\n```json\\n{\\n \\\"type\\\": \\\"scatter\\\",\\n \\\"data\\\": {\\n \\\"datasets\\\": [{\\n \\\"label\\\": \\\"Points\\\",\\n \\\"data\\\": [{ \\\"x\\\": 1, \\\"y\\\": 5 }, { \\\"x\\\": 2, \\\"y\\\": 8 }, { \\\"x\\\": 3, \\\"y\\\": 3 }]\\n }]\\n }\\n}\\n```\\n\\nTime series:\\n```json\\n{\\n \\\"type\\\": \\\"line\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"2024-01\\\", \\\"2024-02\\\", \\\"2024-03\\\", \\\"2024-04\\\"],\\n \\\"datasets\\\": [{ \\\"label\\\": \\\"Value\\\", \\\"data\\\": [120, 145, 132, 178], \\\"fill\\\": true }]\\n }\\n}\\n```\\n\\nPie chart:\\n```json\\n{\\n \\\"type\\\": \\\"pie\\\",\\n \\\"data\\\": {\\n \\\"labels\\\": [\\\"A\\\", \\\"B\\\", \\\"C\\\"],\\n \\\"datasets\\\": [{ \\\"data\\\": [30, 50, 20] }]\\n }\\n}\\n```\\n\\nYou may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, diff --git a/packages/opencode/test/kilocode/chart-tool-gating.test.ts b/packages/opencode/test/kilocode/chart-tool-gating.test.ts new file mode 100644 index 00000000000..4478e9243b1 --- /dev/null +++ b/packages/opencode/test/kilocode/chart-tool-gating.test.ts @@ -0,0 +1,42 @@ +import { expect, test } from "bun:test" +import { KiloToolRegistry } from "@/kilocode/tool/registry" +import type * as Tool from "@/tool/tool" + +// Minimal stub — select() only reads .id from each Tool.Def +const stub = (id: string) => ({ id }) as unknown as Tool.Def + +const tools = { + codebase: stub("codebase"), + recall: stub("recall"), + managerModels: stub("managerModels"), + memory: stub("memory"), + save: stub("save"), + manager: stub("manager"), + process: stub("process"), + chart: stub("chart"), + image: stub("image"), + notify: stub("notify"), +} + +function ids(client: string) { + const prev = process.env.KILO_CLIENT + try { + process.env.KILO_CLIENT = client + return KiloToolRegistry.extra(tools, {}).map((t) => t.id) + } finally { + if (prev === undefined) delete process.env.KILO_CLIENT + else process.env.KILO_CLIENT = prev + } +} + +test("chart tool is included for vscode", () => { + expect(ids("vscode")).toContain("chart") +}) + +test("chart tool is excluded for cli", () => { + expect(ids("cli")).not.toContain("chart") +}) + +test("chart tool is excluded for jetbrains", () => { + expect(ids("jetbrains")).not.toContain("chart") +}) From 53179eb24eb3b9ca20d65660b6aaef3d5d87c455 Mon Sep 17 00:00:00 2001 From: "kilo-maintainer[bot]" Date: Mon, 27 Jul 2026 17:56:14 +0000 Subject: [PATCH 07/25] chore: update kilo-vscode visual regression baselines --- .../labs-tool-call-lab/search-previews-chromium-linux.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png index 207fc838fd7..6a4e3e8ba0b 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/labs-tool-call-lab/search-previews-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:353669eb1f2902306221eebc7a0186e9517e586f8d5e5b6c5ba60196622810ec -size 766293 +oid sha256:fd794db4d2c038984f9e753ba70b8b0473d97a16713a9ff7255f0d2cf505d41a +size 758306 From 2305b2242a020f45798733710234e082c3220d48 Mon Sep 17 00:00:00 2001 From: cmanu Date: Mon, 27 Jul 2026 11:20:33 -0700 Subject: [PATCH 08/25] fix(charts): Remove from cli tests --- .../opencode/test/kilocode/tool-registry-indexing.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts index 01fcf5ab554..702446692d6 100644 --- a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts +++ b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts @@ -355,7 +355,6 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", - "chart", "background_process", "interactive_terminal", "notify_user", @@ -367,7 +366,6 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", - "chart", "background_process", "interactive_terminal", "notify_user", @@ -384,7 +382,6 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", - "chart", "background_process", "interactive_terminal", "notify_user", @@ -441,7 +438,6 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", - "chart", "notify_user", ]) @@ -451,7 +447,6 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", - "chart", "notify_user", ]) @@ -461,7 +456,6 @@ describe("kilocode tool registry indexing", () => { "kilo_memory_recall", "kilo_memory_save", "recall", - "chart", "notify_user", ]) } finally { From 6dd64ab871f2e00b8a2801dae8d03754ce03c8db Mon Sep 17 00:00:00 2001 From: cmanu Date: Tue, 28 Jul 2026 11:37:46 -0700 Subject: [PATCH 09/25] Chart skill --- .kilo/skills/chart/SKILL.md | 86 ++++++++++++++++++ .../opencode/src/kilocode/system-prompt.ts | 91 +------------------ packages/opencode/src/kilocode/tool/chart.ts | 2 +- .../session/native-anthropic-tool-loop.json | 6 +- .../native-openai-oauth-tool-loop.json | 6 +- .../session/native-zen-tool-loop.json | 6 +- 6 files changed, 97 insertions(+), 100 deletions(-) create mode 100644 .kilo/skills/chart/SKILL.md diff --git a/.kilo/skills/chart/SKILL.md b/.kilo/skills/chart/SKILL.md new file mode 100644 index 00000000000..12f595bb292 --- /dev/null +++ b/.kilo/skills/chart/SKILL.md @@ -0,0 +1,86 @@ +--- +name: chart +description: Use when the user asks to visualize data with charts, graphs, or plots using the `chart` tool (bar, line, scatter, pie, time series, etc.). +--- + +# Data Visualization + +The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response. + +Use the `chart` tool when: +- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.) +- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions) + +Use mermaid fenced code blocks (` ```mermaid `) when: +- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram +- Visualizing relationships, processes, or structure — not data values + +Mermaid is NOT a tool and is NOT Chart.js — never call the `chart` tool for mermaid diagrams. Just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed. + +Do not use either for: code, text, or data that is already clear in prose or table form. + +The `chart` tool input accepts: +- `title` (string) — short label shown in the tool header +- `description` (string, optional) — subtitle shown below the title +- `spec` (string) — a Chart.js config object as a JSON string + +The `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples: + +Bar chart: +```json +{ + "type": "bar", + "data": { + "labels": ["A", "B", "C"], + "datasets": [{ "label": "Value", "data": [10, 20, 15] }] + } +} +``` + +Line chart: +```json +{ + "type": "line", + "data": { + "labels": ["Jan", "Feb", "Mar", "Apr"], + "datasets": [{ "label": "Value", "data": [10, 28, 19, 45], "fill": false }] + } +} +``` + +Scatter plot: +```json +{ + "type": "scatter", + "data": { + "datasets": [{ + "label": "Points", + "data": [{ "x": 1, "y": 5 }, { "x": 2, "y": 8 }, { "x": 3, "y": 3 }] + }] + } +} +``` + +Time series: +```json +{ + "type": "line", + "data": { + "labels": ["2024-01", "2024-02", "2024-03", "2024-04"], + "datasets": [{ "label": "Value", "data": [120, 145, 132, 178], "fill": true }] + } +} +``` + +Pie chart: +```json +{ + "type": "pie", + "data": { + "labels": ["A", "B", "C"], + "datasets": [{ "data": [30, 50, 20] }] + } +} +``` + +You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height. diff --git a/packages/opencode/src/kilocode/system-prompt.ts b/packages/opencode/src/kilocode/system-prompt.ts index 4c0d0c88ecf..9e2e393471c 100644 --- a/packages/opencode/src/kilocode/system-prompt.ts +++ b/packages/opencode/src/kilocode/system-prompt.ts @@ -1,7 +1,6 @@ // kilocode_change - new file import { Global } from "@opencode-ai/core/global" -import { Flag } from "@opencode-ai/core/flag/flag" import { Effect } from "effect" import { staticEnvLines, type EditorContext } from "@/kilocode/editor-context" import { KiloMemory } from "@kilocode/kilo-memory/effect" @@ -14,93 +13,6 @@ import * as Log from "@opencode-ai/core/util/log" const log = Log.create({ service: "kilocode.system-prompt" }) export namespace KilocodeSystemPrompt { - export function chartInstructions(): string | undefined { - if (Flag.KILO_CLIENT !== "vscode") return undefined - return [ - "# Data Visualization", - "", - "The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response.", - "", - "Use the `chart` tool when:", - "- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.)", - "- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions)", - "", - "Use mermaid fenced code blocks (` ```mermaid `) when:", - "- The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram", - "- Visualizing relationships, processes, or structure — not data values", - "", - "Mermaid is NOT a tool — just write the mermaid syntax directly in your text response inside a fenced code block. No tool call needed.", - "", - "Do not use either for: code, text, or data that is already clear in prose or table form.", - "", - "The `chart` tool input accepts:", - "- `title` (string) — short label shown in the tool header", - "- `description` (string, optional) — subtitle shown below the title", - "- `spec` (string) — a Chart.js config object as a JSON string", - "", - "The `spec` field must be a Chart.js config JSON string with `type`, `data`, and optionally `options`. Examples:", - "", - "Bar chart:", - "```json", - '{', - ' "type": "bar",', - ' "data": {', - ' "labels": ["A", "B", "C"],', - ' "datasets": [{ "label": "Value", "data": [10, 20, 15] }]', - ' }', - '}', - "```", - "", - "Line chart:", - "```json", - '{', - ' "type": "line",', - ' "data": {', - ' "labels": ["Jan", "Feb", "Mar", "Apr"],', - ' "datasets": [{ "label": "Value", "data": [10, 28, 19, 45], "fill": false }]', - ' }', - '}', - "```", - "", - "Scatter plot:", - "```json", - '{', - ' "type": "scatter",', - ' "data": {', - ' "datasets": [{', - ' "label": "Points",', - ' "data": [{ "x": 1, "y": 5 }, { "x": 2, "y": 8 }, { "x": 3, "y": 3 }]', - ' }]', - ' }', - '}', - "```", - "", - "Time series:", - "```json", - '{', - ' "type": "line",', - ' "data": {', - ' "labels": ["2024-01", "2024-02", "2024-03", "2024-04"],', - ' "datasets": [{ "label": "Value", "data": [120, 145, 132, 178], "fill": true }]', - ' }', - '}', - "```", - "", - "Pie chart:", - "```json", - '{', - ' "type": "pie",', - ' "data": {', - ' "labels": ["A", "B", "C"],', - ' "datasets": [{ "data": [30, 50, 20] }]', - ' }', - '}', - "```", - "", - "You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height.", - ].join("\n") - } - export function environment(input: { ctx: InstanceContext; model: Provider.Model; editor?: EditorContext }) { return [ [ @@ -115,8 +27,7 @@ export namespace KilocodeSystemPrompt { ...staticEnvLines(input.editor), ``, ].join("\n"), - chartInstructions(), - ].filter((x): x is string => x !== undefined) + ] } export function memoryBlocks(input: { diff --git a/packages/opencode/src/kilocode/tool/chart.ts b/packages/opencode/src/kilocode/tool/chart.ts index 4ba6ba9c9c3..b31fc808b51 100644 --- a/packages/opencode/src/kilocode/tool/chart.ts +++ b/packages/opencode/src/kilocode/tool/chart.ts @@ -25,7 +25,7 @@ export const ChartTool = Tool.define( Effect.gen(function* () { return { description: - "Render a data visualization chart using a Chart.js v4 config. Use this when the user asks to visualize data as a chart, graph, or plot. The config is rendered inline in the session.", + "Render a data visualization chart using a Chart.js v4 config. Use this when the user asks to visualize data as a chart, graph, or plot (bar, line, scatter, pie, etc.). Do NOT use this for diagrams, flowcharts, sequence diagrams, or any mermaid content — write those as mermaid fenced code blocks in your text response instead.", parameters: Parameters, execute: (params: Schema.Schema.Type, ctx: Tool.Context) => Effect.gen(function* () { diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json index dc52ade7b54..f7159ddf26b 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-anthropic-tool-loop.json @@ -21,7 +21,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" + "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"input_schema\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"max_tokens\":32000,\"temperature\":0}" }, "response": { "status": 200, @@ -39,7 +39,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"claude-haiku-4-5-20251001\", \"system\": [{\"type\": \"text\", \"text\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"cache_control\": {\"type\": \"ephemeral\"}}], \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"What is the weather in Paris?\", \"cache_control\": {\"type\": \"ephemeral\"}}]}, {\"role\": \"assistant\", \"content\": [{\"type\": \"tool_use\", \"id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"name\": \"get_weather\", \"input\": {\"city\": {}}}]}, {\"role\": \"user\", \"content\": [{\"type\": \"tool_result\", \"tool_use_id\": \"toolu_01A8pEqifk2HVQfq1ZDNP6iY\", \"content\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}], \"tools\": [{\"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"input_schema\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}, \"cache_control\": {\"type\": \"ephemeral\"}}], \"stream\": true, \"max_tokens\": 32000, \"temperature\": 0}" + "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\",\"cache_control\":{\"type\":\"ephemeral\"}}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01A8pEqifk2HVQfq1ZDNP6iY\",\"name\":\"get_weather\",\"input\":{\"city\":{}}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_01A8pEqifk2HVQfq1ZDNP6iY\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"input_schema\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"max_tokens\":32000,\"temperature\":0}" }, "response": { "status": 200, @@ -50,4 +50,4 @@ } } ] -} \ No newline at end of file +} diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json index 690a1d4e84e..5f966e73aee 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-openai-oauth-tool-loop.json @@ -22,7 +22,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"instructions\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-openai-oauth-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -38,7 +38,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.5\", \"input\": [{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_uoEsDnHNhxMLpCUy6hqEyHme\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"instructions\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\", \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-openai-oauth-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"text\": {\"verbosity\": \"low\"}, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"function_call\",\"call_id\":\"call_uoEsDnHNhxMLpCUy6hqEyHme\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_uoEsDnHNhxMLpCUy6hqEyHme\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-openai-oauth-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -47,4 +47,4 @@ } } ] -} \ No newline at end of file +} diff --git a/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json b/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json index 2e9f9fc605e..cf535b73a05 100644 --- a/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/kilocode/session/native-zen-tool-loop.json @@ -22,7 +22,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\":\"gpt-5.2-codex\",\"input\":[{\"role\":\"system\",\"content\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-opencode-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"max_output_tokens\":32000,\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -40,7 +40,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\": \"gpt-5.2-codex\", \"input\": [{\"role\": \"system\", \"content\": \"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"}, {\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"What is the weather in Paris?\"}]}, {\"type\": \"function_call\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\":{}}\"}, {\"type\": \"function_call_output\", \"call_id\": \"call_DfI0RwTrlaizfnQ9zkJC8rks\", \"output\": \"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}], \"tools\": [{\"type\": \"function\", \"name\": \"get_weather\", \"description\": \"Get the current weather for a city.\", \"parameters\": {\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"], \"additionalProperties\": false}}], \"store\": false, \"prompt_cache_key\": \"session-recorded-opencode-loop\", \"reasoning\": {\"effort\": \"medium\", \"summary\": \"auto\"}, \"max_output_tokens\": 32000, \"stream\": true, \"include\": [\"reasoning.encrypted_content\"]}" + "body": "{\"model\":\"gpt-5.2-codex\",\"input\":[{\"role\":\"system\",\"content\":\"You are Kilo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\n\\n# Personality\\n\\n- Your goal is to accomplish the user's task, NOT engage in a back and forth conversation.\\n- You accomplish tasks iteratively, breaking them down into clear steps and working through them methodically.\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively.\\n- Use the `question` tool only when you need an actual answer from the user.\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\n- NEVER end your result with a question or request to engage in further conversation. Formulate the end of your result in a way that is final and does not require further input from the user.\\n- The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.\\n\\n# Code\\n\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\nAnswer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"function_call\",\"call_id\":\"call_DfI0RwTrlaizfnQ9zkJC8rks\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_DfI0RwTrlaizfnQ9zkJC8rks\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"store\":false,\"prompt_cache_key\":\"session-recorded-opencode-loop\",\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"max_output_tokens\":32000,\"stream\":true,\"include\":[\"reasoning.encrypted_content\"]}" }, "response": { "status": 200, @@ -51,4 +51,4 @@ } } ] -} \ No newline at end of file +} From ed85cf93beed49567bafd678d4c768d9f769526d Mon Sep 17 00:00:00 2001 From: cmanu Date: Tue, 28 Jul 2026 12:52:38 -0700 Subject: [PATCH 10/25] Chart skill --- .kilo/skills/chart/SKILL.md | 75 +++++++++++++++++- packages/kilo-ui/src/components/chart.tsx | 80 ++++++++++---------- packages/opencode/src/kilocode/tool/chart.ts | 14 +++- 3 files changed, 124 insertions(+), 45 deletions(-) diff --git a/.kilo/skills/chart/SKILL.md b/.kilo/skills/chart/SKILL.md index 12f595bb292..4518dd2fc6b 100644 --- a/.kilo/skills/chart/SKILL.md +++ b/.kilo/skills/chart/SKILL.md @@ -7,9 +7,7 @@ description: Use when the user asks to visualize data with charts, graphs, or pl The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response. -Use the `chart` tool when: -- The user asks for a chart, graph, or plot of data (bar, line, scatter, pie, time series, etc.) -- Presenting numerical data that would be clearer visually than as a table or prose (trends, comparisons, distributions) +Use the `chart` tool only when the user explicitly asks for a chart, graph, or plot. Only use these supported Chart.js v4 types: `area`, `bar`, `bubble`, `pie`, `doughnut`, `line`, `mixed`, `polarArea`, `radar`, `scatter`. Do not use it for any other type. Use mermaid fenced code blocks (` ```mermaid `) when: - The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram @@ -37,6 +35,17 @@ Bar chart: } ``` +Area chart (line with fill): +```json +{ + "type": "line", + "data": { + "labels": ["Jan", "Feb", "Mar", "Apr"], + "datasets": [{ "label": "Value", "data": [10, 28, 19, 45], "fill": true }] + } +} +``` + Line chart: ```json { @@ -83,4 +92,64 @@ Pie chart: } ``` +Doughnut chart: +```json +{ + "type": "doughnut", + "data": { + "labels": ["A", "B", "C"], + "datasets": [{ "data": [30, 50, 20] }] + } +} +``` + +Radar chart: +```json +{ + "type": "radar", + "data": { + "labels": ["Speed", "Power", "Agility", "Stamina"], + "datasets": [{ "label": "Player", "data": [80, 60, 90, 70] }] + } +} +``` + +Bubble chart: +```json +{ + "type": "bubble", + "data": { + "datasets": [{ + "label": "Group A", + "data": [{ "x": 10, "y": 20, "r": 8 }, { "x": 15, "y": 10, "r": 5 }] + }] + } +} +``` + +Polar area chart: +```json +{ + "type": "polarArea", + "data": { + "labels": ["A", "B", "C", "D"], + "datasets": [{ "data": [11, 16, 7, 14] }] + } +} +``` + +Mixed chart (bar + line): +```json +{ + "type": "bar", + "data": { + "labels": ["Jan", "Feb", "Mar"], + "datasets": [ + { "type": "bar", "label": "Revenue", "data": [100, 120, 90] }, + { "type": "line", "label": "Trend", "data": [95, 115, 100] } + ] + } +} +``` + You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height. diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index a100f64e36c..e17195235d7 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -1,7 +1,8 @@ /** @jsxImportSource solid-js */ -import { createSignal, onCleanup, onMount } from "solid-js" +import { createEffect, createSignal, onCleanup } from "solid-js" import { BasicTool } from "./basic-tool" import type { ToolProps } from "./message-part" +import { busy } from "./tool-utils" function getThemeColors() { const style = getComputedStyle(document.documentElement) @@ -21,7 +22,7 @@ type ChartConfig = { labels?: string[] datasets: { label?: string - data: number[] | { x: number | string; y: number }[] + data: number[] | { x: number | string; y: number; r?: number }[] backgroundColor?: string | string[] borderColor?: string | string[] [key: string]: unknown @@ -31,55 +32,57 @@ type ChartConfig = { } export function ChartTool(props: ToolProps) { - let canvas: HTMLCanvasElement | undefined + const [canvas, setCanvas] = createSignal() const [error, setError] = createSignal() - onMount(async () => { - if (!canvas) return + let rendered = false + + createEffect(() => { + const el = canvas() + const raw = props.output + if (!el || !raw || busy(props.status) || rendered) return let config: ChartConfig try { - config = JSON.parse(props.output ?? "{}") + const parsed = JSON.parse(raw) + if (!parsed || typeof parsed !== "object" || !parsed.type || !parsed.data) { + setError("Invalid chart config — must include type and data") + return + } + config = parsed } catch { - setError("Could not parse chart config") + // not valid JSON (e.g. mermaid or error string) — skip silently return } - if (!config || typeof config !== "object" || !config.type || !config.data) { - setError("Invalid chart config — must include type and data") - return - } + rendered = true - try { - const { Chart, registerables } = await import("chart.js") - Chart.register(...registerables) + const colors = getThemeColors() + const isPolar = config.type === "pie" || config.type === "doughnut" || config.type === "polarArea" - const colors = getThemeColors() - - const isPolar = config.type === "pie" || config.type === "doughnut" || config.type === "polarArea" - - // apply kilo series colors to datasets that don't have their own colors - config.data.datasets = config.data.datasets.map((dataset, i) => { - if (dataset.backgroundColor) return dataset - // pie/doughnut need one color per slice - if (isPolar) { - const data = dataset.data as unknown[] - return { - ...dataset, - backgroundColor: data.map((_, j) => colors.series[j % colors.series.length]), - borderColor: data.map((_, j) => colors.series[j % colors.series.length]), - } - } + const datasets = config.data.datasets.map((dataset, i) => { + if (dataset.backgroundColor) return dataset + if (isPolar) { + const data = dataset.data as unknown[] return { - backgroundColor: colors.series[i % colors.series.length], - borderColor: colors.series[i % colors.series.length], ...dataset, + backgroundColor: data.map((_, j) => colors.series[j % colors.series.length]), + borderColor: data.map((_, j) => colors.series[j % colors.series.length]), } - }) + } + return { + backgroundColor: colors.series[i % colors.series.length], + borderColor: colors.series[i % colors.series.length], + ...dataset, + } + }) - const chart = new Chart(canvas!, { + import("chart.js").then(({ Chart, registerables }) => { + if (!el.isConnected) return + Chart.register(...registerables) + const chart = new Chart(el, { type: config.type as any, - data: config.data as any, + data: { ...config.data, datasets } as any, options: { responsive: true, maintainAspectRatio: true, @@ -103,11 +106,10 @@ export function ChartTool(props: ToolProps) { ...config.options, }, }) - onCleanup(() => chart.destroy()) - } catch (e) { + }).catch((e: unknown) => { setError(e instanceof Error ? e.message : "Failed to render chart") - } + }) }) return ( @@ -122,7 +124,7 @@ export function ChartTool(props: ToolProps) { defaultOpen={props.defaultOpen ?? true} >
- {error() ?
{error()}
: } + {error() ?
{error()}
: }
) diff --git a/packages/opencode/src/kilocode/tool/chart.ts b/packages/opencode/src/kilocode/tool/chart.ts index b31fc808b51..da5e2e21f2e 100644 --- a/packages/opencode/src/kilocode/tool/chart.ts +++ b/packages/opencode/src/kilocode/tool/chart.ts @@ -10,7 +10,7 @@ const Parameters = Schema.Struct({ description: "Optional subtitle shown below the title", }), spec: Schema.String.annotate({ - description: "A valid Chart.js v4 JSON configuration string describing the chart to render", + description: "A Chart.js v4 configuration serialized as a JSON string. Pass the JSON object as a plain string value — do not nest objects, do not escape quotes manually. Example: '{\"type\":\"bar\",\"data\":{\"labels\":[\"A\",\"B\"],\"datasets\":[{\"data\":[1,2]}]}}'", }), }) @@ -25,7 +25,7 @@ export const ChartTool = Tool.define( Effect.gen(function* () { return { description: - "Render a data visualization chart using a Chart.js v4 config. Use this when the user asks to visualize data as a chart, graph, or plot (bar, line, scatter, pie, etc.). Do NOT use this for diagrams, flowcharts, sequence diagrams, or any mermaid content — write those as mermaid fenced code blocks in your text response instead.", + "Render a data visualization chart using a Chart.js v4 config. Use this when the user explicitly asks for a chart, graph, or plot. Supported types: area, bar, bubble, pie, doughnut, line, mixed, polarArea, radar, scatter. Do NOT use this for diagrams, flowcharts, sequence diagrams, or any mermaid content — write those as mermaid fenced code blocks in your text response instead. After calling this tool, do NOT output the JSON spec or raw data in your text response — the chart is the response.", parameters: Parameters, execute: (params: Schema.Schema.Type, ctx: Tool.Context) => Effect.gen(function* () { @@ -40,11 +40,19 @@ export const ChartTool = Tool.define( } catch { return { title: params.title, - output: `Invalid chart spec: could not parse JSON. Please provide a valid Chart.js v4 JSON string.`, + output: `Invalid chart spec: could not parse JSON. If you are trying to render a diagram or mermaid chart, do NOT use the chart tool — write a mermaid fenced code block in your text response instead.`, metadata: { title: params.title, description: params.description, error: "invalid-json" } as Meta, } } + if (!spec || typeof spec !== "object" || !("type" in spec) || !("data" in spec)) { + return { + title: params.title, + output: `Invalid chart spec: must be a Chart.js v4 config with "type" and "data" fields. If you are trying to render a mermaid diagram, do NOT use the chart tool — write a mermaid fenced code block in your text response instead.`, + metadata: { title: params.title, description: params.description, error: "invalid-spec" } as Meta, + } + } + return { title: params.title, output: JSON.stringify(spec), From edc6976b720483741043038a9c55e0b04d5c5391 Mon Sep 17 00:00:00 2001 From: cmanu Date: Tue, 28 Jul 2026 13:10:11 -0700 Subject: [PATCH 11/25] feat(charts): update skill to use line variant for area chart --- .kilo/skills/chart/SKILL.md | 4 +++- packages/opencode/src/kilocode/tool/chart.ts | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.kilo/skills/chart/SKILL.md b/.kilo/skills/chart/SKILL.md index 4518dd2fc6b..ddff65e6677 100644 --- a/.kilo/skills/chart/SKILL.md +++ b/.kilo/skills/chart/SKILL.md @@ -7,7 +7,7 @@ description: Use when the user asks to visualize data with charts, graphs, or pl The `chart` tool is ALWAYS available in this environment. When the user asks to visualize data (charts, graphs, plots), you MUST call the `chart` tool. Never output the config as text, never say the tool is unavailable, never suggest external renderers. Always use the tool call — it is the only correct response for data visualization requests. Do NOT repeat or echo the config JSON in your text response. -Use the `chart` tool only when the user explicitly asks for a chart, graph, or plot. Only use these supported Chart.js v4 types: `area`, `bar`, `bubble`, `pie`, `doughnut`, `line`, `mixed`, `polarArea`, `radar`, `scatter`. Do not use it for any other type. +Use the `chart` tool only when the user explicitly asks for a chart, graph, or plot. Only use these supported Chart.js v4 types: `bar`, `bubble`, `pie`, `doughnut`, `line`, `mixed`, `polarArea`, `radar`, `scatter`. For area charts, use `line` with `fill: true` on the dataset — do NOT use `area` as a type. Use mermaid fenced code blocks (` ```mermaid `) when: - The user asks for a diagram, flowchart, sequence diagram, ER diagram, or architecture diagram @@ -153,3 +153,5 @@ Mixed chart (bar + line): ``` You may customize colors by setting `backgroundColor` and `borderColor` arrays on datasets. The renderer handles sizing — do not set width or height. + +Only include `scales` in `options` for cartesian chart types: `bar`, `line`, `scatter`, `bubble`. Do NOT include `scales` for `pie`, `doughnut`, `polarArea`, `radar`, or `mixed` — it will cause them to fail. diff --git a/packages/opencode/src/kilocode/tool/chart.ts b/packages/opencode/src/kilocode/tool/chart.ts index da5e2e21f2e..c928c46ea79 100644 --- a/packages/opencode/src/kilocode/tool/chart.ts +++ b/packages/opencode/src/kilocode/tool/chart.ts @@ -25,7 +25,7 @@ export const ChartTool = Tool.define( Effect.gen(function* () { return { description: - "Render a data visualization chart using a Chart.js v4 config. Use this when the user explicitly asks for a chart, graph, or plot. Supported types: area, bar, bubble, pie, doughnut, line, mixed, polarArea, radar, scatter. Do NOT use this for diagrams, flowcharts, sequence diagrams, or any mermaid content — write those as mermaid fenced code blocks in your text response instead. After calling this tool, do NOT output the JSON spec or raw data in your text response — the chart is the response.", + "Render a data visualization chart using a Chart.js v4 config. Use this when the user explicitly asks for a chart, graph, or plot. Supported types: bar, bubble, pie, doughnut, line (use fill:true for area charts), mixed, polarArea, radar, scatter. Do NOT use this for diagrams, flowcharts, sequence diagrams, or any mermaid content — write those as mermaid fenced code blocks in your text response instead. After calling this tool, do NOT output the JSON spec or raw data in your text response — the chart is the response.", parameters: Parameters, execute: (params: Schema.Schema.Type, ctx: Tool.Context) => Effect.gen(function* () { @@ -53,6 +53,19 @@ export const ChartTool = Tool.define( } } + // "area" is not a Chart.js type — remap to line with fill + if ((spec as Record).type === "area") { + const s = spec as Record + s.type = "line" + const data = s.data as Record | undefined + if (data && Array.isArray(data.datasets)) { + data.datasets = (data.datasets as Record[]).map((ds) => ({ + fill: true, + ...ds, + })) + } + } + return { title: params.title, output: JSON.stringify(spec), From 74bfa5e1a01df91ae6227ebb03b1aea7de3062a5 Mon Sep 17 00:00:00 2001 From: cmanu Date: Thu, 30 Jul 2026 19:38:47 -0700 Subject: [PATCH 12/25] fix(charts): Move chart register --- packages/kilo-ui/src/components/chart.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index e17195235d7..99892624fe5 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -1,9 +1,12 @@ /** @jsxImportSource solid-js */ import { createEffect, createSignal, onCleanup } from "solid-js" +import { Chart, registerables } from "chart.js" import { BasicTool } from "./basic-tool" import type { ToolProps } from "./message-part" import { busy } from "./tool-utils" +Chart.register(...registerables) + function getThemeColors() { const style = getComputedStyle(document.documentElement) const get = (v: string, fallback: string) => style.getPropertyValue(v).trim() || fallback @@ -77,10 +80,13 @@ export function ChartTool(props: ToolProps) { } }) - import("chart.js").then(({ Chart, registerables }) => { - if (!el.isConnected) return - Chart.register(...registerables) - const chart = new Chart(el, { + let chart: Chart | undefined + onCleanup(() => chart?.destroy()) + + if (!el.isConnected) return + + try { + chart = new Chart(el, { type: config.type as any, data: { ...config.data, datasets } as any, options: { @@ -106,10 +112,9 @@ export function ChartTool(props: ToolProps) { ...config.options, }, }) - onCleanup(() => chart.destroy()) - }).catch((e: unknown) => { + } catch (e) { setError(e instanceof Error ? e.message : "Failed to render chart") - }) + } }) return ( From d506dc425896f3599c4e24c6209df173ba43af3a Mon Sep 17 00:00:00 2001 From: cmanu Date: Thu, 30 Jul 2026 19:43:30 -0700 Subject: [PATCH 13/25] fix(charts): console warning --- packages/kilo-ui/src/components/chart.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index 99892624fe5..382cfe30ebd 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -53,8 +53,9 @@ export function ChartTool(props: ToolProps) { return } config = parsed - } catch { - // not valid JSON (e.g. mermaid or error string) — skip silently + } catch (e) { + // output is not valid JSON — model likely passed an unsupported type + console.warn("Chart: could not parse output as JSON", e) return } From c94d49ce2d03b6e81b3af6ff1541adcba53e08c5 Mon Sep 17 00:00:00 2001 From: cmanu Date: Thu, 30 Jul 2026 19:49:48 -0700 Subject: [PATCH 14/25] fix(charts) update console warn --- packages/kilo-ui/src/components/chart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index 382cfe30ebd..2e299fbaf0e 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -55,7 +55,7 @@ export function ChartTool(props: ToolProps) { config = parsed } catch (e) { // output is not valid JSON — model likely passed an unsupported type - console.warn("Chart: could not parse output as JSON", e) + console.warn("[Kilo Chart]: could not parse output as JSON", e) return } From 2d7b71f118f303018bd43e540a69b21da059fc5b Mon Sep 17 00:00:00 2001 From: cmanu Date: Fri, 31 Jul 2026 07:32:39 -0700 Subject: [PATCH 15/25] fix(charts): bot suggestion applied for cleanup --- packages/kilo-ui/src/components/chart.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index 2e299fbaf0e..40979900861 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -59,7 +59,13 @@ export function ChartTool(props: ToolProps) { return } - rendered = true + let chart: Chart | undefined + onCleanup(() => { + chart?.destroy() + rendered = false + }) + + if (!el.isConnected) return const colors = getThemeColors() const isPolar = config.type === "pie" || config.type === "doughnut" || config.type === "polarArea" @@ -81,11 +87,6 @@ export function ChartTool(props: ToolProps) { } }) - let chart: Chart | undefined - onCleanup(() => chart?.destroy()) - - if (!el.isConnected) return - try { chart = new Chart(el, { type: config.type as any, @@ -113,6 +114,7 @@ export function ChartTool(props: ToolProps) { ...config.options, }, }) + rendered = true } catch (e) { setError(e instanceof Error ? e.message : "Failed to render chart") } From 242b116b81ca58cdc9ce89bb3256a91f8b2a236f Mon Sep 17 00:00:00 2001 From: cmanu Date: Fri, 31 Jul 2026 07:44:52 -0700 Subject: [PATCH 16/25] feat(charts): vscode charts colors --- packages/kilo-ui/src/components/chart.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/kilo-ui/src/components/chart.tsx b/packages/kilo-ui/src/components/chart.tsx index 40979900861..4d7c72b4c0b 100644 --- a/packages/kilo-ui/src/components/chart.tsx +++ b/packages/kilo-ui/src/components/chart.tsx @@ -15,7 +15,14 @@ function getThemeColors() { textWeak: get("--text-weak", "#A3A3A3"), border: get("--border-weak-base", "#FFFFFF1A"), surface: get("--surface-raised-base", "#202020"), - series: ["#3B82F6", "#00BAA9", "#22C55E", "#F97316", "#A855F7", "#EF4444"], + series: [ + get("--vscode-charts-blue", "#3B82F6"), + get("--vscode-charts-green", "#22C55E"), + get("--vscode-charts-purple", "#A855F7"), + get("--vscode-charts-orange", "#F97316"), + get("--vscode-charts-red", "#EF4444"), + get("--vscode-charts-yellow", "#EAB308"), + ], } } From 9819c1c315bd05631862ad6f21bbef21d73b7bdc Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 3 Aug 2026 12:53:40 +0200 Subject: [PATCH 17/25] feat(vscode): restore multi-project section and drag-and-drop support Restore Agent Manager sections and worktree drag-and-drop when multiple projects are shown, with ordering and section moves scoped to the owning project. - Extract shared worktree ordering/grouping logic used by both single and multi-project modes - Add per-project drag-and-drop providers with scoped identifiers - Preserve live run statuses when state payloads omit them - Match single-project label resolution using ordered session titles - Make section auto-rename tracking request-scoped to prevent stale renames - Expand multi-project Storybook story with persisted ordering, sections, and grouped worktrees - Add project-store isolation and ordering tests --- .changeset/bright-project-dnd.md | 5 + image.png | Bin 0 -> 114292 bytes .../src/agent-manager/AgentManagerProvider.ts | 6 +- .../kilo-vscode/src/agent-manager/types.ts | 1 + .../tests/unit/project-store.test.ts | 45 +++ .../tests/unit/section-helpers.test.ts | 18 + .../agent-manager/AgentManagerApp.tsx | 36 +- .../webview-ui/agent-manager/ProjectList.tsx | 3 + .../agent-manager/ProjectSidebarBody.tsx | 345 ++++++++++++------ .../agent-manager/SectionHeader.tsx | 4 +- .../webview-ui/agent-manager/project/store.ts | 8 +- .../agent-manager/section-helpers.ts | 33 ++ .../src/stories/agent-manager.stories.tsx | 25 +- .../src/types/messages/webview-messages.ts | 1 + 14 files changed, 387 insertions(+), 143 deletions(-) create mode 100644 .changeset/bright-project-dnd.md create mode 100644 image.png create mode 100644 packages/kilo-vscode/tests/unit/project-store.test.ts diff --git a/.changeset/bright-project-dnd.md b/.changeset/bright-project-dnd.md new file mode 100644 index 00000000000..750341b6225 --- /dev/null +++ b/.changeset/bright-project-dnd.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Restore Agent Manager sections and worktree drag-and-drop when multiple projects are shown, with ordering and section moves scoped to the owning project. diff --git a/image.png b/image.png new file mode 100644 index 0000000000000000000000000000000000000000..860527e425ec486d2610ef67067730beccc3c831 GIT binary patch literal 114292 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

)_i_w$qHYTJ8x?tBAAuVRBKzr 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