/** * SMI-5456: Agent-mediation marker channel. * * Wave 1 needs to distinguish agent-mediated skill invocations from ambient * ones so the mediation gate (≥25% agent-mediated share by day 30) is * measurable. Two channels feed the three per-event telemetry fields * (`agent_session`, `nudge_origin`, `trigger_id`) plus the per-harness * `framework` attribution (via the vocabulary-validated `harness` hint — * the mediation dashboard's per-harness denominator): * * 1. MCP `_meta` on the tool call (spec-clean; wins when present). No Tier-1 * harness can inject `_meta` on a genuine agent tool call today (Step-0 * spike (e)) — hooks only touch `arguments`, the model has no `_meta` * schema affordance — so this is forward-looking infrastructure that * activates the day a harness ships native support. * 2. A session-scoped marker file written by a harness SessionStart hook * under `~/.skillsmith/agent-markers/` (PRIMARY channel for Wave 1). The * server treats these files as READ-ONLY: it never writes, updates, or * deletes them (that is the hook's job at SessionEnd). Stale files that a * crashed session left behind expire by a session TTL. * * A missing / corrupt / expired file is simply "no marker" — never an error. */ /** On-disk marker schema version — bump on a breaking shape change. */ export declare const AGENT_MARKER_SCHEMA_VERSION = 1; /** * Session TTL. A marker older than this (measured from its `started_at`) is * ignored as stale. * * Rationale for 12h: the primary staleness control is the hook deleting its * own file at SessionEnd; the TTL only backstops a session that crashed * without cleanup. 12h comfortably spans a long interactive working day so a * genuinely live session is never expired mid-flight, while still ensuring a * marker abandoned by a crash cannot mislabel invocations a day or more later. */ export declare const AGENT_MARKER_TTL_MS: number; /** * Max bytes read from a single marker file. * * `readSessionMarker` runs synchronously on the MCP dispatch hot path (every * tool call). A well-formed marker is a handful of scalar fields — a few * hundred bytes; 16 KiB is generous headroom. Without this cap a corrupt or * hostile file of unbounded size would block the event loop for the full * synchronous `readFileSync` + `JSON.parse` on every single tool call until * the hook cleans it up (or the TTL elapses, which only affects staleness, * not the read cost). Oversized files are treated as corrupt: skipped, never * thrown. */ export declare const AGENT_MARKER_MAX_FILE_BYTES: number; /** * Accepted `harness` vocabulary — the SMI-5012 wire format's `framework` enum * (authoritative list: skill-invoke-telemetry-guide.md § Wire format) minus * `'unknown'`. `'unknown'` is the extractor's absence fallback, not a value a * marker may assert: accepting it would let a marker file overwrite a real * extractor result with noise. Anything outside this set (junk from disk or * `_meta`) resolves to `undefined` and never flows into telemetry. */ export declare const KNOWN_HARNESS_FRAMEWORKS: readonly ["claude-code", "cursor", "continue", "cline", "copilot", "windsurf", "codex", "vscode", "opencode", "hermes"]; /** A validated harness/framework value from the marker channel. */ export type HarnessFramework = (typeof KNOWN_HARNESS_FRAMEWORKS)[number]; /** The "no marker present" resolution — all fields at their neutral default. */ export declare const NO_AGENT_MARKER: AgentMarker; /** * Resolved marker — the three per-event fields threaded into the telemetry * payload. Always fully populated (neutral defaults when no marker resolves). */ export interface AgentMarker { /** True when the invocation is part of an agent-mediated session. */ agentSession: boolean; /** True when the invocation originated from a nudge (job-9 onboarding). */ nudgeOrigin: boolean; /** Paywall / nudge trigger id, or `null` when none applies. */ triggerId: string | null; /** * Validated harness identity from the marker channel (the file's `harness` * hint or `_meta.harness`). Feeds the event's `framework` field when the * per-call extractor has nothing better (see wrap.ts). Absent when the * channel supplied no value or an out-of-vocabulary one. */ harness?: HarnessFramework; /** * SMI-6362 §1: the harness's own session identifier (the marker file's * `session_id`), threaded through so a `tool_call` telemetry row can be * grouped by harness session. File-channel only — `_meta` carries no * `session_id` today (no Tier-1 harness injects it, per this module's * header doc), so this is always the marker FILE's value or `undefined`. */ sessionId?: string; } /** * On-disk marker file shape (snake_case to match the telemetry wire format). * Written by a harness SessionStart hook; only ever READ by the server. */ export interface AgentMarkerFile { /** Schema version (see `AGENT_MARKER_SCHEMA_VERSION`). */ schema?: number; /** Harness session identifier (the hook's `session_id`). */ session_id: string; /** Epoch-ms session start; the TTL is measured from this. */ started_at: number; /** Optional harness hint, e.g. `'claude-code'`, `'cursor'`, `'opencode'`. */ harness?: string; /** Agent session? Defaults true for a valid marker; set false to opt out. */ agent_session?: boolean; /** Nudge-originated? Defaults false. */ nudge_origin?: boolean; /** Trigger id, or null. Defaults null. */ trigger_id?: string | null; } /** * Read the freshest non-expired session marker from `~/.skillsmith/agent-markers/`. * * Wave-1 correlation note: the server cannot know its own harness session id, * so it selects the most recently started live marker. Concurrent sessions on * one machine may therefore observe each other's marker — an accepted, * documented imprecision (the `_meta` channel is exact and wins when present). * * @returns the resolved marker, or null when no live marker exists. */ export declare function readSessionMarker(opts?: { now?: number; }): AgentMarker | null; /** * Defensively extract the marker fields from an MCP request's `_meta`. * * `_meta` is a loose passthrough schema (SDK 1.29.0), so junk keys and wrong * types must be ignored. Keys are snake_case to match the wire format: * `agent_session`, `nudge_origin`, `trigger_id`, `harness`. Only well-typed * values are returned; everything else is dropped (`harness` additionally * gated on the `KNOWN_HARNESS_FRAMEWORKS` vocabulary). */ export declare function extractMarkerMeta(meta: unknown): Partial; /** * Resolve the agent marker for a single tool call. * * Per-field precedence: a value present in `_meta` wins; otherwise the session * marker file supplies it; otherwise the neutral default (`false`/`false`/`null`). * * @param meta - the MCP request's `_meta` object (or undefined). * @param opts.now - injectable clock for tests. */ export declare function resolveAgentMarker(meta: unknown, opts?: { now?: number; }): AgentMarker; //# sourceMappingURL=agent-marker.d.ts.map