"use strict";
// direct-ask provenance auto-injection.
//
// MCP tool wrappers that write to ACTION_PROVENANCE_LABELS call this helper
// before `writeNodeWithEdges`. When the admin server has stamped
// `SESSION_NODE_ID` in the MCP spawn env (the `sessionId` UUID of
// the active `:AdminConversation` — name is historical, value is the UUID
// property, not the Neo4j elementId, so it can be threaded through spawn
// without an extra Neo4j round-trip at session-start), the helper:
//   - verifies a `:Conversation` exists with that sessionId AND the
//     write's accountId (account isolation is enforced inside the MATCH,
//     not as a separate gate)
//   - verifies the node carries one of PROVENANCE_SOURCE_LABELS
//   - prepends a synthetic inbound `:PRODUCED` edge to the relationships array
//     so the gate inside `writeNodeWithEdges` accepts the write
//
// The injection is a no-op when:
//   - the env var is unset (typical for autonomous workflow flows that pass an
//     explicit `producedByTaskId` instead)
//   - the labels being written do not require action provenance
//   - the relationships array already carries an inbound `:PRODUCED` edge from
//     any source (idempotency — explicit `producedByTaskId` already prepended
//     one; a second source is redundant noise. If the caller's PRODUCED edge
//     resolves to a node NOT in PROVENANCE_SOURCE_LABELS, the downstream gate
//     rejects with `missing-provenance` — defence-in-depth covers that case)
//
// Loud-failure cases emit `[<logNamespace>] [provenance-missing] reason=<r>`
// to stderr and return the original relationships array unchanged — the
// downstream gate then fires `missing-provenance` so both observability
// signals land in server.log.
Object.defineProperty(exports, "__esModule", { value: true });
exports.injectConversationProvenance = injectConversationProvenance;
const index_js_1 = require("./index.js");
async function injectConversationProvenance(params) {
    const { session, relationships, accountId, writeLabels, conversationNodeId, logNamespace, tool, } = params;
    const original = [...relationships];
    if (!writeLabels.some((l) => index_js_1.ACTION_PROVENANCE_LABELS.has(l)))
        return original;
    if (!conversationNodeId)
        return original;
    if (hasInboundProducedEdge(original))
        return original;
    let lookup;
    try {
        // MATCH on (sessionId, accountId) — the account filter is part of
        // the natural key, so a cross-account env-stamp produces zero rows
        // (a clean `node-not-found`, not a silent leak). `elementId(n)` is
        // still returned because `writeNodeWithEdges` composes the synthetic
        // edge by elementId, not by sessionId — Neo4j's edge composition
        // is internal-id based.
        lookup = await session.run(`MATCH (c:Conversation {sessionId: $cid, accountId: $accountId})
       RETURN elementId(c) AS elementId, labels(c) AS labels LIMIT 1`, { cid: conversationNodeId, accountId });
    }
    catch (err) {
        process.stderr.write(`[${logNamespace}] [provenance-missing] tool=${tool} reason=driver-error message=${err instanceof Error ? err.message : String(err)} conversationNodeId=${conversationNodeId}\n`);
        return original;
    }
    if (lookup.records.length === 0) {
        process.stderr.write(`[${logNamespace}] [provenance-missing] tool=${tool} reason=node-not-found conversationNodeId=${conversationNodeId}\n`);
        return original;
    }
    const elementId = lookup.records[0].get("elementId");
    const labels = lookup.records[0].get("labels");
    const sourceLabel = labels.find((l) => index_js_1.PROVENANCE_SOURCE_LABELS.has(l));
    if (!sourceLabel) {
        process.stderr.write(`[${logNamespace}] [provenance-missing] tool=${tool} reason=wrong-source-label labels=${labels.join(",")} conversationNodeId=${conversationNodeId}\n`);
        return original;
    }
    process.stderr.write(`[${logNamespace}] [provenance-inject] tool=${tool} from=${sourceLabel}:${conversationNodeId}\n`);
    return [
        {
            type: "PRODUCED",
            direction: "incoming",
            targetNodeId: elementId,
        },
        ...original,
    ];
}
/**
 * Detect any pre-existing inbound `:PRODUCED` edge in the relationships
 * array. Source-label validation is the downstream gate's job; here we only
 * need to know whether the caller already provided one — if so, skip
 * injection to avoid duplicate edges.
 */
function hasInboundProducedEdge(relationships) {
    return relationships.some((r) => r.type === "PRODUCED" && r.direction === "incoming");
}
//# sourceMappingURL=conversation-provenance.js.map