import { z } from 'zod';
import { GitCochangeSummarySchema } from '../git/types.js';
import { SourceInlineProjectionSchema } from './source-inline-contract.js';
/**
 * Public JSON shape of a CodebaseIndex.
 *
 * The field is optional on the schema so callers can still attach indexes
 * produced before the contract was versioned. New producers and serializers
 * stamp this value.
 */
export const CODEBASE_INDEX_SCHEMA_VERSION = 1;
/**
 * Canonical raw-source coordinates for one syntactic item.
 *
 * Byte offsets are zero-based UTF-8 offsets; display lines and columns are
 * one-based. The end point is exclusive in both representations. Consumers
 * must not pass byte offsets directly to String#slice. `exact: false` is
 * retained for parsers that can report only a conservative line range.
 */
export const CodeSourceSpanV1Schema = z
    .object({
    version: z.literal(1),
    offsetEncoding: z.literal('utf8-bytes'),
    endExclusive: z.literal(true),
    startLine: z.number().int().positive(),
    startColumn: z.number().int().positive().optional(),
    endLine: z.number().int().positive(),
    endColumn: z.number().int().positive().optional(),
    startByte: z.number().int().nonnegative().optional(),
    endByte: z.number().int().nonnegative().optional(),
    exact: z.boolean(),
})
    .superRefine((span, context) => {
    if (span.endLine < span.startLine) {
        context.addIssue({ code: 'custom', path: ['endLine'], message: 'endLine must not precede startLine.' });
    }
    if ((span.startColumn === undefined) !== (span.endColumn === undefined)) {
        context.addIssue({
            code: 'custom',
            path: ['endColumn'],
            message: 'startColumn and endColumn must be supplied together.',
        });
    }
    if (span.startLine === span.endLine &&
        span.startColumn !== undefined &&
        span.endColumn !== undefined &&
        span.endColumn < span.startColumn) {
        context.addIssue({ code: 'custom', path: ['endColumn'], message: 'endColumn must not precede startColumn.' });
    }
    if ((span.startByte === undefined) !== (span.endByte === undefined)) {
        context.addIssue({
            code: 'custom',
            path: ['endByte'],
            message: 'startByte and endByte must be supplied together.',
        });
    }
    if (span.startByte !== undefined && span.endByte !== undefined && span.endByte < span.startByte) {
        context.addIssue({ code: 'custom', path: ['endByte'], message: 'endByte must not precede startByte.' });
    }
    if (span.exact &&
        (span.startByte === undefined ||
            span.endByte === undefined ||
            span.startColumn === undefined ||
            span.endColumn === undefined)) {
        context.addIssue({
            code: 'custom',
            path: ['exact'],
            message: 'Exact spans require byte offsets and display columns.',
        });
    }
});
export const ParserFactCoverageSchema = z.enum(['full', 'partial', 'unavailable']);
export const ParserSyntaxStatusSchema = z.enum(['valid', 'recovered', 'unchecked']);
/** Stable machine-readable causes shared by parser-diagnostic producers and consumers. */
export const ParserDiagnosticReasonSchema = z.enum([
    'checker-unavailable',
    'dynamic-dispatch-refused',
    'parse-failed',
    'resolver-join-miss',
    'vue-script-absent',
    'vue-template-not-parsed',
    'vue-template-dynamic-component',
    'vue-template-component-unresolved',
    'vue-template-component-ambiguous',
    'vue-template-identifier-unresolved',
    'vue-template-identifier-ambiguous',
    'vue-template-expression-parse-failed',
    'vue-template-scope-unsupported',
    'vue-template-construct-unsupported',
]);
export const ParserDiagnosticCountsSchema = z.object({
    hits: z.number().int().nonnegative(),
    misses: z.number().int().nonnegative(),
});
export const ParserDiagnosticSchema = z.object({
    message: z.string(),
    severity: z.enum(['error', 'warning']),
    line: z.number().int().positive().optional(),
    column: z.number().int().positive().optional(),
    code: z.union([z.string(), z.number()]).optional(),
    source: z.string().optional(),
    reason: ParserDiagnosticReasonSchema.optional(),
    counts: ParserDiagnosticCountsSchema.optional(),
});
export const ParserTierSchema = z.enum(['typescript-compiler', 'vue-sfc-typescript', 'tree-sitter', 'regex-fallback']);
export const ParserResolverTierSchema = z.enum(['typescript-checker', 'syntax', 'none']);
export const ParserCapabilitiesSchema = z.object({
    definitions: ParserFactCoverageSchema,
    imports: ParserFactCoverageSchema,
    calls: ParserFactCoverageSchema,
    inheritance: ParserFactCoverageSchema,
    exports: ParserFactCoverageSchema,
    exactSpans: ParserFactCoverageSchema,
    /** Present when the parser has a distinct template/reference surface. */
    templateUses: ParserFactCoverageSchema.optional(),
});
export const VueTemplateRefusalCountsSchema = z.object({
    dynamicComponents: z.number().int().nonnegative(),
    unresolvedComponents: z.number().int().nonnegative(),
    ambiguousComponents: z.number().int().nonnegative(),
    unresolvedIdentifiers: z.number().int().nonnegative(),
    ambiguousIdentifiers: z.number().int().nonnegative(),
    expressionParseFailures: z.number().int().nonnegative(),
    unsupportedScopes: z.number().int().nonnegative(),
    unsupportedConstructs: z.number().int().nonnegative(),
});
/** Exact refusal counts. Diagnostics are bounded, so they cannot serve as counters. */
export const ParserRefusalCountsSchema = z.object({
    dynamicDispatch: z.number().int().nonnegative(),
    vueTemplate: VueTemplateRefusalCountsSchema.optional(),
    /**
     * Assembly-owned usage-join refusals, kept apart from the parser's counts so
     * re-assembling a persisted file replaces them instead of compounding them.
     */
    vueTemplateJoin: VueTemplateRefusalCountsSchema.optional(),
});
/** Per-file evidence provenance. Absent means a legacy ParsedFile. */
export const ParserProvenanceSchema = z.object({
    parserName: z.literal('occ.code.parsers'),
    parserVersion: z.number().int().positive(),
    tier: ParserTierSchema,
    grammar: z.string().optional(),
    syntax: z.object({
        status: ParserSyntaxStatusSchema,
        errorCount: z.number().int().nonnegative(),
        diagnostics: z.array(ParserDiagnosticSchema).max(100),
        diagnosticsTruncated: z.boolean(),
    }),
    resolver: z.object({
        tier: ParserResolverTierSchema,
        coverage: ParserFactCoverageSchema,
        diagnostics: z.array(ParserDiagnosticSchema).max(100).optional(),
        diagnosticsTruncated: z.boolean().optional(),
    }),
    capabilities: ParserCapabilitiesSchema,
    refusals: ParserRefusalCountsSchema.optional(),
});
/** Aggregate evidence counts shared by status and per-language payload summaries. */
export const ParserEvidenceSummarySchema = z.object({
    totalFiles: z.number().int().nonnegative(),
    provenanceFiles: z.number().int().nonnegative(),
    legacyFiles: z.number().int().nonnegative(),
    parserVersions: z.array(z.number().int().positive()),
    checkerFiles: z.number().int().nonnegative(),
    syntaxFiles: z.number().int().nonnegative(),
    checkerFallbackFiles: z.number().int().nonnegative(),
    recoveredFiles: z.number().int().nonnegative(),
    fallbackFiles: z.number().int().nonnegative(),
    degradedFiles: z.number().int().nonnegative(),
});
export const ObservedLanguageCapabilitiesSchema = ParserEvidenceSummarySchema.extend({
    capabilities: ParserCapabilitiesSchema,
});
/** Actual parser evidence rolled up by language; never a static parser declaration. */
export const ObservedCapabilitiesSchema = z.record(z.string(), ObservedLanguageCapabilitiesSchema);
/** TypeScript semantic-resolution policy. `preferred` is the default. */
export const TypeScriptCheckerModeSchema = z.enum(['preferred', 'required', 'disabled']);
export const CodeNodeTypeSchema = z.enum([
    'file',
    'module',
    'function',
    'class',
    'interface',
    'type-alias',
    'enum',
    'variable',
    'parameter',
]);
export const NormalizedSymbolKindSchema = z.enum([
    'file',
    'module',
    'function',
    'method',
    'class',
    'interface',
    'type',
    'enum',
    'variable',
    'parameter',
    'other',
]);
export function toNormalizedSymbolKind(type, containerName) {
    switch (type) {
        case 'file':
            return 'file';
        case 'module':
            return 'module';
        case 'function':
            return containerName ? 'method' : 'function';
        case 'class':
            return 'class';
        case 'interface':
            return 'interface';
        case 'type-alias':
            return 'type';
        case 'enum':
            return 'enum';
        case 'variable':
            return 'variable';
        case 'parameter':
            return 'parameter';
        default:
            return 'other';
    }
}
export const CodeEdgeTypeSchema = z.enum([
    'contains',
    'imports',
    'calls',
    'uses',
    'inherits',
    'implements',
    'has_parameter',
]);
export const ResolutionStatusSchema = z.enum(['resolved', 'ambiguous', 'unresolved']);
/** Evidence used to resolve one graph edge. */
export const ResolutionBasisSchema = z.enum([
    'containment',
    'module-resolution',
    'lexical-scope',
    'explicit-import',
    'typescript-checker',
    'inheritance',
    'receiver-self',
    'receiver-super',
    'language-symbol-table',
    'vue-component-registration',
    'vue-template-binding',
    'vue-self',
    'heuristic-name',
    'none',
]);
/** Completeness of the evidence behind an edge result. */
export const ResolutionCoverageSchema = z.enum(['full', 'partial', 'unavailable']);
export const ImportKindSchema = z.enum(['local', 'external', 'unresolved']);
export const ContentModeSchema = z.enum(['none', 'excerpt', 'full']);
export const CodeCapabilitiesSchema = z.object({
    definitions: z.boolean(),
    imports: z.boolean(),
    calls: z.boolean(),
    inheritance: z.boolean(),
    content: z.boolean(),
    /** Static support for a distinct template usage surface. */
    templateUses: z.boolean().optional(),
});
export const CodeSkippedFileSchema = z.object({
    path: z.string(),
    relativePath: z.string(),
    reason: z.string(),
});
export const ParsedImportBindingSchema = z.object({
    localName: z.string(),
    importedName: z.string().optional(),
    isNamespace: z.boolean().optional(),
    isDefault: z.boolean().optional(),
    typeOnly: z.boolean().optional(),
});
export const ParsedImportSchema = z.object({
    specifier: z.string(),
    line: z.number(),
    /** Exact source coordinates of the import/re-export reference when available. */
    span: CodeSourceSpanV1Schema.optional(),
    kind: ImportKindSchema,
    bindings: z.array(ParsedImportBindingSchema),
    resolvedPath: z.string().optional(),
    isReExport: z.boolean().optional(),
});
export const ParsedExportSchema = z.object({
    /** Name visible to importers (`default`, an alias, or `*`). */
    exportedName: z.string(),
    /** Declaration name in this file, when the export is local. */
    localName: z.string().optional(),
    /** Name requested from a re-export source. */
    sourceName: z.string().optional(),
    specifier: z.string().optional(),
    resolvedPath: z.string().optional(),
    line: z.number().int().positive(),
    span: CodeSourceSpanV1Schema.optional(),
    kind: z.enum(['local', 'default', 're-export', 'star', 'namespace', 'commonjs']),
    typeOnly: z.boolean().optional(),
});
export const ParsedScopeSchema = z.object({
    id: z.string(),
    kind: z.enum(['module', 'namespace', 'class', 'function', 'block', 'object']),
    parentId: z.string().optional(),
    ownerSymbolId: z.string().optional(),
    span: CodeSourceSpanV1Schema.optional(),
});
export const ParsedSemanticTargetSchema = z.object({
    path: z.string(),
    type: z.enum(['function', 'class', 'interface', 'type-alias', 'enum', 'variable']),
    name: z.string(),
    line: z.number().int().positive(),
    containerName: z.string().optional(),
    localId: z.string().optional(),
});
export const ParsedSemanticResolutionSchema = z
    .object({
    status: ResolutionStatusSchema,
    basis: ResolutionBasisSchema,
    target: ParsedSemanticTargetSchema.optional(),
    candidates: z.array(ParsedSemanticTargetSchema).optional(),
})
    .superRefine((resolution, context) => {
    if (resolution.status === 'resolved' && !resolution.target) {
        context.addIssue({ code: 'custom', path: ['target'], message: 'Resolved semantic evidence requires a target.' });
    }
    if (resolution.status !== 'resolved' && resolution.target) {
        context.addIssue({
            code: 'custom',
            path: ['target'],
            message: 'Only resolved semantic evidence may carry a target.',
        });
    }
    if (resolution.status === 'resolved' && (resolution.basis === 'none' || resolution.basis === 'heuristic-name')) {
        context.addIssue({
            code: 'custom',
            path: ['basis'],
            message: 'Resolved semantic evidence requires a trusted basis.',
        });
    }
});
export const ParsedSymbolSchema = z.object({
    type: z.enum(['function', 'class', 'interface', 'type-alias', 'enum', 'variable']),
    name: z.string(),
    /** Id of this symbol's graph node; stamped during index assembly. */
    nodeId: z.string().optional(),
    /** Pre-v2 line-derived id, retained as a compatibility lookup alias. */
    legacyNodeId: z.string().optional(),
    /** Parser-local declaration identity, independent of graph-node ids. */
    localId: z.string().optional(),
    scopeId: z.string().optional(),
    bodyScopeId: z.string().optional(),
    /** Parser-local identity of the declaration that contains this symbol. */
    containerLocalId: z.string().optional(),
    line: z.number(),
    endLine: z.number().optional(),
    startColumn: z.number().optional(),
    endColumn: z.number().optional(),
    /** Canonical raw-file span. Absent on legacy and inexact parser results. */
    span: CodeSourceSpanV1Schema.optional(),
    containerName: z.string().optional(),
    signature: z.string().optional(),
    exported: z.boolean().optional(),
    fullSignature: z.string().optional(),
    returnType: z.string().optional(),
    jsdoc: z.string().optional(),
    isAsync: z.boolean().optional(),
    isStatic: z.boolean().optional(),
    visibility: z.enum(['public', 'protected', 'private']).optional(),
    /**
     * Set when this symbol is a test block rather than an ordinary declaration.
     *
     * The graph keeps calling it a `function` — a test block is a callback and
     * behaves like one for call attribution, and inventing a node type for it
     * would change the edge taxonomy for every consumer. Entity extraction reads
     * this to give it its own kind.
     */
    testRole: z.enum(['test', 'test_suite']).optional(),
    /**
     * Set when this symbol is reached through a receiver rather than called free.
     *
     * `containerName` cannot answer that question on its own: it names an
     * enclosing class or suite, and the members of an object literal — the usual
     * way a factory exposes an API — have no such enclosure. Without this,
     * receiver-aware call resolution has no way to tell `holder.get()` from a
     * free function and drops the edge.
     */
    isMember: z.boolean().optional(),
    /**
     * Which object literal or class body this member belongs to, as an opaque
     * per-file key.
     *
     * `containerName` cannot stand in: an object literal and a class expression
     * both have members and neither has a name to give them. Resolution needs the
     * distinction so that `this.other()` reaches the caller's own siblings and
     * nothing else in the file.
     */
    memberOwner: z.string().optional(),
    /** True for a compatibility node synthesized by OCC rather than declared verbatim. */
    synthetic: z.boolean().optional(),
});
export const ParsedCallSchema = z.object({
    callerName: z.string(),
    callerLine: z.number(),
    callerContainerName: z.string().optional(),
    calleeName: z.string(),
    qualifier: z.string().optional(),
    /** Parser-proved receiver semantics; spelling alone is never trusted. */
    receiverKind: z.enum(['self', 'super']).optional(),
    line: z.number(),
    /** Exact source coordinates of the callee reference when available. */
    span: CodeSourceSpanV1Schema.optional(),
    callerLocalId: z.string().optional(),
    scopeId: z.string().optional(),
    semanticResolution: ParsedSemanticResolutionSchema.optional(),
});
export const CodeUsageKindSchema = z.enum(['vue-component', 'vue-expression']);
/**
 * A parser-proved source reference that is not necessarily an executable call.
 *
 * `targetLocalId` joins a declaration in the same parsed file. Without it,
 * `targetName` names an explicit runtime import binding and assembly must join
 * that binding to exactly one exported declaration before emitting an edge.
 */
