import type { AugmentConfig } from "./config.js"; import { AugmentDatabase } from "./db.js"; import { type DecoctionDiscoveryCluster } from "./decoction.js"; import type { EmbeddingProvider } from "./embeddings.js"; import type { LinkType, MemoryKind } from "./constants.js"; import { type Proposal, type ProposalStatus } from "./proposals.js"; import { type SleepCluster } from "./sleep.js"; import type { LinkRecord, MemoryRecord, ProjectGraph, ProjectRecord, SearchInput, SearchResult, UpsertInput } from "./types.js"; export interface InitProjectInput { project_name?: string; } export interface UpsertResult { memory: MemoryRecord; action: "created" | "updated" | "similar-updated"; text: string; } export interface SearchOutput { text: string; results: SearchResult[]; } export interface DecoctionRemovalSuggestion { path: string; reason: string; } export interface DecoctionGleanDiscoverInput { action: "discover"; min_cosine?: number; limit?: number; } export interface DecoctionGleanWriteInput { action: "write"; kind: "SPEC" | "ARCH" | "INFO"; name: string; content: string; tags?: string[]; derived_from: string[]; suggested_removals?: DecoctionRemovalSuggestion[]; expected_content_hash?: string; expected_frontmatter_hash?: string; } export type DecoctionGleanInput = DecoctionGleanDiscoverInput | DecoctionGleanWriteInput; export interface DecoctionGleanDiscoverOutput { action: "discover"; text: string; eligible: number; embedded: number; min_cosine: number; min_projects: number; clusters: DecoctionDiscoveryCluster[]; } export interface DecoctionGleanWriteOutput { action: "write"; memory: MemoryRecord; upsert_action: UpsertResult["action"]; suggested_removals: DecoctionRemovalSuggestion[]; warnings: string[]; text: string; } export interface DecoctionCleanupInput { generic_path: string; source_paths: string[]; } export interface DecoctionCleanupResult { generic_path: string; results: Array<{ source_path: string; status: "deleted" | "failed"; retargeted_links: number; error?: string; }>; text: string; } export interface SleepCandidatesInput { /** Restrict to one project; omit to preview candidates in every project (clusters never span projects). */ project_name?: string; min_cosine?: number; min_age_days?: number; /** Max clusters returned (largest first). */ limit?: number; } export interface SleepCandidatesOutput { text: string; clusters: SleepCluster[]; eligible: number; } /** Draft a sleep consolidation: >=2 WORK sources fold into one new SPEC/ARCH/INFO. * The proposal is written OUTSIDE the corpus for review; nothing is consolidated * until {@link AugmentService.sleepApply}. */ export interface SleepProposeInput { project_name: string; kind: "SPEC" | "ARCH" | "INFO"; name: string; content: string; derived_from: string[]; rationale?: string; } export interface SleepApplyInput { filename: string; } export interface SleepApplyOutput { memory: MemoryRecord; archived: string[]; proposal: Proposal; text: string; } /** Draft a speculative memory in the active project from one or more existing * inspirations. Inspirations are deliberately global: they may belong to the * active project, the reserved generic project, or any foreign project. */ export interface DreamMemoryProposeInput { project_name: string; kind: MemoryKind; name: string; content: string; derived_from: string[]; rationale?: string; link?: never; } /** Draft a speculative link. Applying it may only rewrite a source in the * active project, while the target may be current, generic, or foreign. */ export interface DreamLinkProposeInput { project_name: string; link: { from: string; to: string; type: LinkType; }; rationale?: string; kind?: never; name?: never; content?: never; derived_from?: never; } export type DreamProposeInput = DreamMemoryProposeInput | DreamLinkProposeInput; export interface DreamApplyInput { filename: string; } export interface DreamApplyOutput { type: "dream-memory" | "dream-link"; proposal: Proposal; memory?: MemoryRecord; link?: LinkRecord; text: string; } export declare class AugmentService { private readonly config; private readonly embeddingProvider; readonly db: AugmentDatabase; private initialized; /** relative_path → timestamp of the service's own most recent write/delete of that file. */ private readonly selfWrites; /** In-flight or completed snapshot build. Assigned synchronously in {@link corpus} * so a mutation mid-build can clear THIS field; the late-finishing build is only * seen by waiters already awaiting it — never re-published as {@link liveCorpus}. */ private corpusPromise?; /** Fully built snapshot, when one is current. Patched in place by single-memory * writes; cleared with {@link corpusPromise} on full invalidate. Undefined while * a build is mid-flight (promise set but not yet resolved into this field). */ private liveCorpus?; private readonly queryEmbeddingCache; /** FIFO critical section for every corpus mutation. Compound operations use * their private `*Core` helpers while holding this lock so their validation, * frontmatter rewrites, and deletes cannot interleave with another request. */ private mutationTail; constructor(config: AugmentConfig, embeddingProvider?: EmbeddingProvider); useStateDir(stateDir: string): void; close(): Promise; init(): Promise; initProject(input?: InitProjectInput): Promise; listProjects(name?: string): Promise; rebuildIndex(): Promise<{ indexed: number; skipped: number; }>; private rebuildIndexCore; /** * Incremental single-file (re)index — the watcher's per-event path. Re-reads just the changed * file, preserves its row id (path-keyed upsert), and repairs only the link subgraph touching * it. A path whose file no longer exists is dropped from the index (cascade cleans its * embeddings and link rows); the file's frontmatter in OTHER memories is left alone — external * deletion is not authorization to rewrite other files (unlike {@link deleteMemory}). */ indexPath(relativePath: string): Promise; private indexPathCore; /** * Claims a pending self-write marker for `relativePath`. The watcher calls this on every * filesystem event: `true` means the service itself just wrote/deleted that file (its index * work is already done inline) and the event should be ignored; `false` means the edit came * from outside (editor, Dropbox sync) and must be re-indexed. Markers are one-shot and expire. */ consumeSelfWrite(relativePath: string): boolean; private recordSelfWrite; private memoryFileExists; upsert(input: UpsertInput): Promise; private upsertCore; read(id: number): Promise; listMemoriesByProject(projectId: number): Promise; /** Project node plus its memories and the links among them — the dashboard subgraph. */ projectGraph(projectId: number): Promise; deleteMemory(id: number): Promise<{ id: number; relative_path: string; text: string; }>; private deleteMemoryCore; /** Remove every frontmatter link pointing at `targetPath` from all other memories. */ private scrubInboundLinks; /** Resolve a memory by numeric id or by relative path. Ids churn whenever the * index rebuilds; the relative path is the durable identity, so link callers * may pass either. */ private resolveMemoryRef; link(fromRef: number | string, toRef: number | string, typeInput?: string): Promise<{ link: LinkRecord; text: string; }>; private linkCore; listLinks(memoryId: number): Promise; unlink(linkId: number): Promise<{ text: string; }>; private unlinkCore; private invalidateCorpusSnapshot; /** Queue one corpus mutation after the previous request, keeping the queue * usable after failures. Assignment happens synchronously before any caller * can enqueue the next request. */ private serializeMutation; private corpus; /** * Patch one memory into the live snapshot (row, tokens, embedding, incident * links). Cold cache (never searched): no-op. Mid-build (promise set, live not * published yet): full-invalidate so the in-flight build cannot publish * pre-mutation rows after this write. On patch failure: full-invalidate. */ private patchCorpusForMemory; /** Drop one memory from a live snapshot (external file delete / indexPath). */ private removeMemoryFromLiveCorpus; /** Query vectors depend only on the query text and the (process-pinned) model, * so unlike the corpus snapshot they never need invalidating on writes. */ private embedQuery; search(input: SearchInput): Promise; decoctionGlean(input: DecoctionGleanDiscoverInput): Promise; decoctionGlean(input: DecoctionGleanWriteInput): Promise; private decoctionGleanWrite; decoctionCleanup(input: DecoctionCleanupInput): Promise; private decoctionCleanupCore; /** * READ-ONLY sleep preview: which settled WORK memories WOULD consolidate. * Performs zero writes; an agent must draft a proposal and receive explicit * user approval before `sleep_apply` changes the corpus. */ sleepCandidates(input?: SleepCandidatesInput, now?: number): Promise; /** * Draft a sleep consolidation as a review artifact OUTSIDE the corpus. Writes * nothing to the memory store: the corpus is only mutated by {@link sleepApply} * on an explicitly approved proposal. Validates the sources up front (same * checks apply re-runs against the live corpus) so an impossible proposal fails * loudly at draft time. */ sleepPropose(input: SleepProposeInput): Promise<{ proposal: Proposal; text: string; }>; sleepProposals(status?: ProposalStatus): Promise<{ proposals: Proposal[]; text: string; }>; /** * Apply an approved sleep proposal — the ONLY corpus mutation in the flow. The * sequence is a contract: read the proposal fresh from disk (user edits count), * require it still be `proposed`, re-validate against the LIVE corpus (drafts * sit while the corpus moves underneath them), upsert the consolidated record * (provenance threaded into frontmatter), archive each source in place (body * untouched, verbatim), then flip the proposal to `applied`. Does NOT commit * git: the route commits the whole pass as one revertible commit. */ sleepApply(input: SleepApplyInput): Promise; private sleepApplyCore; sleepReject(filename: string): Promise<{ proposal: Proposal; text: string; }>; /** * Draft one speculative memory or link outside the corpus. The daemon never * synthesizes a dream: an agent supplies the complete candidate and this * method only validates its live references and writes a review artifact. */ dreamPropose(input: DreamProposeInput): Promise<{ proposal: Proposal; text: string; }>; /** List the whole dreams/ proposal family, including both dream-memory and * dream-link records. `dream-memory` is intentionally only the directory * selector used by the shared proposal store. */ dreamProposals(status?: ProposalStatus): Promise<{ proposals: Proposal[]; text: string; }>; /** Apply the explicitly approved proposal after re-reading and re-validating * it against the live corpus. The HTTP route owns the single git commit. */ dreamApply(input: DreamApplyInput): Promise; private dreamApplyCore; dreamReject(filename: string): Promise<{ proposal: Proposal; text: string; }>; private validateDreamMemory; private validateDreamLink; /** * Shared sleep validation, run at propose time and re-run against the live * corpus at apply time. Every source must resolve, be a WORK memory in * `projectName` (single-project constraint), and be unarchived; there must be * >=2 of them; and the consolidation target must not already exist. Every * violation names the offending path. */ private validateSleepSources; /** Rewrite one source's frontmatter to archived + consolidated_into, leaving * the body verbatim, and reindex it. Mirrors {@link scrubInboundLinks}. Returns * the source's relative path. */ private archiveSource; private findSimilarMemory; private ensureEmbedding; }