@sema-agent/core
Version:
Stateless, task-oriented AI agent core
66 lines (65 loc) • 4.64 kB
TypeScript
/** One argv word as the lexer read it. */
export interface ShellWord {
/** The word's value with quotes and escapes removed — meaningful only when `expands` is `false`. */
readonly text: string;
/** The unquoted source spelling (assignment and wrapper-flag tests read this, never `text`). */
readonly raw: string;
/** `false`: a literal word. `"one"`: carries an expansion but yields exactly ONE word (it was double-quoted).
* `"many"`: an unquoted expansion, glob or brace pattern, or a quoted `"$@"` / array expansion — may
* yield zero, one or several words. */
readonly expands: false | "one" | "many";
}
/** One redirection of a segment, as read: the operator and its operand word (a heredoc's operand is its
* delimiter; the IO number or `{name}` before the operator is folded into the operator's meaning and not
* kept). The tightening reader never reads these — a rule speaks about program runs; the read-only
* reader does (an output redirection is a write). */
export interface ShellRedirection {
readonly op: string;
readonly target: ShellWord;
}
/** One program run the lexer found, after keyword stripping. */
export interface ShellSegment {
/** The run as SPELLED (a leading keyword removed): the first candidate a rule is compared against. */
readonly argv: readonly ShellWord[];
/** The segment's redirections in source order (see {@link ShellRedirection}); empty when it has none. */
readonly redirections: readonly ShellRedirection[];
/** The deeper candidates, one per peeled layer (leading assignments, then each wrapper of the table):
* `sudo -u root rm -r x` ⇒ `[[rm, -r, x]]`. Empty when nothing peeled. */
readonly peeled: readonly (readonly ShellWord[])[];
/** Present when this segment's program runs cannot be known from the text at all; `argv` then holds
* whatever was read before the failure and must not be matched as a run. */
readonly unreadable?: string;
/** Present when the segment's program runs are NOT fully known although its candidates could be read:
* a peel that stopped before the wrapped program could be named (its options carry an expansion, a
* command string, an option of unknown arity), or a command/process substitution anywhere in the
* segment (it runs a program the lexer does not read). The candidates stand — a rule they reach is
* reached — and every other rule reads `unreadable`. */
readonly peelUnreadable?: string;
}
/** A command line as the tightening reader sees it. */
export interface ShellCommandShape {
readonly segments: readonly ShellSegment[];
/** The connector BETWEEN each consecutive pair of segments (`;` `&&` `||` `|` `|&` `&` `\n`). */
readonly connectors: readonly string[];
/** A subshell, group, control-structure keyword or dropped empty piece bounded the segments: the
* connector list is not one flat chain a compound rule could spell. */
readonly grouped: boolean;
/** A `&` connector anywhere — between two segments or after the last one (`ls &`): some segment runs in the
* background, past any approval-time reading of the line. The connector list alone cannot say so for a
* trailing `&` (a connector rides BETWEEN segments). */
readonly backgrounded: boolean;
/** A redirection on a piece that runs NO program (`> x` alone, `[ -f a ] > x`, a keyword's own redirection):
* such a piece is dropped from `segments` (the tightening reader asks about program runs, and it runs
* none) — but the shell still opens the target, so a reader that asks about EFFECTS must refuse the line. */
readonly strayRedirection: boolean;
}
export { SHELL_WRAPPER_TABLE, type ShellWrapperName } from "./shell-wrapper-table.js";
/** Longer than this and the command is not read at all (one unreadable segment): every pass is linear,
* but a substitution nested inside quoting re-enters the quote reader once per nesting level, and the
* cap is what bounds that on model-supplied text. Upstream's own reader stops at the same size. */
export declare const MAX_SHELL_READ_CHARS = 10000;
/** Read `source` as the tightening reader must: every program run the text could perform, or why it cannot be read. */
export declare function readShellCommand(source: string): ShellCommandShape;
/** Every program run of `shape` can be NAMED from the text: no segment is unreadable, no peel stopped,
* and every run candidate's program word is literal. The offer/mint side's "judgeable" question. */
export declare function isFullyReadable(shape: ShellCommandShape): boolean;