UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

65 lines (64 loc) • 4.71 kB
/** * A skill's BASE DIRECTORY: the placeholder grammar its material may use to name its own root, the bounded * substitution that resolves it, and the delivered-length measure the load gate budgets on. * * Split out of the skill tool because it is a different concern: the tool decides what a skill INVOKE * returns, this decides how the caller's material names files on the machine the tools run on. */ /** The typed refusal a `SkillSpec.baseDir` outside its domain raises — one code, one sentence. */ export declare const SKILL_BASE_DIR_INVALID_CODE = "config.skill_base_dir_invalid"; /** * The ONE screen of `SkillSpec.baseDir`: absent ⇒ no directory (nothing is substituted and no opening line is * added); a NON-EMPTY string ⇒ itself; anything else ⇒ a loud refusal carrying {@link SKILL_BASE_DIR_INVALID_CODE}. * * Both rejected shapes were SILENT before, and each was silent in its own direction. `""` is not "no directory": * it substitutes to nothing, so a body's `${SKILL_ROOT}/ref.md` is delivered as the filesystem-root path * `/ref.md` and the opening line names no directory at all — a wrong answer where absence would have given the * visible unresolved placeholder the seat's contract promises. A non-string makes the delivered LENGTH `NaN`, * and `NaN <= limit` is false, so the load gate dropped the skill and reported "content is NaN chars" — * a configuration typo reading as an over-limit body. Core still neither resolves nor reads the directory * (the brain-outside rule); this screens the VALUE, not the path. * * It runs at the load gate ({@link substitutedSkillLength}, which spec assembly calls for every skill), so the * substitution faces below are reached only with a value that already passed it. */ export declare function skillBaseDirOf(value: unknown): string | undefined; /** * Substitute a skill's base directory for the root placeholders in ITS OWN MATERIAL. * * @contract skill.base_dir — with `SkillSpec.baseDir` set, a `Skill` result opens with the line * `Base directory for this skill: <baseDir>` and every `${SKILL_ROOT}` / `${CLAUDE_PLUGIN_ROOT}` in the * body AND in every attachment's content is replaced by that value; `files[].path` is then read relative to * it. The substitution covers the CALLER'S material only — never the model-supplied `args` section, which is * live input and is delivered as written. Core neither resolves nor reads the directory (the brain-outside * rule that keeps `SkillSpec.files` inline); with `baseDir` absent no line is added, nothing is substituted, * and the result is byte-identical to a pre-baseDir invoke. */ export declare function substituteSkillRoot(text: string, baseDir: string): string; /** * Substitute under a CEILING, and report the size the full substitution would have had. * * Substitution is the one step here that can make text grow without bound: a placeholder is 13 or 21 chars and a real directory can be any length, so an * attachment that is nothing but placeholders expands by a factor of the path's length. Building that string in order to measure it against the attachments * cap — or worse, to discover it does not FIT and drop it — spends exactly the memory the cap exists to bound before the cap is consulted, and a big enough * one exceeds the runtime's own string limit. So the length is counted as the loop walks, nothing is materialized past `maxChars`, and the caller budgets on * `totalChars` while rendering only what fits. */ /** The DELIVERED length of a body once its root placeholders resolve — counted, never materialized, so the load gate measures what the model will actually * receive rather than what the caller typed. Equal to `text.length` when no directory is stated, and the one place the seat's value is SCREENED * ({@link skillBaseDirOf}) — the gate that budgets on this number is the first reader of every skill on every leg. */ export declare function substitutedSkillLength(text: string, baseDir: unknown): number; /** The load gate's ONE reading: does a leg LOAD this skill — is the body it would deliver ({@link substitutedSkillLength}) within * `limit`? Prepare's gate keeps the skills that load (announcing the rest), and a parked Skill approval's binding reads a * skill that does not load as absent — so the binding and the activation judge one view. */ export declare function skillLoadReading(skill: { readonly content: string; readonly baseDir?: unknown; }, limit: number): { readonly loads: boolean; readonly delivered: number; }; export declare function substituteSkillRootBounded(text: string, baseDir: string, maxChars: number): { text: string; totalChars: number; };