UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

96 lines (95 loc) • 5.66 kB
import type { ImageContent } from "../../internal/llm.js"; /** * The Read tool's Jupyter-notebook projection: `.ipynb` is rendered as cells with outputs (CC 2.1.220 * parity), never as raw JSON text. Raw-JSON rendering lets a notebook's base64 outputs overflow the * Read token cap, auto-paginate into an `isPartialView` entry, and thereby lock NotebookEdit out via * the requireRead gate — with the marker's own offset/limit hint pointing at a range that itself * exceeds the cap (the base64 lives on one mega-line). That is the failure mode this lane removes. * * Pure module (no I/O, no env): index.ts owns reading/decoding/state; this owns parse + render. */ /** CC `o3r` parity: extname === ".ipynb". */ export declare function isNotebookPath(p: string): boolean; /** CC `Jx_` = 1e4: PER-CELL cap on the summed output payload (text chars + image * base64 chars). A cell over it has its ENTIRE outputs array replaced by one pointer line — * per cell, never a running global budget (CC's `Xx_` accumulator restarts for every cell). */ export declare const NOTEBOOK_CELL_OUTPUT_POINTER_CHARS = 10000; /** Per-output text clip. CC clips each output at its bash-output limit first (`CCs` → `vtd`) and * then the per-cell 10K pointer check dominates — in the all-cells projection a cell whose output * text exceeds 10K is always pointered, so this clip is unreachable insurance kept at the same * 10K yardstick (simplification of CC's two-stage clip, recorded deliberately). */ export declare const NOTEBOOK_OUTPUT_TEXT_CLIP = 10000; /** Cumulative image budget across the WHOLE notebook: the 10K per-cell pointer bounds one cell, but * N cells × ~9KB images is unbounded. CC gets an implicit bound because image data counts toward its * 256KB projected-JSON cap (`ePi` = 262144) and then HARD-ERRORS; sema reuses that same figure but as * a DEGRADATION budget — images past it are dropped with an honest marker instead of making the * notebook unreadable/uneditable, which is the failure mode this lane exists to remove. * Divergence-logged. */ export declare const NOTEBOOK_IMAGE_BASE64_BUDGET = 262144; export interface NotebookOutputImage { /** Whitespace-stripped base64 (magic-validated — never an unverified blob, see `imageFromData`). */ image_data: string; media_type: "image/png" | "image/jpeg"; } export interface NotebookOutput { output_type: string; text?: string; image?: NotebookOutputImage; } export interface NotebookCell { /** `cell.id`, or the synthesized `cell-${index}` for nbformat<4.5 — index = the UNFILTERED array * position, because NotebookEdit's `/^cell-(\d+)$/` positional fallback resolves exactly that. * Deliberate delta: sema synthesizes for any NON-STRING id (`typeof id === "string"` guard in * `parseNotebookCells`) where CC's `wtd` uses `e.id ?? cell-${t}` and would render e.g. a numeric * id literally — but NotebookEdit's id match is string-only, so * CC's literal form would be un-addressable while sema's positional alias resolves; nbformat * ids are spec-string, so this branch only fires on out-of-spec input. */ cell_id: string; cellType: string; source: string; /** Code cells only (CC `wtd`). */ language?: string; /** Code cells only; CC drops falsy (`e.execution_count || void 0`). */ execution_count?: number; outputs?: NotebookOutput[]; } export type ParsedNotebook = { ok: true; cells: NotebookCell[]; } | { ok: false; reason: "json" | "shape"; }; /** * CC `Ctd` + `wtd`: parse the notebook JSON into the projected cell list. * Failure is a typed reason, not a throw — the Read lane falls back to raw text on it (sema * divergence from CC's hard error: a corrupt notebook stays readable so the model can see what is * wrong; NotebookEdit still refuses it with its own JSON-validation errors, so nothing is loosened). * `notebookPath` is substituted into the per-cell pointer line (CC ships a literal * `<notebook_path>` placeholder there; the real path makes the recipe directly runnable). */ export declare function parseNotebookCells(text: string, notebookPath: string): ParsedNotebook; export interface NotebookRender { blocks: Array<{ type: "text"; text: string; } | ImageContent>; /** Total chars across the (merged) text blocks — the caller's token-cap yardstick. */ textChars: number; /** Total base64 chars actually emitted as image blocks (post-budget). */ imageChars: number; } /** * CC `eI_`/`tI_`/`Atd`: render projected cells to content blocks. Tag shape is CC-verbatim, including * the deliberately malformed closing tag `</cell id="X">`; `<cell_type>` only when not code, * `<language>` only for code cells whose language !== "python". Output text rides with a leading "\n" * and adjacent text blocks merge with "\n", so an all-text notebook collapses to a single text block. * Images ride as real image blocks in sema's `{type:"image", data, mimeType}` shape, bounded by the * cumulative `imageBase64Budget`: past it the image is DROPPED with an honest text marker — a * degradation, never an error. */ export declare function renderNotebookCells(cells: readonly NotebookCell[], imageBase64Budget: number): NotebookRender; /** Wire-card projection of a cell: base64 replaced by a length marker so the `details` card never * carries raw image bytes (they ride the model-facing content blocks; an oversized card would be * omitted whole by the card projector's 4×cap and blind the shell). */ export declare function stripNotebookImageData(cell: NotebookCell): NotebookCell;