Project Files
src / types.ts
/**
* @file types.ts
* Shared TypeScript types for the entire plugin.
*/
/** A raw search result before scoring. */
export interface SearchHit {
readonly title: string;
readonly url: string;
readonly snippet: string;
}
/** A fully extracted web page. */
export interface ExtractedPage {
readonly url: string;
readonly finalUrl: string;
readonly title: string;
readonly description: string;
readonly published: string | null;
readonly text: string;
readonly wordCount: number;
readonly outlinks: ReadonlyArray<Outlink>;
}
export interface Outlink {
readonly text: string;
readonly href: string;
}
export type SourceTier =
| "academic"
| "government"
| "reference"
| "news"
| "professional"
| "general"
| "low";
export interface ScoredCandidate {
readonly url: string;
readonly title: string;
readonly snippet: string;
readonly query: string;
readonly domainScore: number;
readonly freshnessScore: number;
readonly urlQuality: number;
readonly totalScore: number;
readonly tier: SourceTier;
}
export type WorkerRole =
| "breadth"
| "depth"
| "recency"
| "academic"
| "critical"
| "statistical"
| "regulatory"
| "technical"
| "primary"
| "comparative";
/**
* The orchestrator generates custom worker specs tailored to each topic.
*/
export interface DynamicWorkerSpec {
readonly role: WorkerRole;
readonly label: string;
readonly queries: ReadonlyArray<string>;
readonly budgetWeight: number;
readonly followLinks: boolean;
readonly preferredTiers?: ReadonlyArray<SourceTier>;
}
/** A single unit of parallel work assigned to one swarm worker. */
export interface SwarmTask {
readonly id: string;
readonly role: WorkerRole;
readonly label: string;
readonly queries: ReadonlyArray<string>;
readonly pageBudget: number;
readonly contentLimit: number;
readonly followLinks: boolean;
readonly safeSearch: "strict" | "moderate" | "off";
readonly preferredTiers?: ReadonlyArray<SourceTier>;
readonly searchResultsPerQuery: number;
readonly maxPagesPerDomain: number;
readonly maxLinksToEvaluate: number;
readonly maxLinksToFollow: number;
readonly candidatePoolMultiplier: number;
readonly workerConcurrency: number;
readonly minRelevanceScore: number;
readonly maxOutlinksPerPage: number;
readonly searchPages: number;
readonly extraEngines: ReadonlyArray<string>;
readonly linkCrawlDepth: number;
readonly queryMutationThreshold: number;
readonly enableLocalSources: boolean;
readonly localCollectionIds?: ReadonlyArray<string>;
readonly roleCollectionMap?: ReadonlyMap<string, ReadonlyArray<string>>;
}
/** Result produced by a single swarm worker. */
export interface WorkerResult {
readonly taskId: string;
readonly role: WorkerRole;
readonly label: string;
readonly sources: ReadonlyArray<CrawledSource>;
readonly queries: ReadonlyArray<string>;
readonly errors: ReadonlyArray<string>;
}
/** A crawled and extracted page, enriched with research metadata. */
export interface CrawledSource {
readonly url: string;
readonly finalUrl: string;
readonly title: string;
readonly description: string;
readonly published: string | null;
readonly text: string;
readonly wordCount: number;
readonly outlinks: ReadonlyArray<Outlink>;
readonly sourceQuery: string;
readonly workerRole: WorkerRole;
readonly workerLabel: string;
readonly domainScore: number;
readonly freshnessScore: number;
readonly tier: SourceTier;
readonly relevanceScore: number;
readonly origin: SourceOrigin;
}
export interface AgentMessage {
readonly fromWorker: string;
readonly keyFindings: ReadonlyArray<string>;
readonly suggestedFollowUps: ReadonlyArray<string>;
}
export interface ContradictionEntry {
readonly claim: string;
readonly sourceA: {
readonly index: number;
readonly title: string;
readonly stance: string;
};
readonly sourceB: {
readonly index: number;
readonly title: string;
readonly stance: string;
};
readonly severity: "minor" | "moderate" | "major";
}
export interface ResearchDimension {
readonly id: string;
readonly label: string;
readonly keywords: ReadonlyArray<string>;
readonly queries: (topic: string) => ReadonlyArray<string>;
}
export interface QueryPlan {
readonly queriesByRole: Readonly<Record<WorkerRole, ReadonlyArray<string>>>;
readonly usedAI: boolean;
readonly topicKeywords: ReadonlyArray<string>;
readonly dynamicSpecs?: ReadonlyArray<DynamicWorkerSpec>;
}
/** Adaptive gap-fill plan with targeted worker role per gap. */
export interface AdaptiveGapPlan {
readonly role: WorkerRole;
readonly label: string;
readonly queries: ReadonlyArray<string>;
readonly followLinks: boolean;
readonly preferredTiers?: ReadonlyArray<SourceTier>;
}
export interface ReportSource {
readonly index: number;
readonly url: string;
readonly title: string;
readonly description: string;
readonly published: string | null;
readonly text: string;
readonly wordCount: number;
readonly sourceQuery: string;
readonly workerRole: WorkerRole;
readonly workerLabel: string;
readonly domainScore: number;
readonly freshnessScore: number;
readonly tier: SourceTier;
readonly relevanceScore: number;
readonly origin: SourceOrigin;
}
export interface CompiledReport {
readonly markdown: string;
readonly sources: ReadonlyArray<ReportSource>;
readonly topicKeywords: ReadonlyArray<string>;
readonly coveredDims: ReadonlyArray<string>;
readonly gapDims: ReadonlyArray<string>;
readonly aiSynthesis?: string;
readonly contradictions: ReadonlyArray<ContradictionEntry>;
}
export type SourceOrigin = "web" | "local";
export interface ResearchConfig {
readonly topic: string;
readonly focusAreas: ReadonlyArray<string>;
readonly depthPreset: import("./constants").DepthPreset;
readonly contentLimitPerPage: number;
readonly enableLinkFollowing: boolean;
readonly enableAIPlanning: boolean;
readonly safeSearch: "strict" | "moderate" | "off";
readonly enableLocalSources: boolean;
readonly localCollectionIds?: ReadonlyArray<string>;
readonly roleCollectionMap?: ReadonlyMap<string, ReadonlyArray<string>>;
}
export interface ResearchResult {
readonly report: CompiledReport;
readonly queriesUsed: ReadonlyArray<string>;
readonly totalSources: number;
readonly totalRounds: number;
}
export type StatusFn = (message: string) => void;
export type WarnFn = (message: string) => void;