@sema-agent/core
Version:
Stateless, task-oriented AI agent core
65 lines (64 loc) • 4.71 kB
TypeScript
/**
* 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;
};