/** * `uptimizr agent report` — headless, scheduled analytics reports (ADR 0051 §6, * design sketch §F.4). * * A weekly scene-health digest should not need a human to open a chat. This * subcommand runs the **same** headless loop the browser assistant and the MCP * server run — `runAgent` from `@uptimizr/agent-core` over the generated * read-only tool catalog — once, from a shell, and writes Markdown to a file, * to stdout, or to a signed webhook. * * Three boundaries are deliberate and load-bearing: * * 1. **The collector gains no in-process LLM loop.** This is a separate process * that talks to the collector over its ordinary HTTP query API with an * ordinary project API key, exactly as any other agent client would. Nothing * here is importable by the server (keep backends thin, ADR 0005). * 2. **Scheduling is the operator's.** cron, a systemd timer, a GitHub Action — * the CLI runs once and exits with a meaningful code. Uptimizr operates * nothing (ADR 0017). * 3. **Provider configuration comes from the environment only and is never * persisted.** The provider key is read once into the adapter; it is never * logged, echoed, written to a report or included in an error message. The * collector key is likewise never printed. * * The report is **read-only**: the catalog it exposes performs `GET`s against * the query API and nothing else, so a `query`-capability key is all it needs * (and all it should be given — #309 / ADR 0051 §7). */ import { type AgentMessage, type AgentSkill, type LlmProvider, type ProviderUsage, type ReadTool } from "@uptimizr/agent-core"; /** * A problem the operator can fix, reported as one clear line rather than a * stack trace. Everything user-facing this module rejects — a missing variable, * an unknown skill, an unreachable provider — is one of these. */ export declare class AgentReportError extends Error { /** Process exit code to use (see {@link EXIT}). */ readonly exitCode: number; constructor(message: string, /** Process exit code to use (see {@link EXIT}). */ exitCode?: number); } /** * Exit codes, documented because a scheduled job branches on them. * * `incomplete` is deliberately non-zero: a digest whose tool calls partly failed * is still written (and says so), but a cron wrapper must be able to notice. */ export declare const EXIT: { /** The report was produced and every tool call succeeded. */ readonly ok: 0; /** Usage or configuration error — nothing ran. */ readonly usage: 1; /** The provider call, or the webhook delivery, failed. */ readonly provider: 2; /** A report was produced but is incomplete: a tool call failed, or no answer. */ readonly incomplete: 3; }; /** Wire formats the hosted adapter speaks, plus the model-free CI provider. */ export type ReportProviderKind = "anthropic" | "openai" | "scripted"; /** Version tag on the JSON report, so a consumer can branch on the shape. */ export declare const REPORT_SCHEMA = "uptimizr.agent-report/1"; /** Injectable process surface, so the whole command is testable in-process. */ export interface AgentReportDeps { env: NodeJS.ProcessEnv; /** Current time in epoch ms (pinned by tests). */ now: () => number; fetchImpl: typeof fetch; /** Report output (stdout): written verbatim, no trailing newline added. */ stdout: (text: string) => void; /** Progress and diagnostics (stderr): one line at a time. */ stderr: (line: string) => void; /** File writer, so tests need no temp directory. */ writeFile: (path: string, content: string) => void; /** * Build the LLM backend. Defaults to the hosted adapter (or the scripted * provider); tests inject a deterministic one. */ createProvider: (config: ResolvedProvider, context: ProviderContext) => LlmProvider; } /** What `createProvider` needs beyond the resolved configuration. */ export interface ProviderContext { skill: AgentSkill; tools: readonly ReadTool[]; window: ReportWindow; scene?: string; } /** The provider configuration, minus the key, which is never surfaced. */ export interface ResolvedProvider { kind: ReportProviderKind; model: string; endpoint: string; /** Present only for a hosted run; never logged, reported or serialised. */ apiKey?: string; } /** The analysis window in epoch milliseconds. */ export interface ReportWindow { since: number; until: number; /** How it was expressed (`7d`, or `explicit` for `--since/--until`). */ label: string; } /** One tool call the agent made, as the report records it. */ export interface ReportToolCall { name: string; arguments: Record; /** Wall-clock milliseconds of the collector request, when one was made. */ durationMs: number | null; ok: boolean; /** The error text the loop fed back to the model, when the call failed. */ error: string | null; /** Characters of the result handed back to the model. */ resultChars: number; } /** The structured report written by `--json` and posted to `--webhook`. */ export interface AgentReportJson { schema: typeof REPORT_SCHEMA; skill: string; title: string; scene: string | null; window: ReportWindow; collectorUrl: string; provider: { kind: ReportProviderKind; model: string; }; startedAt: string; finishedAt: string; durationMs: number; steps: number; maxSteps: number; stoppedOnMaxSteps: boolean; /** Token accounting summed over the run, when the provider reported any. */ usage: ProviderUsage | null; /** Whether `GET /api/v1/context` answered, and how much prompt it contributed. */ context: { available: boolean; chars: number; }; toolCalls: ReportToolCall[]; answer: string; } interface ParsedArgs { flags: Record; switches: Set; } /** * Parse `--flag value` / `--flag=value` / `--switch`. Deliberately tiny and * dependency-free, matching the rest of the CLI, and strict: an unknown flag is * an error rather than being silently ignored, because a typo'd `--scene` in a * cron line would otherwise produce a confidently project-wide report. */ export declare function parseAgentReportArgs(argv: readonly string[]): ParsedArgs; /** * Resolve the analysis window: explicit `--since`/`--until` epoch milliseconds * win, otherwise `--window ` counts back from now. * * Parsed by hand (digits then a unit letter) rather than with a regular * expression — the value is operator input and plain scanning has no ReDoS * surface at all. */ export declare function resolveWindow(flags: Record, nowMs: number, defaultWindow?: string): ReportWindow; /** * The window in the words a skill's text expects (`{{range}}`): "the last 7d", * or the two dates when the window was given explicitly. * * The system prompt already states the window in epoch milliseconds, but the * user turn a skill renders opens with a range of its own, and a report run with * `--window 24h` must not ask for "the last 7 days" (#316). */ export declare function describeWindow(window: ReportWindow): string; /** * Compose the system prompt: role, shared guidelines, the clock, the window the * operator asked for, the project context document, and the output rules. * * `projectContext` is the compact rendering of `GET /api/v1/context` (ADR 0051 * §5) — the project's real scene ids, region ids and custom-event names. It is * injected here exactly as `useAssistant` and the eval harness inject it, and is * simply absent on a collector too old to serve the endpoint. */ export declare function buildReportSystemPrompt(options: { nowMs: number; window: ReportWindow; scene?: string; projectContext?: string; }): string; /** One collector request the run made, timed. */ interface RecordedRead { path: string; durationMs: number; ok: boolean; } /** * Pair the transcript's tool calls with the timed reads, in order. * * A tool result that starts with one of {@link NO_REQUEST_PREFIXES} was rejected * by the loop before it built a request, so it consumes no recorded read; every * other result — including a collector error, which *did* make a request — * consumes the next one. */ export declare function collectToolCalls(messages: readonly AgentMessage[], reads: readonly RecordedRead[]): ReportToolCall[]; /** * Render the Markdown report: the model's findings, then a **Method** section * that lists every tool call with its arguments and outcome. * * The Method section is not decoration — it is what makes an unattended, * model-written document auditable: a reader can see exactly which aggregates * the figures came from, re-run them, and spot a call that silently failed. */ export declare function renderReportMarkdown(report: AgentReportJson): string; /** * The deterministic, key-free provider (`UPTIMIZR_AGENT_PROVIDER=scripted`). * * It is a real {@link LlmProvider} driven through the real loop against the real * collector — only the "model" is replaced by a rule: on its first turn it calls * exactly the tools the chosen skill names, scoped to the run's window and * scene; on its second it renders what the collector returned. * * It performs **no analysis** and is documented as such. What it is good for is * everything around the analysis: proving the collector URL, the API key, the * skill name, the output paths and the webhook signature all work, in CI or on a * new host, with no provider account and no egress. */ export declare function createScriptedReportProvider(context: ProviderContext): LlmProvider; /** * Resolve the provider from the environment — and only from the environment * (ADR 0051 §6): nothing about a provider is ever written to disk by this CLI. * * The key is read from `UPTIMIZR_AGENT_API_KEY`, falling back to the provider's * own conventional variable so an operator who already exports `ANTHROPIC_API_KEY` * needs no second export. It is returned for the adapter's constructor and is * never placed in any other value this module produces. */ export declare function resolveProvider(env: NodeJS.ProcessEnv): ResolvedProvider; /** `uptimizr agent report --help`. */ export declare function reportUsage(): string; /** * `uptimizr agent report --list-skills`. * * A skill declares its own arguments, but this command does not expose one flag * per argument: `scene` comes from `--scene` and `range` is filled in from the * resolved window (`--window`, or `--since`/`--until`). Printing `[--range]` * would advertise a flag that does not exist, so arguments are named the way an * operator actually supplies them. */ export declare function listSkills(): string; /** * Run `uptimizr agent report`. * * Returns the process exit code rather than calling `process.exit`, so the whole * command is exercisable in-process by the test suite. Every failure the * operator can act on surfaces as one stderr line, never a stack trace, and * never containing a key. */ export declare function runAgentReport(argv: readonly string[], overrides?: Partial): Promise; export {}; //# sourceMappingURL=agentReport.d.ts.map