// Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
/**
 * LangChain Adapter — primitive mappings between LangChain tool calls and APS delegations.
 *
 * Pure mapping + receipt-builder layer. Stateful runtime (pending-intent tracking,
 * audit trail, gateway reporting) lives in the gateway — callers that need those
 * behaviours supply them via the `onReceipt` / `onDenied` hooks.
 */
import { scopeAuthorizes, verifyDelegation } from '../core/delegation.js';
import { verifyPassport } from '../verification/verify.js';
import { sign } from '../crypto/keys.js';
import { canonicalize } from '../core/canonical.js';
function buildLCReceipt(agentId, delegationId, privateKey, toolName, scope, status, summary) {
    const data = {
        receiptId: `rcpt_lc_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
        version: '1.1',
        timestamp: new Date().toISOString(),
        agentId, delegationId,
        action: { type: 'langchain_tool', target: toolName, scopeUsed: scope },
        result: { status, summary },
        delegationChain: [],
    };
    const sig = sign(canonicalize(data), privateKey);
    return { ...data, signature: sig };
}
/** Map LangChain tool name to APS scope */
export function langchainToolToScope(toolName, scopeMapping) {
    if (scopeMapping && scopeMapping[toolName])
        return scopeMapping[toolName];
    return `tools:${toolName}`;
}
/** Govern a single LangChain tool call */
export async function governLangChainTool(call, execute, config) {
    const scope = langchainToolToScope(call.name, config.scopeMapping);
    const { passport, delegation, privateKey } = config;
    // Passport check
    const pc = verifyPassport(passport);
    if (!pc.valid) {
        const reason = `Passport invalid: ${pc.errors.join(', ')}`;
        if (config.onDenied)
            config.onDenied({ tool: call.name, reason });
        const receipt = buildLCReceipt(passport.passport.agentId, delegation.delegationId, privateKey, call.name, scope, 'failure', reason);
        if (config.onReceipt)
            config.onReceipt(receipt);
        return { denied: true, reason, receipt };
    }
    // Delegation check
    const dc = verifyDelegation(delegation);
    if (!dc.valid) {
        const reason = `Delegation invalid: ${dc.errors.join(', ')}`;
        if (config.onDenied)
            config.onDenied({ tool: call.name, reason });
        const receipt = buildLCReceipt(passport.passport.agentId, delegation.delegationId, privateKey, call.name, scope, 'failure', reason);
        if (config.onReceipt)
            config.onReceipt(receipt);
        return { denied: true, reason, receipt };
    }
    // Scope check
    if (!scopeAuthorizes(delegation.scope, scope)) {
        const reason = `Scope "${scope}" not covered by delegation [${delegation.scope.join(', ')}]`;
        if (config.onDenied)
            config.onDenied({ tool: call.name, reason });
        const receipt = buildLCReceipt(passport.passport.agentId, delegation.delegationId, privateKey, call.name, scope, 'failure', reason);
        if (config.onReceipt)
            config.onReceipt(receipt);
        return { denied: true, reason, receipt };
    }
    // Execute
    const output = await execute(call.args);
    const receipt = buildLCReceipt(passport.passport.agentId, delegation.delegationId, privateKey, call.name, scope, 'success', 'Tool executed successfully');
    if (config.onReceipt)
        config.onReceipt(receipt);
    return { output, receipt };
}
/** Create a governance middleware for LangGraph */
export function createLangGraphGovernance(config) {
    return (call, execute) => governLangChainTool(call, execute, config);
}
//# sourceMappingURL=langchain.js.map