/** * conventions — subflow + stage ID constants (builder↔recorder protocol). * * Pattern: Single Source of Truth constants (Ward Cunningham's SSOT). * Role: Contract between `core/` builders and `recorders/core/` observers. * Builders mount subflows with these IDs; recorders pattern-match * on the IDs to emit grouped domain events. * Emits: N/A (constants only). * * Rename any ID here → both builders and recorders stay in sync. */ import type { ContextSlot } from './events/types.js'; /** Subflow IDs — mounted by builders, observed by recorders. */ export declare const SUBFLOW_IDS: { /** Injection Engine subflow. Evaluates every Injection's trigger * and writes activeInjections[] for the slot subflows to consume. */ readonly INJECTION_ENGINE: "sf-injection-engine"; /** Inner subflow inside LLMCall that wraps the invocation * (seed + slots + call-llm + optional thinking + extract-final). * Mounted by LLMCall's outer `client` chart. */ readonly LLM_CALL: "sf-llm-call"; /** System-prompt slot subflow. Observed by ContextRecorder. */ readonly SYSTEM_PROMPT: "sf-system-prompt"; /** Messages slot subflow. */ readonly MESSAGES: "sf-messages"; /** Tools slot subflow. */ readonly TOOLS: "sf-tools"; /** ReAct router subflow (inside Agent). */ readonly ROUTE: "sf-route"; /** Tool-call execution subflow (inside Agent loop). */ readonly TOOL_CALLS: "sf-tool-calls"; /** Merge step inside Parallel. */ readonly MERGE: "sf-merge"; /** Final-answer composition inside Agent. Mounted via * `addSubFlowChartBranch('final', ...)` so the subflow id is the * Route decider's branch key — `'final'`, no `sf-` prefix. The * decider returns `'final'` as a routing value AND the same string * becomes the subflow's id. */ readonly FINAL: "final"; /** Cache subflow (v2.14). Wraps the whole per-turn cache machinery — * decide markers → CacheGate decider → apply/skip — as ONE collapsible * boundary in the chart. Provider-independent decision layer; the * attached provider's CacheStrategy turns markers into wire format. * UpdateSkillHistory stays OUTSIDE (in the main loop) so the rolling * skillHistory window persists across iterations without round-tripping * through this subflow. */ readonly CACHE: "sf-cache"; /** Cache decision subflow (v2.6). Walks activeInjections, emits * agnostic CacheMarker[]. Provider-independent. Standalone building * block; the agent now uses the `decideCacheMarkers` stage inside * `sf-cache` instead of mounting this directly. */ readonly CACHE_DECISION: "sf-cache-decision"; /** Thinking-normalization mount (v2.14). Wraps the consumer's * ThinkingHandler.normalize() in a real footprintjs subflow so it * has its own runtimeStageId for tracing. The result lands on the * parent LLMCall's `thinkingBlocks` payload, so this subflow is * pure plumbing from the agent step's POV — never a user-facing * step in the StepGraph. */ readonly THINKING: "sf-thinking"; }; export type SubflowId = (typeof SUBFLOW_IDS)[keyof typeof SUBFLOW_IDS]; /** Stage IDs — plain function stages that builders mount. */ export declare const STAGE_IDS: { readonly SEED: "seed"; /** Relevance entry router (`entryByRelevance`). A once-per-turn function stage * mounted between Initialize and InjectionEngine (off the ReAct loop) that * picks the starting skill by embedding similarity → sets `currentSkillId`. */ readonly PICK_ENTRY: "pick-entry"; /** Context-assembly selector stage. Runs AFTER InjectionEngine and * fans the 3 slot subflows (system-prompt / messages / tools) out in * PARALLEL (selector picks all 3 every iteration; failFast so a * required slot's throw aborts the turn). They converge before * CacheDecision. Shared by buildAgentChart + buildDynamicAgentChart; * the flat viz proof chart uses the same id as its root selector. */ readonly CONTEXT: "context"; /** Outer "client" stage in LLMCall's wrapped chart. Receives args on * the first visit, $break()s on the second (post-loop) visit with * the LLM answer as TraversalResult. This is the lens-friendly * affordance — the User pill maps to this stage. */ readonly CLIENT: "client"; readonly CALL_LLM: "call-llm"; /** Final-response extraction stage that runs after CallLLM (and * optional sf-thinking). For LLMCall this is mostly symmetric with * Agent's `sf-final` branch — gives lens a "Final" node and a * clear commit boundary marking "we have the answer." */ readonly EXTRACT_FINAL: "extract-final"; readonly FINAL: "final"; readonly FORMAT_MERGE: "format-merge"; readonly MERGE_LLM: "merge-llm"; readonly EXTRACT_MERGE: "extract-merge"; /** Updates the rolling skill-history window before CacheGate * evaluates skill-churn (v2.6). */ readonly UPDATE_SKILL_HISTORY: "update-skill-history"; /** CacheGate decider stage — routes to apply-markers / no-markers * based on kill switch / hit rate / skill churn (v2.6). */ readonly CACHE_GATE: "cache-gate"; /** CacheGate branch (routing key) when markers SHOULD be applied * this iteration. Pass-through stage; markers stay in scope. (v2.6) */ readonly APPLY_MARKERS: "apply-markers"; /** CacheGate branch (routing key) when markers should be SKIPPED * this iteration. Stage clears scope.cacheMarkers. (v2.6) */ readonly SKIP_CACHING: "no-markers"; /** BuildLLMRequest stage — calls strategy.prepareRequest to apply * markers to the wire request (v2.6). */ readonly BUILD_LLM_REQUEST: "build-llm-request"; }; export type StageId = (typeof STAGE_IDS)[keyof typeof STAGE_IDS]; /** True when a subflow id corresponds to one of the 3 context slots. */ export declare function isSlotSubflow(id: string): id is typeof SUBFLOW_IDS.SYSTEM_PROMPT | typeof SUBFLOW_IDS.MESSAGES | typeof SUBFLOW_IDS.TOOLS; /** Map a slot subflow id to its ContextSlot type. Undefined for non-slot ids. */ export declare function slotFromSubflowId(id: string): ContextSlot | undefined; /** * Resolve the context slot a scope write belongs to FROM THE WRITE'S OWN * `runtimeStageId` — not from a "currently-open slot" stack. * * Why: once the 3 slot subflows run in PARALLEL (selector fan-out), their * entry/write/exit events INTERLEAVE — a stack top is unreliable, so a write * inside `sf-messages` could be attributed to (or dropped against) * `sf-tools`. The write's `runtimeStageId` (`[subflowPath/]stageId#index`) * always encodes which slot subflow enclosed it; we scan the path segments * innermost-first for a slot id. Matches the sequential result exactly * (the write is still inside its own slot), so it is behavior-preserving. */ export declare function slotFromRuntimeStageId(runtimeStageId: string): ContextSlot | undefined; /** True when an id is any of the library's known subflow IDs. */ export declare function isKnownSubflow(id: string): id is SubflowId; /** True when an id is any of the library's known stage IDs. */ export declare function isKnownStage(id: string): id is StageId; /** * Semantic role of a stage, used by renderers to decide visual emphasis. * * The agent's chart mixes a handful of stages users actually care about * (the HEROES — what context was built, what the model decided, what it did) * with mechanism stages (PLUMBING). This is the ONE place that says which is * which; renderers stay generic and style purely off this role (e.g. heroes * prominent, plumbing muted). Keeping it here — the semantic owner — avoids * the "name-based filter list duplicated across renderers" anti-pattern. * * - `hero-slot` — a context slot (system-prompt / messages / tools) * - `hero-llm` — the LLM invocation * - `hero-action` — tool execution (the agent's actions) * - `plumbing` — mechanism (injection engine, cache, route, thinking, …) * - `boundary` — neutral chart boundaries (Initialize root, Final) + * anything unrecognised (rendered normally, never muted) */ export type StageRole = 'hero-slot' | 'hero-llm' | 'hero-action' | 'plumbing' | 'boundary'; /** * Classify a stage id into its {@link StageRole}. Accepts a path-qualified id * (`sf-llm-call/call-llm`) — only the LOCAL segment matters, so it works at * any nesting depth. Built entirely from the id constants above, so adding a * stage to the chart only requires listing it here. */ export declare function stageRole(id: string): StageRole; /** * A {@link Milestone}'s kind — the domain vocabulary for "meaningful step you'd * scrub to" in the agent's run. */ export type MilestoneKind = 'iteration' | 'slot' | 'llm-turn' | 'tool-call' | 'decision'; /** * A time-travel milestone: a domain-declared scrub stop. Conceptually each * milestone marks the boundary of a COLLECTION of commits (the commits that * belong to that step) — so the Lens slider can step stage-by-stage * (iteration → llm-turn → tool-call → …) instead of stopping only on * structural subflow boundaries. The renderer iterates whatever the domain * classifies; it never hardcodes agent vocabulary. */ export interface Milestone { readonly kind: MilestoneKind; /** Human-readable base label ("LLM turn"); the renderer may add an ordinal. */ readonly label: string; } /** * Classify a stage id into a {@link Milestone}, or `null` when the stage is NOT * a milestone boundary (its commits fold into the surrounding milestone's * collection). This is the DOMAIN's declaration of which steps are scrub-worthy; * the Lens consumes it to build the time-travel slider (see * agentfootprint-lens `cursorPositionsAtDrill`). * * Mirrors {@link stageRole}: accepts a runtimeStageId (`call-llm#17`), a * path-qualified id (`sf-llm-call/call-llm`), or a bare local id — only the * LOCAL stage segment matters, so it works at any nesting depth and for both * commit ids and subflow-group ids. */ export declare function milestoneFor(id: string): Milestone | null; /** * Scope-key convention for context injections. * * Each slot subflow writes its injections to a well-known scope key. * ContextRecorder observes writes to these keys to emit context.injected * events. Builders that mount slot subflows MUST write injections to the * corresponding key; this is the data-level contract between builder and * recorder. */ export declare const INJECTION_KEYS: { readonly SYSTEM_PROMPT: "systemPromptInjections"; readonly MESSAGES: "messagesInjections"; readonly TOOLS: "toolsInjections"; }; export type InjectionKey = (typeof INJECTION_KEYS)[keyof typeof INJECTION_KEYS]; /** Map a slot to its injection scope key. */ export declare function injectionKeyForSlot(slot: 'system-prompt' | 'messages' | 'tools'): InjectionKey; /** True when a scope key is any of the known injection keys. */ export declare function isInjectionKey(key: string): key is InjectionKey; //# sourceMappingURL=conventions.d.ts.map