/** * Read-only Mastra `apiRoutes` exposing the factory project's knowledge graph. * * Serves the Knowledge page in factory-ui: a polling graph snapshot (nodes * as nodes, wikilink edges derived from record text), a node flyout payload * with per-record provenance, and the recent activity feed. Every endpoint is a * GET — this module never writes knowledge. * * Scoping is fail-closed: the org and resource rungs are derived server-side * from the authenticated caller and the validated `:id` project. The DEFAULT * view queries `[org:, resource:]` (org + project records). * Thread-scoped records are reachable ONLY via an explicit, server-validated * `threadId` query parameter (the drill-down view), which appends the thread * rung to the query scope. A thread is drillable iff it produced knowledge * visible under the caller's org/project prefix; unknown or cross-org threads * 404 — never a silent fallback to the default view. */ import type { ApiRoute } from '@mastra/core/server'; import type { KnowledgeScope, KnowledgeStorage } from '@mastra/core/storage'; import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js'; import type { RouteDependencies } from './route.js'; import { Route } from './route.js'; /** Window caps. Injectable at construction only — never per-request. */ export interface KnowledgeRouteLimits { /** Max nodes in a graph snapshot (newest-first). */ maxNodes: number; /** Max records parsed for edges per snapshot (newest-first). */ maxRecords: number; /** Max fallback `resolveNode` store lookups per request (deduped per unique name+scope). */ maxFallbackLookups: number; } export interface KnowledgeRoutesDeps extends RouteDependencies { /** Factory projects domain — validates the `:id` project belongs to the caller's org. */ projects: FactoryProjectsStorage; /** Lazy handle to the knowledge storage domain; endpoints 503 when absent. */ knowledge: () => Promise; limits?: Partial; } /** A graph node. `recordCount` is window-derived (records inside the snapshot window only). */ export interface KnowledgeGraphNode { id: string; name: string; kind: string; description?: string; scope: KnowledgeScope; /** Deepest rung of the record's scope: org | resource | thread. */ rung: 'org' | 'resource' | 'thread'; /** * True when a non-deleted pinned record's wikilinks reference ONLY this * node (A9: multi-target pins mark their edges instead — the pin is * about the relationship; a single-target pin has no edge to carry it). */ pinned: boolean; /** Records owned by this node INSIDE the snapshot window (not a total). */ recordCount: number; createdAt: string; updatedAt: string; } export interface KnowledgeGraphEdge { id: string; /** The owning node of the record (its `node`). */ source: string; /** The wikilink-resolved node. */ target: string; /** * Always 'wikilink': the record's `node` is the edge SOURCE, so the * plan's "parent link" collapses into the wikilink edge — nodes carry no * separate parent field to derive a second edge type from. */ type: 'wikilink'; /** The record whose text produced the edge. */ recordId: string; /** * True when the edge is derived from a PINNED record linking two nodes — * the pin marks the relationship, so the accent lives on the edge (A9). */ pinned?: boolean; } /** * A knowledge record as a first-class graph element (A11): every record in the window, * with the in-window nodes it touches. The client renders by arity — * 1 node: a small dot linked to it; 2: the connecting line; 3+: a midpoint * junction splitting to each node. Pin records have their hidden reserved * owner omitted, so their arity comes purely from wikilink targets. */ export interface KnowledgeGraphRecord { /** The record id. */ id: string; /** Owner node first (omitted for pins), then resolved wikilink targets. */ nodeIds: string[]; pinned: boolean; /** Record text, truncated for hover cards. */ text: string; } export interface KnowledgeGraphPayload { view: 'project' | 'thread'; threadId?: string; nodes: KnowledgeGraphNode[]; edges: KnowledgeGraphEdge[]; records: KnowledgeGraphRecord[]; /** True when the node or record window cap was hit (newest-first window). */ truncated: boolean; /** Wikilink targets that resolved in the store but fell outside the node window. */ outOfWindow: Array<{ id: string; name: string; }>; /** Unique unknown names skipped once the fallback-lookup cap was hit. */ unresolvedCapped: { count: number; names: string[]; }; /** Pin counts per rung of the active view (thread is null in the default view). */ pinCensus: { resource: number; thread: number | null; }; /** Change hint: newest knowledge activity id (per-process monotonic — hint only). */ version: string | null; } export interface KnowledgeNodeRecordPayload { id: string; node: string; /** 'owned' when the node is the record's parent, 'mentions' when it only wikilinks it. */ relation: 'owned' | 'mentions'; text: string; scope: KnowledgeScope; rung: 'org' | 'resource' | 'thread'; sourceThreadId: string; capturedAt: string; when?: string; pinned: boolean; metadata?: Record; } export interface KnowledgeNodePayload { node: { id: string; name: string; kind: string; content: string; scope: KnowledgeScope; rung: 'org' | 'resource' | 'thread'; createdAt: string; updatedAt: string; }; records: KnowledgeNodeRecordPayload[]; } export declare class KnowledgeRoutes extends Route { #private; constructor(deps: KnowledgeRoutesDeps); routes(): ApiRoute[]; } //# sourceMappingURL=knowledge.d.ts.map