export const ParsedUsageSchema = z
    .object({
    usageKind: CodeUsageKindSchema,
    sourceLocalId: z.string(),
    targetName: z.string(),
    referenceName: z.string().optional(),
    targetLocalId: z.string().optional(),
    line: z.number().int().positive(),
    span: CodeSourceSpanV1Schema,
    resolutionBasis: ResolutionBasisSchema,
})
    .superRefine((usage, context) => {
    if (!usage.span.exact || usage.span.startByte === undefined || usage.span.endByte === undefined) {
        context.addIssue({
            code: 'custom',
            path: ['span'],
            message: 'A template usage requires an exact byte-addressable source span.',
        });
    }
    const validBasis = usage.usageKind === 'vue-component'
        ? usage.resolutionBasis === 'explicit-import' ||
            usage.resolutionBasis === 'vue-component-registration' ||
            usage.resolutionBasis === 'vue-self'
        : usage.resolutionBasis === 'vue-template-binding' || usage.resolutionBasis === 'explicit-import';
    if (!validBasis) {
        context.addIssue({
            code: 'custom',
            path: ['resolutionBasis'],
            message: 'A template usage requires evidence matching its usage kind.',
        });
    }
});
export const ParsedInheritanceSchema = z.object({
    className: z.string(),
    classLine: z.number(),
    baseName: z.string(),
    line: z.number(),
    /** Exact source coordinates of the base-type reference when available. */
    span: CodeSourceSpanV1Schema.optional(),
    kind: z.enum(['extends', 'implements']).optional(),
    qualifier: z.string().optional(),
    /** Lexical scope containing the derived declaration. */
    scopeId: z.string().optional(),
    semanticResolution: ParsedSemanticResolutionSchema.optional(),
});
export const CodeExcerptSchema = z.object({
    content: z.string(),
    startLine: z.number(),
    endLine: z.number(),
    reason: z.string(),
});
export const ParsedFileSchema = z.object({
    path: z.string(),
    relativePath: z.string(),
    moduleName: z.string(),
    language: z.string(),
    content: z.string().optional(),
    lines: z.array(z.string()).optional(),
    excerpt: CodeExcerptSchema.optional(),
    symbols: z.array(ParsedSymbolSchema),
    imports: z.array(ParsedImportSchema),
    exports: z.array(ParsedExportSchema).optional(),
    scopes: z.array(ParsedScopeSchema).optional(),
    calls: z.array(ParsedCallSchema),
    /** Additive non-call usage observations; absent on parser contracts before v4. */
    usages: z.array(ParsedUsageSchema).optional(),
    inheritances: z.array(ParsedInheritanceSchema),
    /** Actual parser/resolver evidence for this file. Absent means legacy/unknown. */
    provenance: ParserProvenanceSchema.optional(),
});
export const IndexTruncationSchema = z.object({
    reason: z.enum(['maxFiles', 'maxBytes', 'maxFilesAndBytes']),
    keptFiles: z.number(),
    droppedFiles: z.number(),
    totalFiles: z.number(),
    totalBytes: z.number(),
    maxFiles: z.number().optional(),
    maxBytes: z.number().optional(),
});
export const CodeNodeSchema = z.object({
    id: z.string(),
    /** Pre-v2 line-derived id. Consumers must resolve it only when unique. */
    legacyNodeId: z.string().optional(),
    type: CodeNodeTypeSchema,
    name: z.string(),
    path: z.string(),
    relativePath: z.string().optional(),
    line: z.number().optional(),
    endLine: z.number().optional(),
    startColumn: z.number().optional(),
    endColumn: z.number().optional(),
    /** Canonical raw-file span copied from the parsed symbol when available. */
    span: CodeSourceSpanV1Schema.optional(),
    language: z.string(),
    moduleName: z.string().optional(),
    containerId: z.string().optional(),
    containerName: z.string().optional(),
    signature: z.string().optional(),
    external: z.boolean().optional(),
});
export const CodeEdgeCandidateSchema = z.object({
    name: z.string(),
    nodeId: z.string().optional(),
    type: CodeNodeTypeSchema.optional(),
    relativePath: z.string().optional(),
    line: z.number().optional(),
    endLine: z.number().optional(),
    containerName: z.string().optional(),
    span: CodeSourceSpanV1Schema.optional(),
});
export const CodeEdgeSchema = z
    .object({
    id: z.string(),
    type: CodeEdgeTypeSchema,
    from: z.string(),
    to: z.string().optional(),
    fromPath: z.string(),
    toPath: z.string().optional(),
    line: z.number().optional(),
    /** Exact source coordinates of the edge-producing reference when available. */
    span: CodeSourceSpanV1Schema.optional(),
    status: ResolutionStatusSchema,
    /** Why this status was reached. Absent on indexes produced before schema v1. */
    resolutionBasis: ResolutionBasisSchema.optional(),
    /** Completeness of the evidence consulted for this edge. */
    resolutionCoverage: ResolutionCoverageSchema.optional(),
    targetName: z.string().optional(),
    detail: z.string().optional(),
    specifier: z.string().optional(),
    importKind: ImportKindSchema.optional(),
    usageKind: CodeUsageKindSchema.optional(),
    candidates: z.array(CodeEdgeCandidateSchema).optional(),
})
    .superRefine((edge, context) => {
    if (edge.status === 'resolved' && !edge.to) {
        context.addIssue({ code: 'custom', path: ['to'], message: 'A resolved edge requires a target.' });
    }
    if (edge.status !== 'resolved' && edge.to) {
        context.addIssue({ code: 'custom', path: ['to'], message: 'Only a resolved edge may carry a target.' });
    }
    if (edge.status === 'resolved' && (edge.resolutionBasis === 'none' || edge.resolutionBasis === 'heuristic-name')) {
        context.addIssue({
            code: 'custom',
            path: ['resolutionBasis'],
            message: 'A resolved edge requires trusted evidence.',
        });
    }
    if (edge.type === 'uses') {
        if (edge.status !== 'resolved') {
            context.addIssue({ code: 'custom', path: ['status'], message: 'A usage edge must be resolved.' });
        }
        if (!edge.usageKind) {
            context.addIssue({ code: 'custom', path: ['usageKind'], message: 'A usage edge requires its usage kind.' });
        }
        if (!edge.span?.exact || edge.span.startByte === undefined || edge.span.endByte === undefined) {
            context.addIssue({
                code: 'custom',
                path: ['span'],
                message: 'A usage edge requires an exact byte-addressable source span.',
            });
        }
    }
    else if (edge.usageKind) {
        context.addIssue({ code: 'custom', path: ['usageKind'], message: 'Only a usage edge may carry a usage kind.' });
    }
});
export const CodebaseIndexSchema = z.object({
    /** Missing only on legacy, pre-versioned indexes. */
    schemaVersion: z.literal(CODEBASE_INDEX_SCHEMA_VERSION).optional(),
    repoRoot: z.string(),
    contentMode: ContentModeSchema,
    files: z.array(ParsedFileSchema),
    nodes: z.array(CodeNodeSchema),
    edges: z.array(CodeEdgeSchema),
    capabilities: z.record(z.string(), CodeCapabilitiesSchema),
    /** Parser observations intentionally withheld because no trustworthy target existed. */
    parserRefusals: ParserRefusalCountsSchema.optional(),
    skippedFiles: z.array(CodeSkippedFileSchema),
    truncated: IndexTruncationSchema.optional(),
});
export const DirectoryTreeModeSchema = z.enum(['compact', 'verbose', 'map']);
export const DirectoryTreeOptionsSchema = z.object({
    mode: DirectoryTreeModeSchema.optional(),
    maxDepth: z.number().int().nonnegative().optional(),
    maxEntriesPerDir: z.number().int().positive().optional(),
    tokenBudget: z.number().int().positive().optional(),
    byteBudget: z.number().int().positive().optional(),
    focusPath: z.string().optional(),
    tokens: z.boolean().optional(),
    minTokens: z.number().int().nonnegative().optional(),
    top: z.number().int().positive().optional(),
    mapTokens: z.number().int().positive().optional(),
});
export const DirectoryTreeSymbolSchema = z.object({
    nodeId: z.string().optional(),
    name: z.string(),
    kind: NormalizedSymbolKindSchema,
    signature: z.string(),
    line: z.number().optional(),
    relativePath: z.string(),
});
export const DirectoryTreeEntrySchema = z.object({
    kind: z.enum(['directory', 'file']),
    path: z.string(),
    name: z.string(),
    depth: z.number(),
    fileCount: z.number().optional(),
    language: z.string().optional(),
    score: z.number().optional(),
    normalizedScore: z.number().optional(),
    symbols: z.array(DirectoryTreeSymbolSchema).optional(),
    tokens: z.number().int().nonnegative().optional(),
    pct: z.number().nonnegative().optional(),
    files: z.number().int().nonnegative().optional(),
});
export const DirectoryTreeResultSchema = z.object({
    root: z.string(),
    focusPath: z.string().optional(),
    mode: DirectoryTreeModeSchema,
    maxDepth: z.number(),
    maxEntriesPerDir: z.number(),
    tokenBudget: z.number().optional(),
    tokensUsed: z.number(),
    byteBudget: z.number().optional(),
    bytesUsed: z.number(),
    totalFiles: z.number(),
    totalDirectories: z.number(),
    entries: z.array(DirectoryTreeEntrySchema),
    lines: z.array(z.string()),
    truncated: z.boolean(),
    truncatedPaths: z.array(z.string()),
    totalTokens: z.number().int().nonnegative().optional(),
    tokenizer: z.string().optional(),
    mapTokens: z.number().int().positive().optional(),
    wouldFitBudget: z.boolean().optional(),
    minTokens: z.number().int().nonnegative().optional(),
    top: z.number().int().positive().optional(),
    honesty: z.object({
        freshness: z.literal('index-snapshot'),
        stalenessReason: z.string(),
        truncation: z.object({
            truncated: z.boolean(),
            omittedPaths: z.number().int().nonnegative(),
        }),
        coverage: z.object({
            countedFiles: z.number().int().nonnegative(),
            totalFiles: z.number().int().nonnegative(),
            unavailableFiles: z.number().int().nonnegative(),
        }),
        ambiguityCount: z.number().int().nonnegative(),
    }),
});
export const CodeSearchResultSchema = z.object({
    node: CodeNodeSchema,
    resolution: ResolutionStatusSchema.optional(),
    ...SourceInlineProjectionSchema.shape,
});
export const ContentMatchSchema = z.object({
    path: z.string(),
    relativePath: z.string(),
    language: z.string(),
    line: z.number(),
    snippet: z.string(),
});
export const RelationMatchSchema = z.object({
    from: CodeNodeSchema,
    edge: CodeEdgeSchema,
    to: CodeNodeSchema.optional(),
    ...SourceInlineProjectionSchema.shape,
});
export const DependencyAnalysisSchema = z.object({
    target: z.string(),
    targetFile: CodeNodeSchema.optional(),
    importers: z.array(RelationMatchSchema),
    localImports: z.array(RelationMatchSchema),
    externalImports: z.array(RelationMatchSchema),
    unresolvedImports: z.array(RelationMatchSchema),
});
export const ClassTreeAnalysisSchema = z.object({
    target: CodeNodeSchema,
    parents: z.array(RelationMatchSchema),
    children: z.array(RelationMatchSchema),
    methods: z.array(CodeNodeSchema),
});
export const CallChainSchema = z.object({
    nodes: z.array(CodeNodeSchema),
    edges: z.array(CodeEdgeSchema),
    direction: z.enum(['forward', 'reverse']).optional(),
    status: z.enum(['resolved', 'blocked_ambiguous']).optional(),
    blockedAt: CodeNodeSchema.optional(),
    blockedBy: CodeEdgeSchema.optional(),
});
export const CodeSourceCandidateSchema = z.object({
    nodeId: z.string(),
    name: z.string(),
    type: CodeNodeTypeSchema,
    path: z.string(),
    relativePath: z.string().optional(),
    line: z.number().optional(),
    endLine: z.number().optional(),
    span: CodeSourceSpanV1Schema.optional(),
    containerName: z.string().optional(),
});
/**
 * Line-independent symbol identity used to address a symbol across edits.
 * Unlike node ids (which embed the 1-based line), a key survives edits above
 * the symbol; `kind` is optional to mirror `readCodeSource` name-based
 * resolution.
 */
