@sema-agent/core
Version:
Stateless, task-oriented AI agent core
96 lines (95 loc) • 5.66 kB
TypeScript
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;