interface EmbeddedSection {
    title: string;
    body: string;
    embedding: number[];
}
/**
 * Write a document's :Section children (idempotent). Deletes any prior
 * (:KnowledgeDocument)-[:HAS_SECTION]->(:Section) so re-ingest does not
 * duplicate, then re-creates the section chain mirroring memory-ingest's
 * contract (HAS_SECTION + NEXT, embedding per section). Empty-embedding
 * sections are still written (property-searchable; reindex backfills the
 * vector). The `:Section` target label keeps this cleanup from touching other
 * HAS_SECTION uses (e.g. Notion db→row edges).
 */
export declare function writeKnowledgeDocSections(tx: {
    run: (q: string, p?: Record<string, unknown>) => Promise<unknown>;
}, opts: {
    docId: string;
    accountId: string;
    scope: string;
    source: string;
    createdByAgent: string;
    sessionId: string | null;
    sourceDocumentKey: string;
    sections: EmbeddedSection[];
}): Promise<void>;
export interface LinkedInConnectionRow {
    givenName: string;
    familyName: string;
    linkedinUrl: string;
    email?: string | null;
    company?: string | null;
    title?: string | null;
    /** ISO 8601 date (YYYY-MM-DD), parsed by the caller from "23 Apr 2026". */
    connectedOn: string;
}
/**
 * One Obsidian vault page, after parsing + wikilink resolution. The
 * obsidian-vault-import tool produces these from on-disk markdown and passes
 * them as the `rows` payload — the handler here writes them in fixed Cypher.
 */
export interface ObsidianPageRow {
    /** Vault-relative path with forward slashes. Natural key with accountId. */
    obsidianPath: string;
    /** Filename without `.md`. */
    title: string;
    /** Aliases declared in YAML frontmatter. */
    aliases: string[];
    /** Tag tokens (lowercased, no `#` prefix) — frontmatter ∪ inline body. */
    tags: string[];
    /** Body content with frontmatter stripped. */
    body: string;
    /** SHA-256 of file bytes — idempotency check key. */
    contentHash: string;
    /** ISO 8601 date when daily-note detected, else null. */
    noteDate: string | null;
    /** Last-modified epoch-ms — written as `obsidianMtime`. */
    mtimeMs: number;
    /** Wikilinks emitted by this page, with resolution already applied. */
    wikilinks: ResolvedWikilink[];
    /** Embedded attachments — copied to {accountDir}/archive/obsidian/<importId>/. */
    attachments: ResolvedAttachment[];
}
export interface ResolvedWikilink {
    /** Display text the operator typed inside [[…]] (post-alias-strip). */
    linkText: string;
    /** Alias text after `|`, or null when absent. */
    alias: string | null;
    /** Anchor portion after `#`, or null. */
    anchor: string | null;
    /** What the link resolves to. */
    target: {
        kind: "page";
        obsidianPath: string;
    } | {
        kind: "entity";
        nodeId: string;
        nodeLabel: string;
    } | {
        kind: "stub";
        name: string;
    };
}
export interface ResolvedAttachment {
    /** Original path as written in the markdown. */
    rawPath: string;
    /** Path under {accountDir}/archive/obsidian/<importId>/ after copy. */
    archivePath: string;
    /** MIME-style encoding marker (image/png, application/pdf, …). */
    encodingFormat: string;
    /** Display alt text or null. */
    alt: string | null;
}
/**
 * One Notion page in a `notion-pages` archive call. The notion-import skill
 * parses page markdown and front-matter, then submits batches of these rows.
 *
 * Parent semantics: `parentNotionId === null` means "top-level page" — the
 * handler writes (:NotionWorkspace)-[:HAS_SECTION]->(:KnowledgeDocument) at
 * insert time. Non-null parents are NOT written here; the skill submits them
 * to `notion-relations-pass-2` with `edgeType: 'HAS_SECTION'` so cross-batch
 * forward references resolve correctly after every page is MERGEd.
 */
export interface NotionPageRow {
    /** 32-hex Notion UUID. Natural key with accountId. */
    notionId: string;
    /** Human title from the basename. */
    title: string;
    /** Full markdown body, front-matter already stripped. */
    text: string;
    /** Null when this page is a workspace-root page. */
    parentNotionId: string | null;
    /** ISO 8601 datetime from front-matter `Created`, or null. */
    notionCreatedAt: string | null;
    /** ISO 8601 datetime from front-matter `Last edited`, or null. */
    notionLastEditedAt: string | null;
    /** elementId of an in-account :Person resolved by the skill, or null. */
    authorPersonNodeId: string | null;
}
/**
 * One row from a Notion database in a `notion-database-rows` archive call.
 * The per-call `label` (one of Person, Organization, Project, Task, Event,
 * KnowledgeDocument) and `databaseNotionId` live on the params, not the row.
 */
