import { z } from "zod"; import { createStructuredModel } from "../../shared/agent/model.config.js"; export { NEGOTIATOR_MEMORY_KINDS } from "../domain/negotiation.memory.js"; export type { DistilledMemoryKind } from "../domain/negotiation.memory.js"; /** Hard ceiling on entries distilled per reflection pass (per side). */ export declare const MAX_DISTILLED_MEMORIES = 3; /** * One distilled memory entry as produced by the reflection LLM. The caller * owns persistence: it resolves `aboutCounterparty` to a `subjectUserId`, * computes the embedding, and attaches provenance `sourceRefs`. */ export declare const DistilledMemorySchema: z.ZodObject<{ kind: z.ZodEnum<["playbook", "disclosure_rule", "counterparty_dossier", "threshold"]>; /** Self-contained operational statement, useful without the transcript. */ content: z.ZodString; /** Evidence strength, 0..1. Explicit client statements score high. */ confidence: z.ZodNumber; /** * True when the entry is about the counterparty (kind should be * `counterparty_dossier`); false for client-side rules and playbooks. */ aboutCounterparty: z.ZodBoolean; /** Turn indexes (0-based, into the provided transcript) evidencing this entry. */ turnIndexes: z.ZodDefault>; }, "strip", z.ZodTypeAny, { kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier"; confidence: number; content: string; aboutCounterparty: boolean; turnIndexes: number[]; }, { kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier"; confidence: number; content: string; aboutCounterparty: boolean; turnIndexes?: number[] | undefined; }>; export type DistilledMemory = z.infer; export declare const ReflectionResultSchema: z.ZodObject<{ memories: z.ZodArray; /** Self-contained operational statement, useful without the transcript. */ content: z.ZodString; /** Evidence strength, 0..1. Explicit client statements score high. */ confidence: z.ZodNumber; /** * True when the entry is about the counterparty (kind should be * `counterparty_dossier`); false for client-side rules and playbooks. */ aboutCounterparty: z.ZodBoolean; /** Turn indexes (0-based, into the provided transcript) evidencing this entry. */ turnIndexes: z.ZodDefault>; }, "strip", z.ZodTypeAny, { kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier"; confidence: number; content: string; aboutCounterparty: boolean; turnIndexes: number[]; }, { kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier"; confidence: number; content: string; aboutCounterparty: boolean; turnIndexes?: number[] | undefined; }>, "many">; }, "strip", z.ZodTypeAny, { memories: { kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier"; confidence: number; content: string; aboutCounterparty: boolean; turnIndexes: number[]; }[]; }, { memories: { kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier"; confidence: number; content: string; aboutCounterparty: boolean; turnIndexes?: number[] | undefined; }[]; }>; export type ReflectionResult = z.infer; /** A transcript row projected into the reflecting client's perspective. */ export interface ReflectionTranscriptEntry { index: number; speaker: "client" | "counterparty"; action: string; message?: string; reasoning?: string; } export interface NegotiationReflectionInput { /** The user whose negotiator is reflecting (memories land on their agent). */ clientUser: { id: string; name?: string; bio?: string; }; counterpartyUser: { id: string; name?: string; bio?: string; }; /** The client's seat in this negotiation. */ seat: "initiator" | "counterparty"; outcome: { hasOpportunity: boolean; reasoning: string; turnCount: number; }; transcript: ReflectionTranscriptEntry[]; /** Network prompt for context (optional). */ indexContext?: string; } export interface ChatReflectionInput { clientUser: { id: string; name?: string; }; /** The negotiator DM messages, oldest first. */ messages: Array<{ role: "user" | "assistant"; content: string; }>; } /** * Payload the finalize node hands to the injected {@link ReflectEnqueueFn}. * Carries user display context so the reflect worker never re-loads profiles; * turn history is loaded from the conversation by the worker (payloads stay * small in Redis). */ export interface NegotiationReflectJobData { negotiationId: string; conversationId: string; opportunityId?: string; sourceUser: { id: string; name?: string; bio?: string; }; candidateUser: { id: string; name?: string; bio?: string; }; initiatorUserId: string; outcome: { hasOpportunity: boolean; reasoning: string; turnCount: number; }; } /** * Injected enqueue callback for post-negotiation reflection (P5.2). The * protocol package has no BullMQ access — services/api wires this at its * composition roots, exactly like `QuestionerEnqueueFn`. Called fire-and- * forget from the finalize node: a reflection failure must never affect the * negotiation outcome. */ export type ReflectEnqueueFn = (job: NegotiationReflectJobData) => Promise; export interface NegotiationReflectorConfig { /** Hard ceiling on the reflection LLM round-trip, in ms (default 20000). */ timeoutMs?: number; } /** * The memory distiller (P5.2). One structured LLM call per reflection pass, * producing ≤ {@link MAX_DISTILLED_MEMORIES} private memory entries for one * client's negotiator. Throws on LLM/validation failure — callers (the reflect * queue worker) own the swallow-and-log policy, since reflection must never * affect a negotiation outcome. */ export declare class NegotiationReflector { private readonly timeoutMs; constructor(config?: NegotiationReflectorConfig); /** * Distill memories from a finished negotiation, from one side's perspective. * @throws When the LLM call times out or returns schema-invalid output. */ reflectNegotiation(input: NegotiationReflectionInput): Promise; /** * Distill stated preferences/corrections from a client ↔ negotiator chat. * @throws When the LLM call times out or returns schema-invalid output. */ reflectChat(input: ChatReflectionInput): Promise; private distill; /** * Raw structured-model round trip. Split out as a seam so tests can drive * the schema-validation path without a live provider. */ protected callModel(model: ReturnType, chatMessages: Array<{ role: string; content: string; }>): Promise; }