@sema-agent/core
Version:
Stateless, task-oriented AI agent core
70 lines (69 loc) • 4.91 kB
TypeScript
/**
* design/125 D2 — **`runSpec`**:SpecContract 的 thin 装配器(形态定死 = `runTask` + Stop hook;裁决
* 议题#1:不组合 runGoal —— doneCheck 只在模型自报后跑不适合外部强制,且 mechanical goal 终态自己发射
* TaskOutcome,组合 = 同 taskSignature 双发射,违反 design/73 红线③)。
*
* 装配五步(全部**组合而非覆盖**=fable-M4):
* 1. preflight:frozenPaths 校验+字节快照;亲跑三值 oracle;requireRedStart(默认 true)⇒ 必须 red
* (green→`spec.not_red`、error→`spec.oracle_error`,均 fail-loud);捕获 baseline 计数。
* 2. frozen deny policy 经 {@link combinePolicies} 合入部署基线(deny 短路不可被顶);绝不裸赋顶掉
* `taskSpec.toolPolicy`(TRAP #1)。
* 3. Hooks 多路复用:runSpec 的 stop gate 先跑(guard-restore → oracle 判)——red ⇒ block 短路(失败
* 尾部经 `delimitUntrusted` 围栏后置入:oracle 输出是模型可影响面);green ⇒ **链式调用**用户 stop
* (不吞其否决权);error ⇒ block 一次并注明 oracle 基础设施故障,**连续 2 次 error 放弃把关**走终态
* 判定(防 oracle 自坏死循环)。其它 hook 槽原样透传。
* 4. 收口:终态亲跑 oracle = 最终判定;`emitTaskOutcome`(mechanical):`oracleHadRedRun` = 任一
* 「真跑过且判 red」的观察(preflight ∨ stop-gate ∨ 终态;error 永不置位)。durable pause
* (suspended/needs_review)非终态,不发射(同 goal.ts 先例:无半事实、resume 不重复计数)。
* 5. objective 框架:spec 文本围栏置入 + frozenPaths 只读声明(框架 = 可信第一方,spec = DATA)。
*
* 已知两代价(fable,写明):① stop cap=8 引擎硬常量 —— >8 次红反馈的超长修复需拆任务;② stop gate 只在
* 自然结束点咨询,maxTurns/timeout/abort 结束不咨询 —— 终态亲跑兜住**判定**但不兜**续命**(预算配比
* 参考 harness 实战:maxTurns=60 / 自限 6)。
*
* runSpec **不接**的 harness 职责(fable-MIN1,显式留给使用方):JSONL 事件账本、不变量检查器、workcopy
* 生命周期(调用方保证起点是干净树 —— preflight 红才有意义)、模型/Runner 装配、chaos 包装。frozen 写入
* 面:policy 只挡 Write/Edit 族 = belt;bash 写入由快照恢复兜(suspenders);危险 bash deny 仍是使用方纪律。
*/
import type { RunnerSelfSeat } from "../core/runner/contracts.js";
import type { TaskResult, TaskSpec } from "../core/types.js";
import { type ToolPolicy } from "../core/tool-policy.js";
import type { TaskOutcome } from "../core/task-outcome.js";
import type { EngineNotice } from "../core/engine-notice.js";
import { type OracleBaseline, type OracleGateResult, type OracleVerdict, type SpecContract } from "../core/spec-contract.js";
export interface RunSpecOptions {
/** Workcopy root — the containment root for frozenPaths, oracle cwd, and `spec.path` resolution. */
rootDir: string;
/** Per-run base spec (model/tools/toolPolicy/hooks/limits/…). `objective` is owned by runSpec (the
* spec framing); `toolPolicy` and `hooks` are COMPOSED with runSpec's, never replaced. */
taskSpec: Omit<TaskSpec, "objective">;
/** Receives the oracle gates' once-per-process `config.secret_env_scrubbed` notice (`runOracle`'s `onNotice`);
* absent ⇒ the console line. */
onNotice?: (notice: EngineNotice) => void;
}
export interface RunSpecOracleReport {
/** Terminal adjudication (the authoritative bit — the stop gate only advises mid-run). */
final: OracleVerdict;
perGate: OracleGateResult[];
/** Preflight counts backing `minPass:"baseline"` (empty when no gate parses counts). */
baseline: OracleBaseline;
}
export interface RunSpecResult {
result: TaskResult;
oracle: RunSpecOracleReport;
/** The emitted outcome fact. Undefined on a durable pause (suspended/needs_review — not a terminal). */
outcome?: TaskOutcome;
/** How many frozen files were byte-restored after tampering (stop gates + terminal sweep). */
guardRestores: number;
}
/** Exported for direct unit pinning (the path-identity family matrix). */
export declare function frozenDenyPolicy(rootDir: string, frozenResolved: string[]): ToolPolicy;
/**
* Run one task under a {@link SpecContract}. 🔴 CODE-tier input contract (fable-M3): `contract` must come
* from the deployment author / a trusted code path — its oracle commands are executed verbatim.
*
* Throws coded errors on contract violations before any model turn runs: `spec.no_oracle`,
* `spec.frozen_path_invalid`, `spec.frozen_missing`, `spec.spec_unreadable`, `spec.not_red`,
* `spec.oracle_error`.
*/
export declare function runSpec(runner: Pick<RunnerSelfSeat, "runTask"> & Partial<Pick<RunnerSelfSeat, "emitTaskOutcome" | "gateBaseline">>, contract: SpecContract, opts: RunSpecOptions): Promise<RunSpecResult>;