kestrel.markets
Version:
A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.
133 lines (125 loc) • 8.66 kB
text/typescript
/**
* # session/wake-frontier — the band-parameterized wake floor + the frontier's wake shapes
*
* The safety floor + the pending-wake / per-wake-context types the agent-driven wake frontier
* (kestrel-5zl.4) is built from, extracted from the Simulate driver ({@link import("./simulate.ts")}) so
* the band → floor table lives beside its own interface. After the T-5 OPEN baseline the AGENT drives its
* own monitoring cadence via `scheduleWake`; the platform FLOORS it (never replaces it) with the
* {@link WakeFloor} resolved here from the cell's timescale band.
*
* - {@link wakeFloorForBand} / {@link WakeFloor} — the pure `band → floor` table (kestrel-5zl.16.7); the
* DAY row reproduces today's day-shaped constants byte-for-byte (the no-churn anchor);
* - {@link FrontierWake} — a wake pending on the frontier (a dateless sim-time instant + its provenance);
* - {@link WakeCtx} — the per-wake context the driver knows that the snapshot does not.
*
* PURE: {@link wakeFloorForBand} is a total function of the band alone (no wall clock, no RNG) — same band
* ⇒ identical floor ⇒ byte-identical wake schedule. Safety can never be silenced (CLAUDE fail-closed); the
* floors are author-tunable config, never removable by the agent.
*/
import { assertNever } from "../lang/index.ts";
import type { ViewSelection } from "../frame/pane-catalog.ts";
import type { WakeSource } from "./agent.ts";
import type { TimescaleBand } from "./carry.ts";
// ── The band-parameterized wake floor (kestrel-5zl.16.7) ──────────────────────────────────────────
// The three wake-frontier controls below WERE day-shaped module constants; they are now the `day`
// row of a pure {@link wakeFloorForBand} table keyed on the cell's timescale band (`carry.band`, the
// multi-session horizon Axis A). The DAY row REPRODUCES today's exact defaults byte-for-byte (the
// no-churn anchor); the other rows scale the same three controls to their timescale. Threaded once at
// the top of `runSimulateSession` from `openingCarry.band` (default `day`), so a standalone /
// flat-open run is byte-identical to today. See the design note on kestrel-5zl.16.7.
/** The DAY staleness-backstop deadline (minutes): a gap longer than this after any delivered vantage
* forces a platform safety wake, so a book is never left unattended (CONTEXT "Staleness backstop").
* This is ALSO the universal fail-closed floor — a HELD book in a WIDER band whose backstop lands past
* settle still gets a safety wake before settle (never a silent no-wake window while exposed). */
export const DAY_STALENESS_BACKSTOP_MIN = 30;
/** The DAY minimum spacing (minutes) between DELIVERED wakes: an agent wake closer than this to the
* last vantage is FOLDED (coalesced) and surfaced on the retained delivery's `attention.coalesced` —
* never a separate turn, never a silent drop. The staleness/structural floors are exempt. */
const DAY_MIN_WAKE_SPACING_MIN = 1;
/** The DAY per-session ceiling on delivered wakes: past it the agent's wakes are folded (downgraded to
* safety-only) so a runaway cadence can't exhaust attention — the staleness/structural floors still fire
* (safety is never capped away). A generous default; the budget algebra proper is scoped to b3l. */
const DAY_MAX_WAKES = 512;
/** The three wake-frontier controls, resolved from the cell's timescale band (kestrel-5zl.16.7). A pure
* function of the band alone (no wall clock, no RNG) — same band ⇒ identical floor ⇒ byte-identical
* wake schedule. The `structural` close ramp is a separate control owned by `day.ts` (kestrel-5zl.3);
* its per-band re-shaping re-anchors at that owner, so it is NOT part of this floor. */
export interface WakeFloor {
/** The staleness-backstop deadline (minutes): the max gap after a vantage before a safety wake. */
readonly stalenessBackstopMin: number;
/** The coalescing floor (minutes): an agent wake nearer than this to the last vantage is folded. */
readonly minWakeSpacingMin: number;
/** The per-session ceiling on delivered wakes (safety floors are exempt). */
readonly maxWakes: number;
}
/**
* The band → {@link WakeFloor} table (kestrel-5zl.16.7). Pure + total (every {@link TimescaleBand} maps).
*
* band backstop spacing (coalesce) ceiling
* scalp 2 min 0.25 min (15 s) 4096 — seconds-scale, tick cadence
* day 30 min 1 min 512 — TODAY'S EXACT DEFAULTS (the no-churn anchor)
* swing 240 min (4 h) 5 min 512 — intraday → multi-day
* position 1440 min (~1 d) 15 min 512 — a ~1-day backstop + daily cadence
*
* The DAY row is the identity of today's day-shaped constants (byte-for-byte); the scalp/swing/position
* rows are the minimal shape consistent with `carry.band` + those defaults (design note on 5zl.16.7).
* The fail-closed floor (a HELD book in a WIDE band always keeps a backstop before settle — never a
* silent no-wake window while exposed) is enforced in `runSimulateSession`'s `armStaleness`, not
* by widening this table: widening cadence by band must never create a silent exposed window.
*/
export function wakeFloorForBand(band: TimescaleBand): WakeFloor {
switch (band) {
case "scalp":
return { stalenessBackstopMin: 2, minWakeSpacingMin: 0.25, maxWakes: 4096 };
case "day":
return { stalenessBackstopMin: DAY_STALENESS_BACKSTOP_MIN, minWakeSpacingMin: DAY_MIN_WAKE_SPACING_MIN, maxWakes: DAY_MAX_WAKES };
case "swing":
return { stalenessBackstopMin: 240, minWakeSpacingMin: 5, maxWakes: DAY_MAX_WAKES };
case "position":
return { stalenessBackstopMin: 1440, minWakeSpacingMin: 15, maxWakes: DAY_MAX_WAKES };
default:
return assertNever(band, "simulate: unknown timescale band");
}
}
/** A wake pending on the frontier — a dateless sim-time instant plus its provenance. `reason` is the
* semantic label threaded into {@link import("./agent.ts").ActingFrame.wakeReason}; `label` is the
* dateless `HHMM` ET clock the WAKE checkpoint carries (the unchanged bus contract). */
export interface FrontierWake {
readonly ts: number;
readonly reason: string;
readonly source: WakeSource;
readonly label: string;
/** This wake's MONOTONE PER-SOURCE ordinal — the stable half of its `wakeKeyOf` replay identity
* (kestrel-5zl.20). Minted where the wake is RAISED (`pushFrontier` / a latency-fold's construction),
* NOT where it is delivered, so folding a delivery (an inserted own-fill advancing the budget/spacing,
* a probe skip) can never shift the ordinal a later same-source wake carries. A pure count of RAISES,
* per source (no wall clock, no RNG) — same bus + same turns ⇒ identical ordinals ⇒ identical keys. */
readonly sourceOrdinal: number;
/** The scheduled wake's stored View (kestrel-wa0j.4), resolved fail-closed at schedule time and
* carried to the delivery it selects panes for. Absent ⇒ the default WAKE View. */
readonly view?: ViewSelection;
}
/** The per-wake context the driver knows that the snapshot does not (dateless relative time only). */
export interface WakeCtx {
readonly wakeOrdinal: number;
/** WHICH frontier raised this delivery (kestrel-5zl.19) — with {@link wakeSourceOrdinal}, the stable
* half of the replay identity `wakeKeyOf` derives. */
readonly wakeSource: WakeSource;
/** This delivery's 0-based ordinal among the wakes THIS source RAISED (kestrel-5zl.20) — minted at the
* raise, carried on the {@link FrontierWake}, so neither a vantage another source inserts NOR a delivery
* that folded (spacing/budget/probe skip) shifts it (the global {@link wakeOrdinal} does shift). */
readonly wakeSourceOrdinal: number;
/** The wake's SEMANTIC reason (agent reason / `"staleness-backstop"` / the seed HHMM label). */
readonly wakeReason: string;
readonly wakeTs: number;
readonly prevVantageTs: number;
readonly nextWakeTs: number | null;
readonly lastTs: number;
/** Wakes remaining under the attention budget; `null` ⇒ the agent-driven horizon is open-ended. */
readonly wakesRemaining: number | null;
/** Same-key wakes folded into THIS delivery (coalesced past the budget) — the visible squelch. */
readonly coalesced: number;
/** The View this delivery carries (kestrel-wa0j.4) — the scheduled wake's stored View, already
* resolved fail-closed at schedule time. Absent ⇒ the default WAKE View (byte-identical). */
readonly view?: ViewSelection;
}