export const SymbolKeyQuerySchema = z.object({
    relativePath: z.string(),
    kind: CodeNodeTypeSchema.optional(),
    name: z.string(),
    containerName: z.string().optional(),
    expected: z
        .object({
        bodyHash: z.string().regex(/^[0-9a-f]{64}$/),
        normalizedHash: z.string().regex(/^[0-9a-f]{64}$/),
        structuralHash: z.string().regex(/^[0-9a-f]{64}$/),
        exactSpanHash: z
            .string()
            .regex(/^[0-9a-f]{64}$/)
            .optional(),
        /**
         * Version of the normalizing lexer that produced the stored tiers.
         * When present and different from the current `ENTITY_HASH_VERSION`,
         * comparison refuses (`unknown-tier`) instead of misreporting the
         * version bump as drift. Absent tuples compare as before, with the
         * documented residual risk on a future version bump.
         */
        hashVersion: z.number().int().positive().optional(),
    })
        .optional(),
});
/**
 * Mechanical comparison of one stored symbol-hash tuple with current bytes.
 *
 * `cosmetic-drift` is the legacy label and remains a permanently decodable
 * arm. `normalized-equal` is the 0.20 default: body bytes changed while the frozen normalizing lexer emitted
 * the same token stream; it is an identity heuristic, not a semantic no-op
 * claim. `semantic-drift` likewise means only that the normalized token stream
 * differs, not that runtime behavior differs.
 */
