/** * `SpecsClient` — backed by `POST /v1/specs/validate` on QAR. * * Stateless pre-flight: validates an inline `AgentSpec` or `GraphSpec` * against the same wire-validation rules `task.start` runs, but without * dispatching anything. Useful for: * * - **CI gates** — consumer repos run `client.specs.validate(spec)` in a * PR check; failure on any spec rule blocks merge. * - **Studio live-edit** — every keystroke in the visual editor calls * validate; instant feedback while the user authors. * - **SDK pre-flight** — a developer iterating on an inline spec in a * notebook validates before calling `tasks.start`. * * Bearer auth is *forwarded if available* but not required by the endpoint. * Validation is a pure function over the spec shape — no PII, no tenant * resolution. A bearer is included only so Qodo Cloud can attribute the call * for audit purposes when one is configured. * * Per-rule failures (`{ valid: false, errors: [{ rule_id, path, message }] }`) * are *not* exceptions — they're the normal happy-path return. Only * pre-wire issues (missing `kind`, unreachable host, non-2xx HTTP) throw. */ import type { InlineAgentSpec, InlineGraphSpec, SpecValidateResult } from '../qar/specs.js'; export declare class SpecsClient { private readonly baseUrl; private readonly allowInsecureTransport; private readonly authProvider; private readonly extraHeaders; private readonly fetchImpl; private readonly traceContext; private cachedHttpBase; /** * Memoized bearer resolution. Three states: * - `undefined` — never resolved yet. * - `string` — bearer was found and is reused on subsequent calls. * - `null` — confirmed "no bearer configured"; we won't probe again. * * `AuthProvider.validate()` does synchronous filesystem work and throws * `QodoAuthError` when no key exists anywhere. In high-frequency scenarios * (CI gating many specs, Studio live-edit per keystroke) re-running that on * every call would burn syscalls and throw/catch loops. Cache the answer. */ private cachedBearer; /** * Validate an `AgentSpec` or `GraphSpec` against QAR's wire-validation * rules without dispatching a task. * * Returns the typed `SpecValidateResult` envelope: * - `{ valid: true }` — the spec passed every rule. * - `{ valid: false, errors: [...] }` — the spec failed one or more * rules; each entry is `{ rule_id, path, message }` where `rule_id` is * a `D10-R*` (AgentSpec) or `D11-R*` (GraphSpec) tag and `path` is a * JSON Pointer (RFC 6901) into the spec (`""` for the root). * * Throws: * - `QodoSpecValidationError` — the supplied object is missing a known * `kind` discriminator, so it's not a well-formed spec at all. (Per-rule * failures are *not* exceptions — they come back as `valid: false`.) * - `QodoAuthError` — only when a bearer was provided and the server * rejected it (401/403). When no bearer is configured the endpoint is * anonymous, so 401/403 surfaces as a plain `Error` instead. * - plain `Error` — any other non-2xx response, with the body included. */ validate(spec: InlineAgentSpec | InlineGraphSpec): Promise; /** * Try to resolve a bearer without throwing if none is configured. * * `AuthProvider.validate()` throws `QodoAuthError` when no key is found * anywhere — that's the right call for `tasks.start` (auth required) but * the wrong one here (validate is anonymous-friendly). We swallow that * specific error and treat it as "no bearer" so the call proceeds without * an `Authorization` header. Any other thrown shape (filesystem error * inside `validate`, etc.) propagates so we don't mask real bugs. * * Memoized via `cachedBearer` — once the answer is known we don't re-probe * the filesystem on every call. CI gating many specs and Studio live-edit * are both high-frequency callers of `validate()`. */ private tryResolveBearer; /** * Apply the wire-default `kind` discriminator to an `InlineAgentSpec` whose * type-optional `kind` was omitted. The check is duck-type: an object with * `apiVersion: 'qar/v1'` + no explicit `kind` is treated as an AgentSpec * (the only optional-kind arm of `InlineSpec` — `InlineGraphSpec.kind` is * required by the type and `assertSpecKind` enforces it). Anything else * passes through untouched and gets caught by `assertSpecKind` below. * * Mirrors `TaskClient.startWithAgent`'s `normalizeInlineAgentSpec` call so * the SDK presents one consistent normalization story regardless of which * entry point the consumer uses. */ private static normalizeKind; /** * Reject anything that isn't a JSON object with a recognized `kind` * discriminator. JS callers can pass `null`, undefined, primitives, etc. — * fail fast with a typed error rather than letting `JSON.stringify` ship * `null`/`"foo"` to the endpoint. * * Accepts only the wire-canonical `kind: 'AgentSpec' | 'GraphSpec'` literals * matching QAR's `SPEC_KIND_AGENT` / `SPEC_KIND_GRAPH`. The legacy lowercase * `'graph'` arm was dropped — the wire validator only ever accepted the * capitalized form, so consumers carrying the old literal now get a typed * pre-wire error instead of a `task.done(failed)` round-trip. */ private static assertSpecKind; /** * Lazy URL parsing — symmetric with `InfoClient.requireHttpBase`. * `QodoClient.validateOptions` only checks `baseUrl` is a non-empty string; * eager derivation would change behavior for callers who construct a client * with an invalid scheme but never call any HTTP method. */ private requireHttpBase; } //# sourceMappingURL=SpecsClient.d.ts.map