/** * Shared tool logic for both the MCP server and the CLI. * * Each function here maps 1:1 to one /api/v1 route. The package is a thin proxy: * whether a route is deployed is a *runtime* concern — a not-yet-deployed route * returns the API's own response/error (e.g. 404) which we surface, rather than * crashing the server. * * Server-side `requirePermission` enforces the key's scope, so a tool call to a * route the key lacks permission for naturally returns 403. */ import { z } from "zod"; import { type ApiFetchOptions, type Config } from "./config.js"; export interface ListTracesArgs { limit?: number; /** Case-insensitive substring over kind/principalId/roleKey/route/correlationId. */ q?: string; /** Lower time bound: epoch ms OR Grafana relative token (e.g. `now-24h`). */ from?: string; /** Upper time bound: epoch ms OR Grafana relative token (e.g. `now`). */ to?: string; kind?: string; /** Exact HTTP status code (e.g. 404). */ status?: number; /** HTTP status class. */ statusClass?: "2xx" | "4xx" | "5xx"; direction?: string; principalType?: string; roleKey?: string; correlationId?: string; } export interface GetKpiArgs { metric?: string; since?: string; /** Lower time bound: epoch ms OR Grafana relative token (e.g. `now-24h`). */ from?: string; /** Upper time bound: epoch ms OR Grafana relative token (e.g. `now`). */ to?: string; } export interface QueryOpenClawArgs { /** Matches the server contract: POST /api/v1/openclaw/query reads `question`. */ question?: string; /** Free-form passthrough the route forwards to the bridge action. */ payload?: unknown; } export interface ListAnomaliesArgs { limit?: number; since?: string; /** Case-insensitive substring over message/kind/correlationId. */ q?: string; /** Lower time bound: epoch ms OR Grafana relative token (e.g. `now-24h`). */ from?: string; /** Upper time bound: epoch ms OR Grafana relative token (e.g. `now`). */ to?: string; /** Anomaly status (maps to anomalyStatus, e.g. 'open'|'acknowledged'). */ status?: string; severity?: string; source?: string; kind?: string; } export interface ReportAnomalyArgs { kind: string; /** Server accepts only info|warn|critical (400 otherwise). */ severity: "info" | "warn" | "critical"; message: string; correlationId?: string; /** Maps to the server's `evidence` field (non-PHI structured context). */ evidence?: unknown; /** * Agent-authored documents shipped WITH the anomaly (e.g. the full proposal * markdown) so admins read them in Atrium's Anomalies tab. Server bounds: * at most 4 items, name 1-200 chars, content <= 48000 chars each (400 * otherwise — never silently truncated). Must stay PII-free. */ attachments?: { name: string; content: string; }[]; } /** * Shared MCP input schemas, kept here (not in server.ts) so they can be unit * tested without importing server.ts/cli.ts — both of which call `main()` at * module load. server.ts spreads these into `registerTool({ inputSchema })`. */ export declare const queryOpenClawInput: { readonly question: z.ZodOptional; readonly payload: z.ZodOptional; }; export declare const losslessDoctorInput: { instanceName: z.ZodString; action: z.ZodEnum<{ status: "status"; doctor: "doctor"; repair_rollover_splits: "repair_rollover_splits"; }>; agentId: z.ZodOptional; }; export declare const anomalyAttachmentsInput: { anomalyId: z.ZodString; }; export declare const anomalyOccurrencesInput: { anomalyId: z.ZodOptional; kind: z.ZodOptional; limit: z.ZodOptional; }; export declare const resolveAnomalyInput: { readonly anomalyId: z.ZodString; readonly status: z.ZodOptional>; }; export interface ResolveAnomalyArgs { anomalyId: string; status?: "resolved" | "acknowledged"; } export declare const reportAnomalyInput: { readonly kind: z.ZodString; readonly severity: z.ZodEnum<{ info: "info"; warn: "warn"; critical: "critical"; }>; readonly message: z.ZodString; readonly correlationId: z.ZodOptional; readonly evidence: z.ZodOptional; readonly attachments: z.ZodOptional>>; }; export declare const getDeliveryReportInput: { readonly sessionId: z.ZodOptional; }; export declare const deleteDeliverySessionsInput: { readonly sessionIds: z.ZodArray; }; export declare const listTracesInput: { readonly limit: z.ZodOptional; readonly q: z.ZodOptional; readonly from: z.ZodOptional; readonly to: z.ZodOptional; readonly kind: z.ZodOptional; readonly status: z.ZodOptional; readonly statusClass: z.ZodOptional>; readonly direction: z.ZodOptional; readonly principalType: z.ZodOptional; readonly roleKey: z.ZodOptional; readonly correlationId: z.ZodOptional; }; export declare const getKpiInput: { readonly metric: z.ZodOptional; readonly since: z.ZodOptional; readonly from: z.ZodOptional; readonly to: z.ZodOptional; }; export declare const listAnomaliesInput: { readonly limit: z.ZodOptional; readonly since: z.ZodOptional; readonly q: z.ZodOptional; readonly from: z.ZodOptional; readonly to: z.ZodOptional; readonly status: z.ZodOptional; readonly severity: z.ZodOptional; readonly source: z.ZodOptional; readonly kind: z.ZodOptional; }; export declare const getFeedbackReportInput: { feedbackId: z.ZodString; }; /** * GET /api/v1/feedback-report — one user-submitted report by its shareable * REFERENCE. Returns the frozen forensic snapshot (message text/parts, prompt, * context window, session settings — volunteered by the reporter) + survival * flags (chatExists/messageExists: the report OUTLIVES message/chat deletion). * Requires `traces.read`. */ export declare function getFeedbackReport(config: Config, args: { feedbackId: string; }, options?: ApiFetchOptions): Promise; export declare const listFeedbackReportsInput: { all: z.ZodOptional; }; /** GET /api/v1/feedback-reports — the support inbox: open reports as * references + metadata (category, age, thread length; never the snapshot). * Requires `traces.read`. Fetch one by reference via get_feedback_report. */ export declare function listFeedbackReports(config: Config, args: { all?: boolean; }, options?: ApiFetchOptions): Promise; export declare const replyFeedbackReportInput: { feedbackId: z.ZodString; text: z.ZodString; }; /** POST /api/v1/feedback-report/reply — append a service reply to a report's * thread; the owner is notified. Requires `feedback.respond` (agent role). */ export declare function replyFeedbackReport(config: Config, args: { feedbackId: string; text: string; }, options?: ApiFetchOptions): Promise; export declare const closeFeedbackReportInput: { feedbackId: z.ZodString; note: z.ZodOptional; }; /** POST /api/v1/feedback-report/close — resolve a report (idempotent; the row * and thread are kept). Requires `feedback.respond` (agent role). */ export declare function closeFeedbackReport(config: Config, args: { feedbackId: string; note?: string; }, options?: ApiFetchOptions): Promise; export declare const getChatStateInput: { readonly chatId: z.ZodString; }; export declare const getCompactionHistoryInput: { readonly chatId: z.ZodString; }; export declare const getSchemaInput: { readonly id: z.ZodString; }; export declare const getTraceEnrichmentInput: { readonly correlationId: z.ZodString; readonly chatId: z.ZodOptional; readonly at: z.ZodOptional; }; export interface GetTraceEnrichmentArgs { correlationId: string; chatId?: string; at?: number; } export declare const diagnoseChatInput: { readonly chatId: z.ZodString; }; export declare const reconcileChatInput: { readonly chatId: z.ZodString; }; export declare const syncInstanceInput: { readonly instance: z.ZodString; }; /** GET /api/v1/health — liveness probe (no auth needed, but we send the key). */ export declare function health(config: Config, options?: ApiFetchOptions): Promise; /** * GET /api/v1/compat — the bridge compatibility snapshot (reachable, * bridgeVersion, per-instance targets + their gatewayVersion). Requires * `bridge.read`. Diagnoses the "version gateway inconnue" gating: empty * `targets` (or a `gatewayVersion: null` target) is what gates AgentFiles / * ChatDefaults off. */ export declare function getCompat(config: Config, options?: ApiFetchOptions): Promise; /** * GET /api/v1/bridge-status — a CLEAR per-instance bridge<->gateway health view: per * instance `bridgeUrlConfigured`, `available`/`degraded` + `reason`, `gatewayVersion` + * `gatewayState`/`lastErrorCode`, `agentCount` + discovery freshness. Requires * `bridge.read`. The fast "what's wrong with my instances" check — e.g. an instance with * `bridgeUrlConfigured:false` is exactly why a sync returns `no_bridge_url`. */ export declare function bridgeStatus(config: Config, options?: ApiFetchOptions): Promise; /** * GET /api/v1/integrations — Opik/Langfuse integration status: per vendor * `configured`/`enabled` + the NON-SECRET effective endpoints + the shipping * cursors (lastAt/failureCount/error code). NEVER a key. Requires `traces.read`. * The self-correction loop's first step: an agent learns whether enriched * observability data is available (and shipping is healthy) before asking for it. */ export declare function getIntegrations(config: Config, options?: ApiFetchOptions): Promise; /** * GET /api/v1/chat-state — per-message lifecycle of one chat (METADATA ONLY: no * text). Requires `traces.read`. Exposes the stuck-streaming signal: a message * `status:"streaming"` with a large `ageSeconds` (`stuckStreaming:true`) is a * turn whose finalize frame the bridge never relayed. A provenance part also * carries a SOC2-safe `structure` (per-item kind + hasFileName/hasScore booleans, * counts, allowlisted source/route) for diagnosing the Sources panel content-free. * * TURN RECONSTRUCTION (content-free): per message, `outbox:{outboxId,status}` is * the dispatch JOIN KEY — `chatId:outboxId` is the correlationId of that turn's * chat.send / openclaw.dispatch (and openclaw.rehydrate) traces, so list_traces * stitches a message to its dispatch chain. NOTE on `outbox:null`: it means EITHER * no outbox row (an assistant message — only user turns dispatch) OR a user message * older than the per-status read cap; when top-level `outboxTruncated` is true, read * null on an OLDER user message as "beyond the cap", NOT as "never dispatched". The * most-recent user turns are always covered. Per message, `routedInstanceName` / * `routedAgentId` give the per-turn routed agent (null = the chat's primary). * Chat-level `routing` (perTurnRouting + lastRouted* + the opaque `routingSegment`) * shows whether/where the chat fans turns to specialists. `subAgents` is the * content-free delegation summary: `byStatus` counts + capped `failedSample` / * `runningSample` (each = childIdShort + status enum + errorCategory enum + * hasTaskName bool + ageSeconds — NEVER the task/result/error text or phase). */ export declare function getChatState(config: Config, args: { chatId: string; }, options?: ApiFetchOptions): Promise; /** * GET /api/v1/compaction-history — the gateway's compaction checkpoints for one * chat's session (LAZY: the only caller of the gateway's sessions.compaction.list, * never on the turn path). Requires `traces.read`. CONTENT-FREE: each checkpoint = * {checkpointId, createdAt, reason, tokensBefore, tokensAfter} — the stored summary * (conversation content) never crosses this API. Correlate with the per-turn * `chat.gateway_pressure` traces (list_traces kind=chat.gateway_pressure): pressure * shows WHEN the session filled up + which turn compacted; this shows what each * compaction condensed (e.g. reason "auto-threshold", 19698 -> 1050 tokens). */ export declare function getCompactionHistory(config: Config, args: { chatId: string; }, options?: ApiFetchOptions): Promise; /** * GET /api/v1/schemas — the published CONTRACT schemas an integration author can * conform to (provenance/v1 today; more as the surface grows). Metadata list (id, * title, version, category). PUBLIC (no key required). The discovery step before * fetching one schema with get_schema. */ export declare function listSchemas(config: Config, options?: ApiFetchOptions): Promise; /** * GET /api/v1/schemas/:id — one published contract schema's JSON (e.g. * "provenance.v1"), to validate a plugin's emitted reports against. PUBLIC (no key * required). 404 for an unknown id. */ export declare function getSchema(config: Config, args: { id: string; }, options?: ApiFetchOptions): Promise; /** * GET /api/v1/trace-enrichment — the SOC2-safe STRUCTURE of a turn's trace (keyed * by its correlationId) from the configured Opik/Langfuse: span * names/types/lifecycle/timing/parent tree, NEVER input/output/message * text/metadata. Requires `traces.read`. The self-correction loop's deep read: an * agent sees the REAL OpenClaw message structure behind an anomaly without ever * seeing regulated data. */ export declare function getTraceEnrichment(config: Config, args: GetTraceEnrichmentArgs, options?: ApiFetchOptions): Promise; /** * GET /api/v1/diagnose — ONE actionable assessment of a chat for the * self-correction loop: SOC2-safe chat-state + bridge availability, classified * (stuck_stream | dispatch_error | attachment_problem | subagent_stuck | * subagent_failure | bridge_unavailable | bridge_degraded | healthy) with a * `suggestedAction` and, when a safe corrective exists, a `suggestedTool`. * `subagent_stuck` (a delegated sub-agent running far too long — a main turn * awaiting it can hang) and `subagent_failure` (a recent failed delegation) read * the new chat-state `subAgents` summary. Requires `traces.read`. Read-only. Call * FIRST on a user report, then act on the suggestion. */ export declare function diagnoseChat(config: Config, args: { chatId: string; }, options?: ApiFetchOptions): Promise; /** * POST /api/v1/reconcile-chat — the BOUNDED corrective `diagnose` may recommend: * flip this chat's stuck 'streaming' message(s) to error (preserving text), * releasing the hung UI so the user can retry. Requires `selfheal` (a sensitive * write). Audited. Only touches messages already streaming past a short cutoff. */ export declare function reconcileChat(config: Config, args: { chatId: string; }, options?: ApiFetchOptions): Promise; /** * POST /api/v1/instances/sync — force an instance sync: poke the bridge (resolve creds + * connect -> pairing) then pull THAT instance's agents into Atrium NOW, instead of waiting * for the discovery cron. Requires `selfheal` (the admin + agent service-account roles). * Returns `{ status, agents, detail }` — `status` is the exact outcome (synced | no_agents * | no_bridge_url | unreachable | unauthorized | not_served | deploy_misconfigured) and * `detail` is a plain-English explanation an agent can act on. */ export declare function syncInstance(config: Config, args: { instance: string; }, options?: ApiFetchOptions): Promise; /** * POST /api/v1/delivery-record/start — start a delivery-latency recording session * (measures the bridge->Convex->frontend streaming pipeline, per delta, content-free). * Requires `selfheal` (activation is a privileged write). Returns { sessionId, * autoStopAt }; the session auto-stops after ~10 min. */ export declare function startDeliveryRecord(config: Config, options?: ApiFetchOptions): Promise; /** POST /api/v1/delivery-record/stop — stop the active recording. Requires `selfheal`. */ export declare function stopDeliveryRecord(config: Config, options?: ApiFetchOptions): Promise; /** * GET /api/v1/delivery-report — skew-corrected per-segment latency for a recording * session: A=bridge->Convex, B=Convex exec, C=Convex->frontend (p50/p95/max + counts; * C.count <= A.count by design, since the client only observes coalesced states). * Requires `traces.read`. Omit sessionId for the active (or most recent) session. */ export declare function getDeliveryReport(config: Config, args: { sessionId?: string; }, options?: ApiFetchOptions): Promise; /** * GET /api/v1/delivery-sessions — list recent recording sessions (sessionId, * startedAt, stoppedAt, startedBy, active). Requires `traces.read`. Use to pick a * sessionId for get_delivery_report or delete_delivery_sessions. */ export declare function listDeliverySessions(config: Config, options?: ApiFetchOptions): Promise; /** * POST /api/v1/delivery-record/delete — delete recording sessions and their timing * rows. Requires `selfheal`. Deleting the active session also stops recording. */ export declare function deleteDeliverySessions(config: Config, args: { sessionIds: string[]; }, options?: ApiFetchOptions): Promise; /** * GET /api/v1/activity — the platform-activity snapshot for deploy go/no-go: * activeStreams (count + max age), runningSubAgents, outbox queued/pending, * distinct active users over 5/15/60-min windows, and a deployReadiness * verdict ("idle" | "active" + the blocking reasons). Requires `traces.read`. * SOC2: counts/ages/timestamps only — never a chatId, email or content. */ export declare function getActivity(config: Config, options?: ApiFetchOptions): Promise; /** GET /api/v1/traces — recent trace events. Requires `traces.read`. */ export declare function listTraces(config: Config, args?: ListTracesArgs, options?: ApiFetchOptions): Promise; /** GET /api/v1/kpi — KPI rollups. Requires `kpi.read`. */ export declare function getKpi(config: Config, args?: GetKpiArgs, options?: ApiFetchOptions): Promise; /** * POST /api/v1/openclaw/query — query OpenClaw via the bridge. * Requires `openclaw.query`. Sends `{ question, payload }` (the only keys the * server route reads; it 400s when both are undefined). */ export declare function queryOpenClaw(config: Config, args?: QueryOpenClawArgs, options?: ApiFetchOptions): Promise; /** GET /api/v1/anomalies — detected anomalies. Requires `anomalies.read`. */ export declare function listAnomalies(config: Config, args?: ListAnomaliesArgs, options?: ApiFetchOptions): Promise; /** POST /api/v1/lossless — sanctioned lossless-claw doctor dispatch (status / * diagnosis / SAFE rollover repair only). Requires `selfheal`. */ export declare function losslessDoctor(config: Config, args: { instanceName: string; action: string; agentId?: string; }, options?: ApiFetchOptions): Promise; /** GET /api/v1/anomaly-attachments — the agent-authored documents shipped with * ONE anomaly (list_anomalies shows only their metadata). Requires * `anomalies.read`. */ export declare function getAnomalyAttachments(config: Config, args: { anomalyId: string; }, options?: ApiFetchOptions): Promise; /** GET /api/v1/anomaly-occurrences — the APPEND-ONLY observation history of one * anomaly (or of a whole cause, across successive rows). `list_anomalies` gives the * current state and its counters; this answers "when, and how many times" — the * question a single overwritten row could never answer. Requires * `anomalies.read`. */ export declare function getAnomalyOccurrences(config: Config, args: { anomalyId?: string; kind?: string; limit?: number; }, options?: ApiFetchOptions): Promise; /** POST /api/v1/anomalies/resolve — close/acknowledge an anomaly. Requires * `anomalies.report` (the same write permission as reporting one). */ export declare function resolveAnomaly(config: Config, args: ResolveAnomalyArgs, options?: ApiFetchOptions): Promise; /** * POST /api/v1/anomalies — report an anomaly. Requires * `anomalies.report`. Sends `evidence` (the server's field name), not `details`. */ export declare function reportAnomaly(config: Config, args: ReportAnomalyArgs, options?: ApiFetchOptions): Promise;