export const SymbolHashVerdictSchema = z.enum([
    'fresh',
    'cosmetic-drift',
    'normalized-equal',
    'semantic-drift',
    'gone',
    'unknown-tier',
]);
export const SymbolVerificationHashesSchema = z.object({
    hashVersion: z.number().int().positive(),
    algo: z.enum(['lexer', 'declaration', 'none']),
    bodyHash: z.string(),
    normalizedHash: z.string().optional(),
    structuralHash: z.string().optional(),
    /** Hash of the parser-proved exact UTF-8 byte span when available. */
    exactSpanHash: z
        .string()
        .regex(/^[0-9a-f]{64}$/)
        .optional(),
});
/** Fully resolved form of a symbol key (kind always known after resolution). */
export const SymbolKeySchema = z.object({
    relativePath: z.string(),
    kind: CodeNodeTypeSchema,
    name: z.string(),
    containerName: z.string().optional(),
});
export const HashedSymbolSchema = z.object({
    key: SymbolKeySchema,
    nodeId: z.string(),
    line: z.number(),
    endLine: z.number(),
    signature: z.string().optional(),
    span: CodeSourceSpanV1Schema.optional(),
    /**
     * Frozen contract (feature key `code.symbol.bodyHash`): full 64-char
     * lowercase hex sha256 over the UTF-8 bytes of the legacy full-line source
     * slice `readCodeSource` returns for the node. Consumers persist this as a
     * long-lived baseline; changing algorithm or normalization is breaking.
     */
    bodyHash: z.string(),
    /** Additive exact-span identity; `bodyHash` remains the frozen line-slice contract. */
    exactSpanHash: z
        .string()
        .regex(/^[0-9a-f]{64}$/)
        .optional(),
    /** Version and availability of the verification hash tiers below. */
    hashVersion: z.number().int().positive(),
    hashAlgo: z.enum(['lexer', 'declaration', 'none']),
    normalizedHash: z.string().optional(),
    structuralHash: z.string().optional(),
    /**
     * Versioned verification tiers. `bodyHash` is repeated here so callers can
     * persist one tuple and later pass its three hashes back as `expected`.
     * Persist `hashVersion` with the tuple: a stored tuple without it cannot be
     * distinguished from drift after a normalizing-lexer version bump.
     */
    hashes: SymbolVerificationHashesSchema,
    /**
     * Compatibility flag: historically true for a multi-line body span. New
     * compiler/Tree-sitter one-line declarations may also report true when exact
     * bytes were recovered. Use `exactSpanHash` or `span.exact` when byte-exact
     * evidence is required.
     */
    spanExact: z.boolean(),
});
export const SymbolHashLineageResolutionSchema = z.discriminatedUnion('status', [
    z.object({ status: z.literal('resolved'), successor: CodeSourceCandidateSchema }),
    z.object({ status: z.literal('unavailable') }),
    z.object({ status: z.literal('ambiguous'), candidates: z.array(CodeSourceCandidateSchema) }),
]);
export const HashCodeSymbolsResultSchema = z.discriminatedUnion('status', [
    z.object({
        status: z.literal('resolved'),
        query: SymbolKeyQuerySchema,
        symbol: HashedSymbolSchema,
        verdict: SymbolHashVerdictSchema.optional(),
    }),
    z.object({
        status: z.literal('not_found'),
        query: SymbolKeyQuerySchema,
        candidates: z.array(CodeSourceCandidateSchema),
        message: z.string(),
        verdict: SymbolHashVerdictSchema.optional(),
        lineage: SymbolHashLineageResolutionSchema.optional(),
    }),
    z.object({
        status: z.literal('ambiguous'),
        query: SymbolKeyQuerySchema,
        candidates: z.array(CodeSourceCandidateSchema),
        message: z.string(),
    }),
    z.object({ status: z.literal('error'), query: SymbolKeyQuerySchema.optional(), message: z.string() }),
]);
export const CodeSeamCochangeSchema = z.object({
    count: z.number(),
    lift: z.number(),
    lastCochangedAt: z.string(),
});
/**
 * A seam: a cross-module relationship derived at query time from import/call
 * edges between directory-modules, optionally weighted by historical
 * co-change. `seamKey` contract: `'seam:' + [moduleA, moduleB].sort().join('::')`
 * — order-independent and stable while both directories exist. Directory
 * renames change the key by design; consumers treat an unresolvable key as
 * drift, never as clean.
 */
