@sema-agent/core
Version:
Stateless, task-oriented AI agent core
123 lines (122 loc) • 8.03 kB
TypeScript
/**
* The RUNNER convenience half of the consolidation driver: resolve the model
* seat out of a deployment's `RunnerDeps` (explicit chat seat wins; else the `consolidate` role
* chain) into the options `runMemoryConsolidationDriver` eats.
*
* Role resolution lives HERE and not in the engine-layer verb on purpose: the driver function's
* own seat is an explicit chat function — models, roles and
* tiers are runner vocabulary. The chain is `consolidate → summarize → coded refusal`, and the
* refusal is deliberate: one consolidation run reads the whole library (~10⁵ prompt tokens), so
* an unset role silently escalating to the most expensive `default` model would be the
* bad-value-silence family. A deployment that already declared a cheap `summarize` tier has made
* exactly the cost choice this role wants, so that hop stays; pointing `roles.consolidate` at the
* main model is a legal explicit choice.
*/
import type { Model } from "../../internal/llm.js";
import type { Brain, ModelRoles, ModelRef, TaskSpec } from "../types.js";
import { type DistillerChatFn } from "../memory-engine/distiller.js";
import { type RunMemoryConsolidationOptions, type ConsolidationDriverEngine } from "../memory-engine/consolidation-driver.js";
import { type MemoryConsolidationOptions } from "../memory-engine/consolidation.js";
import type { LlmDistillerContract } from "../memory-engine/distiller.js";
import type { EngineNotice } from "../engine-notice.js";
/** The slice of `RunnerDeps` this resolver reads (pass the deps object itself). */
export interface MemoryConsolidationDriverDeps {
brain: Brain;
models?: Record<string, Model>;
tiers?: Record<string, ModelRef>;
roles?: ModelRoles;
memoryConsolidation?: MemoryConsolidationOptions;
memoryConsolidationDriver?: {
chat?: {
baseUrl: string;
apiKey: string;
model: string;
} | DistillerChatFn;
model?: string;
contract?: LlmDistillerContract;
maxCycles?: number;
mintRepairBudget?: number;
};
/** Optional per-model auth hook (the TaskSpec-shaped seat) for the role-resolved path. */
getApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
}
/**
* Resolve a deployment's driver options: `{ chat, model }` from the explicit seat or the role
* chain, plus the deployment knobs (contract / maxCycles / mintRepairBudget) and the protocol
* parameters (`memoryConsolidation`) threaded through verbatim. The result feeds
* `runMemoryConsolidationDriver(engine, scope, resolved)` directly; every bad value refuses
* coded here or in the verb's own screen, never folds to a default.
*/
export declare function resolveMemoryConsolidationDriver(deps: MemoryConsolidationDriverDeps): RunMemoryConsolidationOptions;
/**
* "Is there ANY model seat to resolve a consolidation run from?" (the question the
* `autoRunOnRecommendation` construction door asks). It mirrors {@link resolveMemoryConsolidationDriver}'s
* seat BRANCH — an explicit chat seat (function or endpoint triple) OR a resolvable
* `consolidate`/`summarize` role — WITHOUT the value validation (a present-but-alias model id is a
* DIFFERENT bad value, caught loudly by the driver's own screen when it runs). False ⇔ the resolve
* verb would throw its "no model seat" refusal. Pinned against that resolve for drift.
*/
export declare function consolidationSeatResolvable(deps: MemoryConsolidationDriverDeps): boolean;
/**
* The advisory-to-execution bridge: fire ONE host consolidation run for the
* recommended `scope`, fire-and-forget. Called from the post-task settle tail (NOT the harvest tail)
* exactly once, only when {@link RunnerDeps.autoRunOnRecommendation} is armed AND the terminal
* harvest recommended consolidation. It NEVER throws and NEVER rejects: the driver's own R1–R7
* failure table settles the run (zero-write on mint failure, `_incomplete` on non-convergence), this
* bridge only relays the run's advisory notices to `onNotice` and routes a seat/engine fault to
* `onError(phase:"memory")`. Adds no failure semantics of its own — the trigger is the only new thing.
*/
export declare function autoRunConsolidationFromRecommendation(args: {
deps: MemoryConsolidationDriverDeps;
engine: ConsolidationDriverEngine;
scope: string;
sessionId: string;
onNotice?: (notice: EngineNotice) => void;
onError?: (err: unknown, ctx: {
phase: "memory";
sessionId: string;
}) => void;
}): Promise<void>;
/**
* THE shipped default for `RunnerDeps.autoRunOnRecommendation` when the seat is
* ABSENT: ON — the reference CLI's auto-dream form (auto-execute under the same 24h ∧ 5-session
* threshold gate the recommendation edge already uses; an explicit `false` is the deployment's off
* switch). One named value so the polarity is this one line; every sentence about the default names
* this constant rather than spelling a polarity. Read ONLY through {@link autoRunOnRecommendationEffective}.
*/
export declare const AUTO_RUN_ON_RECOMMENDATION_DEFAULT = true;
/**
* The ONE resolver of "is auto-consolidation armed on this deployment", read by the
* prepare door's manifest fact and by the settle-tail trigger (never re-derived at a call site):
* - explicit `true` ⇒ armed (the door has already refused it unless it can run);
* - explicit `false` ⇒ off — the deployment's own off switch, and it beats the default;
* - ABSENT ⇒ {@link AUTO_RUN_ON_RECOMMENDATION_DEFAULT}, and ONLY where it can run (the protocol
* switch wired AND a model seat resolvable) — the reference CLI's "auto-dream: off while
* auto-memory is off": a default-on world must not arm a deployment that wired no consolidation;
* - any other value ⇒ off (never arm on garbage; the door refuses it at prepare anyway).
*
* @contract memory.auto_consolidation.arming_has_one_resolver — whether a deployment's automatic consolidation is armed is answered here and nowhere else, a host that merely DISPLAYS the arming included: an ABSENT seat is not "off" (it reads the named default, and only where both seats are wired), so a reader branching on `RunnerDeps.autoRunOnRecommendation` alone is wrong in BOTH directions — it calls a default-armed deployment unarmed and a deployment with no consolidation armed. A deployment fact, never a per-session one; exported from the package root for that reason.
*/
export declare function autoRunOnRecommendationEffective(deps: MemoryConsolidationDriverDeps & {
autoRunOnRecommendation?: unknown;
}): boolean;
/**
* The settle tail's ONE question: does this scope's gate row hold a recommendation
* that no completed run has consumed? Asked of the GATE ROW through the same predicate the
* recommendation edge is the negation of ({@link recommendationUnconsumed}) — never of a single
* harvest's report: a crossing recorded by an earlier checkpoint harvest of the same run, or by a
* session whose settle never fired, is still owed a run here. (The report-based reading stranded such
* a scope forever: the edge sat disarmed, nothing advanced `lastRunAt`.) A gate the engine cannot read
* answers `false` — the verbs would refuse loudly on it anyway.
*/
export declare function scopeHasUnconsumedRecommendation(engine: Pick<ConsolidationDriverEngine, "readConsolidationGate">, scope: string): boolean;
/**
* The auto-consolidation ARMING door (bad-value loudness, three states), called from
* the prepare config doors at construction on every leg. A garbage value refuses; a CONTRADICTION
* (`true` with nothing to run) refuses — never folded to off, so an armed-but-unrunnable trigger (the
* invisible-failure family) cannot ship: a deployment learns at boot, not one silently dropped
* recommendation later. Lives beside {@link consolidationSeatResolvable}, the one question it asks.
*/
export declare function assertAutoConsolidationWirable(deps: MemoryConsolidationDriverDeps & {
autoRunOnRecommendation?: unknown;
}): void;