export interface NotionDatabaseRowRow {
    /** 32-hex Notion UUID. Natural key with accountId. */
    notionId: string;
    /** Mapped properties (canonical camelCase where mapped, original col names otherwise). */
    properties: Record<string, string | number | boolean | null>;
}
/**
 * One pass-2 relation. Source and target must already exist (MERGEd by a
 * prior `notion-pages` or `notion-database-rows` call); rows whose endpoints
 * are missing are counted as `skippedUnknownTarget`, not errors.
 */
export interface NotionRelationRow {
    sourceNotionId: string;
    targetNotionId: string;
    /** One of NOTION_RELATION_EDGE_TYPES. Unknown types are counted, not thrown. */
    edgeType: string;
    /** Edge properties (e.g. `mentionContext` for unmapped Notion relations). */
    properties?: Record<string, string | number | boolean | null>;
}
export type ArchiveRow = LinkedInConnectionRow | ObsidianPageRow | NotionPageRow | NotionDatabaseRowRow | NotionRelationRow;
export interface ArchiveWriteParams {
    archiveType: string;
    ownerNodeId: string;
    accountId: string;
    rows: ArchiveRow[];
    sessionId?: string;
    /** notion-pages: UUID per import run, stamped on every created node as `importId`. */
    importId?: string;
    /** notion-database-rows: 32-hex Notion ID of the parent database (the `:KnowledgeDocument {kind:'notion-database'}`). */
    databaseNotionId?: string;
    /** notion-database-rows: one of Person|Organization|Project|Task|Event|KnowledgeDocument. */
    label?: string;
}
export interface ArchiveWriteResult {
    archiveType: string;
    processedRows: number;
    /** Per-handler counter map. Keys are documented in each archiveType's reference. */
    counters: Record<string, number>;
    errors: Array<{
        rowIndex: number;
        reason: string;
    }>;
    /** Present only for archiveTypes that run a `finalize` step. */
    nextChainStatus?: "ok" | "failed" | "partial" | "skipped";
    nextChainReason?: string;
}
export interface ArchiveHandlerWriteContext {
    params: ArchiveWriteParams;
    batchRows: ArchiveRow[];
    sessionId: string | null;
}
export interface ArchiveHandlerFinalizeContext {
    params: ArchiveWriteParams;
    sessionId: string | null;
    /** True when at least one writeBatch returned errors. The handler may
     *  still run finalize; it reports `status='partial'` on success. */
    hadBatchErrors: boolean;
}
export interface ArchiveHandlerFinalizeResult {
    counters: Record<string, number>;
    /** Handlers report ok/partial/skipped. The outer entry-point sets `failed`
     *  on the public result when finalize itself throws. */
    status: "ok" | "partial" | "skipped";
    reason?: string;
}
export declare function memoryArchiveWrite(params: ArchiveWriteParams): Promise<ArchiveWriteResult>;
export declare const PERSON_AND_CONNECTED_EDGE_CYPHER = "\nMATCH (owner) WHERE elementId(owner) = $ownerNodeId\nWITH owner, $rows AS rows\nUNWIND rows AS row\nMERGE (p:Person {linkedinUrl: row.linkedinUrl})\n  ON CREATE SET\n    p.accountId        = $accountId,\n    p.source           = 'linkedin',\n    p.createdByAgent   = 'linkedin-import',\n    p.createdBySource  = 'linkedin-import',\n    p.createdBySession = $sessionId,\n    p.createdAt        = datetime(),\n    p.scope            = 'admin'\nSET\n    p.givenName    = row.givenName,\n    p.familyName   = row.familyName,\n    p.currentTitle = row.title,\n    p.embedding    = row.personEmbedding\nFOREACH (_ IN CASE WHEN row.email IS NOT NULL AND row.email <> '' THEN [1] ELSE [] END |\n  SET p.email = row.email\n)\nMERGE (owner)-[c:CONNECTED_ON_LINKEDIN]->(p)\n  ON CREATE SET\n    c.connectedOn = date(row.connectedOn),\n    c.source      = 'linkedin',\n    c.createdAt   = datetime()\nRETURN count(*) AS processed\n";
export declare const ORG_AND_WORKS_FOR_CYPHER = "\nUNWIND $rows AS row\nWITH row WHERE row.company IS NOT NULL AND row.company <> ''\nMATCH (p:Person {linkedinUrl: row.linkedinUrl})\nMERGE (o:Organization {accountId: $accountId, name: row.company})\n  ON CREATE SET\n    o.source           = 'linkedin',\n    o.createdByAgent   = 'linkedin-import',\n    o.createdBySource  = 'linkedin-import',\n    o.createdBySession = $sessionId,\n    o.createdAt        = datetime(),\n    o.scope            = 'admin',\n    o.embedding        = row.orgEmbedding\nMERGE (p)-[w:WORKS_FOR]->(o)\n  ON CREATE SET\n    w.title     = row.title,\n    w.source    = 'linkedin',\n    w.current   = true,\n    w.createdAt = datetime()\n  ON MATCH SET\n    w.title     = coalesce(row.title, w.title)\nRETURN count(*) AS processed\n";
export declare const OBSIDIAN_PAGE_CYPHER = "\nMATCH (owner) WHERE elementId(owner) = $ownerNodeId\nWITH owner, $rows AS rows\nUNWIND rows AS row\nMERGE (k:KnowledgeDocument {accountId: $accountId, obsidianPath: row.obsidianPath})\n  ON CREATE SET\n    k.knowledgeDocId   = randomUUID(),\n    k.source           = 'obsidian',\n    k.createdByAgent   = 'obsidian-import',\n    k.createdBySource  = 'obsidian-import',\n    k.createdBySession = $sessionId,\n    k.createdAt        = datetime(),\n    k.scope            = 'admin'\nSET\n    k.title         = row.title,\n    k.contentHash   = row.contentHash,\n    k.text          = row.body,\n    k.summary       = row.summary,\n    k.embedding     = row.docEmbedding,\n    k.aliases       = row.aliases,\n    k.tags          = row.tags,\n    k.obsidianMtime = datetime({ epochMillis: toInteger(row.mtimeMs) }),\n    k.updatedAt     = datetime()\nFOREACH (_ IN CASE WHEN row.noteDate IS NOT NULL THEN [1] ELSE [] END |\n  SET k.noteDate = date(row.noteDate)\n)\nMERGE (owner)-[h:HAS_NOTE]->(k)\n  ON CREATE SET\n    h.source    = 'obsidian',\n    h.createdAt = datetime()\nRETURN collect({ key: row.obsidianPath, docId: elementId(k) }) AS docs\n";
export declare const OBSIDIAN_TAG_CYPHER = "\nUNWIND $rows AS row\nMATCH (k:KnowledgeDocument {accountId: $accountId, obsidianPath: row.obsidianPath})\nUNWIND row.tags AS tagName\nWITH k, tagName WHERE tagName IS NOT NULL AND tagName <> ''\nMERGE (t:DefinedTerm {accountId: $accountId, category: 'obsidian-tag', name: tagName})\n  ON CREATE SET\n    t.source           = 'obsidian',\n    t.createdByAgent   = 'obsidian-import',\n    t.createdBySource  = 'obsidian-import',\n    t.createdBySession = $sessionId,\n    t.createdAt        = datetime(),\n    t.scope            = 'admin',\n    t.embedding        = $tagEmbeddings[tagName]\nMERGE (k)-[r:TAGGED_WITH]->(t)\n  ON CREATE SET\n    r.source    = 'obsidian',\n    r.createdAt = datetime()\nRETURN count(*) AS processed\n";
export declare const OBSIDIAN_WIKILINK_PAGE_CYPHER = "\nUNWIND $pageLinks AS wl\nMATCH (src:KnowledgeDocument {accountId: $accountId, obsidianPath: wl.fromPath})\nMATCH (dst:KnowledgeDocument {accountId: $accountId, obsidianPath: wl.toPath})\nMERGE (src)-[r:WIKILINKS_TO]->(dst)\n  ON CREATE SET\n    r.linkText  = wl.linkText,\n    r.alias     = wl.alias,\n    r.anchor    = wl.anchor,\n    r.source    = 'obsidian',\n    r.createdAt = datetime()\nRETURN count(*) AS processed\n";
export declare const OBSIDIAN_WIKILINK_ENTITY_CYPHER = "\nUNWIND $entityLinks AS wl\nMATCH (src:KnowledgeDocument {accountId: $accountId, obsidianPath: wl.fromPath})\nMATCH (dst) WHERE elementId(dst) = wl.toNodeId AND dst.accountId = $accountId\nMERGE (src)-[r:MENTIONS]->(dst)\n  ON CREATE SET\n    r.linkText  = wl.linkText,\n    r.alias     = wl.alias,\n    r.source    = 'obsidian',\n    r.createdAt = datetime()\nRETURN count(*) AS processed\n";
export declare const OBSIDIAN_WIKILINK_STUB_CYPHER = "\nUNWIND $stubLinks AS wl\nMATCH (src:KnowledgeDocument {accountId: $accountId, obsidianPath: wl.fromPath})\nMERGE (c:Concept {accountId: $accountId, name: wl.stubName, source: 'obsidian-stub'})\n  ON CREATE SET\n    c.createdByAgent   = 'obsidian-import',\n    c.createdBySource  = 'obsidian-import',\n    c.createdBySession = $sessionId,\n    c.createdAt        = datetime(),\n    c.scope            = 'admin'\nMERGE (src)-[r:MENTIONS]->(c)\n  ON CREATE SET\n    r.linkText  = wl.linkText,\n    r.alias     = wl.alias,\n    r.source    = 'obsidian',\n    r.createdAt = datetime()\nRETURN count(*) AS processed\n";
export declare const OBSIDIAN_ATTACHMENT_CYPHER = "\nUNWIND $rows AS row\nWITH row WHERE size(row.attachments) > 0\nMATCH (k:KnowledgeDocument {accountId: $accountId, obsidianPath: row.obsidianPath})\nUNWIND row.attachments AS att\nMERGE (d:DigitalDocument {accountId: $accountId, attachmentPath: att.archivePath})\n  ON CREATE SET\n    d.digitalDocumentId = randomUUID(),\n    d.source            = 'obsidian',\n    d.encodingFormat    = att.encodingFormat,\n    d.alt               = att.alt,\n    d.embedding         = $attachmentEmbeddings[att.archivePath],\n    d.createdByAgent    = 'obsidian-import',\n    d.createdBySource   = 'obsidian-import',\n    d.createdBySession  = $sessionId,\n    d.createdAt         = datetime(),\n    d.scope             = 'admin'\nMERGE (d)-[e:EMBEDDED_IN]->(k)\n  ON CREATE SET\n    e.source    = 'obsidian',\n    e.createdAt = datetime()\nRETURN count(*) AS processed\n";
export declare const NOTION_PAGE_CYPHER = "\nMATCH (workspace:NotionWorkspace) WHERE elementId(workspace) = $ownerNodeId\nWITH workspace, $rows AS rows\nUNWIND rows AS row\nMERGE (k:KnowledgeDocument {accountId: $accountId, notionId: row.notionId})\n  ON CREATE SET\n    k.knowledgeDocId   = randomUUID(),\n    k.kind             = 'notion-page',\n    k.source           = 'notion',\n    k.createdByAgent   = 'notion-import',\n    k.createdBySource  = 'notion-import',\n    k.createdBySession = $sessionId,\n    k.createdAt        = datetime(),\n    k.scope            = 'admin',\n    k.importId         = $importId\nSET\n    k.title     = row.title,\n    k.text      = row.text,\n    k.summary   = row.summary,\n    k.embedding = row.docEmbedding,\n    k.updatedAt = datetime()\nFOREACH (_ IN CASE WHEN row.notionCreatedAt IS NOT NULL THEN [1] ELSE [] END |\n  SET k.notionCreatedAt = datetime(row.notionCreatedAt)\n)\nFOREACH (_ IN CASE WHEN row.notionLastEditedAt IS NOT NULL THEN [1] ELSE [] END |\n  SET k.notionLastEditedAt = datetime(row.notionLastEditedAt)\n)\nFOREACH (_ IN CASE WHEN row.parentNotionId IS NULL THEN [1] ELSE [] END |\n  MERGE (workspace)-[r:HAS_SECTION]->(k)\n    ON CREATE SET r.source = 'notion', r.createdAt = datetime()\n)\nRETURN collect({ key: row.notionId, docId: elementId(k) }) AS docs\n";
export declare const NOTION_PAGE_AUTHOR_CYPHER = "\nUNWIND $rows AS row\nWITH row WHERE row.authorPersonNodeId IS NOT NULL\nMATCH (page:KnowledgeDocument {accountId: $accountId, notionId: row.notionId})\nMATCH (author) WHERE elementId(author) = row.authorPersonNodeId AND author.accountId = $accountId\nMERGE (author)-[r:AUTHORED]->(page)\n  ON CREATE SET r.source = 'notion', r.createdAt = datetime()\nRETURN count(*) AS processed\n";
export {};
//# sourceMappingURL=memory-archive-write.d.ts.map