@sema-agent/core
Version:
Stateless, task-oriented AI agent core
27 lines (26 loc) • 1.77 kB
TypeScript
import type { TaskSpec } from "./types.js";
/** Thrown when `overrides` would LOOSEN a safety field `base` already set (a tighten-only violation). */
export declare class TaskSpecTightenError extends Error {
constructor(message: string);
}
/**
* Merge `overrides` onto `base` with TIGHTEN-ONLY semantics for the safety-bearing fields. Non-safety fields
* follow plain last-wins object spread (`overrides` value wins when present, else `base`). The safety fields
* get explicit rules:
*
* - **`toolPolicy`** — both set ⇒ `combinePolicies(base, override)` (deny-wins; the base's denials survive,
* the override can only ADD denials/asks). Either alone ⇒ that one. This is the central fix: a blind spread
* would drop the base policy entirely.
* - **`handsReadOnly`** — `true` is stricter. `base:true` + `override:false` ⇒ **throw** (loosening). Otherwise
* the stricter (`true` if either is true) wins.
* - **`shellGate`** — one value (`"always"`, the operator floor). Either side set ⇒ the floor stands; it cannot
* be loosened because absence is the only other state and an override cannot unset a base value.
* - **`onAsk`** — no safe automatic merge (the approver and the policy must be coordinated). Both set to
* DIFFERENT values ⇒ **throw**; equal ⇒ keep; one set ⇒ that one.
* - **`hooks`** — no standard composition (stacking hook results is caller-specific). Both set ⇒ **throw**;
* one set ⇒ that one.
*
* @throws {TaskSpecTightenError} when an override would loosen a stricter base safety field, or when two
* incompatible safety values are both set and cannot be merged automatically.
*/
export declare function tightenTaskSpec(base: TaskSpec, overrides: Partial<TaskSpec>): TaskSpec;