diff --git a/packages/opencode/src/kilocode/tool/notebook.ts b/packages/opencode/src/kilocode/tool/notebook.ts index acf6463522c..4b254c790d6 100644 --- a/packages/opencode/src/kilocode/tool/notebook.ts +++ b/packages/opencode/src/kilocode/tool/notebook.ts @@ -28,16 +28,17 @@ const render = (kind: "markdown" | "code", text: string) => { export async function open(filepath: string): Promise { if (path.extname(filepath).toLowerCase() !== ".ipynb") return undefined - const data = parse((await Encoding.read(filepath)).text) - if (!object(data) || !Array.isArray(data.cells)) return undefined + const raw = (await Encoding.read(filepath)).text + const data = parse(raw) + if (!object(data) || !Array.isArray(data.cells)) return Readable.from([raw]) const cells: string[] = [] for (const cell of data.cells) { - if (!object(cell)) return undefined + if (!object(cell)) continue if (cell.cell_type !== "markdown" && cell.cell_type !== "code") continue const text = source(cell.source) - if (text === undefined) return undefined + if (text === undefined) continue cells.push(render(cell.cell_type, text)) } diff --git a/packages/opencode/test/kilocode/read-notebook.test.ts b/packages/opencode/test/kilocode/read-notebook.test.ts index b411e9ab66e..85e96023045 100644 --- a/packages/opencode/test/kilocode/read-notebook.test.ts +++ b/packages/opencode/test/kilocode/read-notebook.test.ts @@ -93,6 +93,27 @@ describe("kilocode notebook reads", () => { }), ) + it.live("skips invalid cells without exposing raw notebook payloads", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const filepath = path.join(dir, "partial.ipynb") + const content = JSON.stringify({ + cells: [ + null, + { cell_type: "code", source: null, outputs: ["INVALID_OUTPUT_SHOULD_NOT_APPEAR"] }, + { cell_type: "markdown", source: ["Readable cell"], metadata: { marker: "CELL_METADATA_SHOULD_NOT_APPEAR" } }, + ], + }) + yield* put(filepath, content) + + const result = yield* run(dir, { filePath: filepath }) + + expect(result.output).toContain("Readable cell") + expect(result.output).not.toContain("INVALID_OUTPUT_SHOULD_NOT_APPEAR") + expect(result.output).not.toContain("CELL_METADATA_SHOULD_NOT_APPEAR") + }), + ) + it.live("applies read pagination to extracted cell text", () => Effect.gen(function* () { const dir = yield* tmpdirScoped()