export const CodeSeamSchema = z.object({
    seamKey: z.string(),
    moduleA: z.string(),
    moduleB: z.string(),
    importEdgeCount: z.number(),
    callEdgeCount: z.number(),
    cochange: CodeSeamCochangeSchema.optional(),
    /** 1-based position in the full ranked enumeration (before minLift/limit). */
    rank: z.number(),
    ...SourceInlineProjectionSchema.shape,
});
export const ListCodeSeamsOptionsSchema = z.object({
    /** Caller-supplied co-change summary (from `getGitCochange`) to join in. */
    cochange: GitCochangeSummarySchema.optional(),
    minLift: z.number().optional(),
    limit: z.number().int().positive().optional(),
});
export const ResolveCodeSeamsResultSchema = z.object({
    seamKey: z.string(),
    status: z.enum(['resolved', 'missing']),
    seam: CodeSeamSchema.optional(),
});
export const CodeCommandPayloadSchema = z.object({
    repo: z.string(),
    contentMode: ContentModeSchema,
    meta: z
        .object({
        loadSource: z.enum(['cache', 'build']),
    })
        .optional(),
    query: z.record(z.string(), z.unknown()),
    results: z.unknown(),
    stats: z.object({
        filesIndexed: z.number(),
        nodes: z.number(),
        edges: z.number(),
        skippedFiles: z.number(),
    }),
    capabilities: z.record(z.string(), CodeCapabilitiesSchema),
    /** Present on current producers; optional so persisted pre-feature envelopes remain decodable. */
    observedCapabilities: ObservedCapabilitiesSchema.optional(),
});
//# sourceMappingURL=types.js.map