import { createStructuredModel } from "../../shared/agent/model.config.js"; import { type NegotiationTurn, type UserNegotiationContext, type SeedAssessment } from "../domain/negotiation.state.js"; import type { NegotiationSeat, NegotiationProtocolVersion } from "../../shared/schemas/negotiation-state.schema.js"; import type { NegotiationPrivateConsultation, NegotiationUserAnswer } from "../../shared/interfaces/database.interface.js"; import { type NegotiatorMemoryEntry } from "../domain/negotiation.memory.js"; import { type AttributedPriorDialogue } from "../negotiation.attribution.js"; export interface NegotiationAgentInput { ownUser: UserNegotiationContext; otherUser: UserNegotiationContext; indexContext: { networkId: string; prompt?: string; }; seedAssessment: SeedAssessment; history: NegotiationTurn[]; isFinalTurn?: boolean; /** Whether ownUser is the party that initiated the discovery (searched/signalled). */ isDiscoverer?: boolean; /** The explicit search query that triggered discovery (if any). Takes priority over background intents. */ discoveryQuery?: string; /** Whether this negotiation is continuing a prior conversation with the same counterparty. */ isContinuation?: boolean; /** User answers collected by the questioner between negotiation sessions. */ userAnswers?: NegotiationUserAnswer[]; /** Exact recipient's private consultation; never part of shared turn history. */ privateConsultation?: NegotiationPrivateConsultation; /** * The acting user's seat under the v2 client-advocate protocol. Selects the * seat-scoped turn schema and prompt stance when `protocolVersion` is `v2`. * Ignored under v1. Defaults from `isDiscoverer` when omitted. */ seat?: NegotiationSeat; /** * Negotiation protocol version for this task (inherited, never re-stamped). * `v1` (default) keeps the legacy symmetric vocabulary and prompt. */ protocolVersion?: NegotiationProtocolVersion; /** * Whether the `ask_user` client-consult pause (P3.2) is available on this * turn. The caller (negotiation graph) grants it only when the feature flag * is on, the pause loop is fully wired (questioner + answer-window timer + * opportunity to resume against), the turn is v2 non-final and non-opening, * and this side has not already consumed its one client question for the * negotiation. When true, the seat schema and prompt gain the action. */ canAskUser?: boolean; /** * Deadlock→bargaining drafting stance (IND-428, flag-gated by the caller). * Present = the graph detected a stalemate (N consecutive counter/question * turns) and this turn should be drafted in the bargaining stance — * concessions/scope reductions instead of re-arguing merits. v2 only; * ignored under v1. Absent → the prompt is byte-identical to before. */ bargaining?: { consecutiveNonConvergent: number; }; /** * Retrieved negotiator memories for the acting user (P5.3 read path). * Rendered as a private prompt section — hard disclosure constraints plus * advisory hints. Absent/empty → the prompt is byte-identical to before. */ memory?: NegotiatorMemoryEntry[]; /** * Prior dialogue with this counterparty grouped and labeled per opportunity * (IND-569). When present on a continuation it replaces the flat prior-turn * dump: earlier concluded opportunities and legacy unattributed turns render * as clearly separated, labeled blocks so the agent never reads another * opportunity's turns as part of the current exchange. Absent → the prompt * falls back to the flat continuation history (byte-identical to before). */ priorDialogue?: AttributedPriorDialogue; /** * Durable caller-owned execution identity. Timeout workers reuse this exact * value across delivery retries; it is forwarded as model-run metadata for * deterministic provider tracing/idempotency without entering the prompt. */ executionId?: string; } export interface IndexNegotiatorConfig { /** * Hard ceiling on a single LLM turn round-trip, in ms. When the underlying * model.invoke call exceeds this, an AbortSignal cancels the request and the * promise rejects — the calling turn node catches the rejection and treats it * as a failed turn, so one slow upstream call cannot consume the whole * negotiate-phase budget. * * Defaults to `NEGOTIATOR_TURN_TIMEOUT_MS` env var when set, otherwise * `DEFAULT_TURN_TIMEOUT_MS`. Sized to clip the p99 tail on Gemini-2.5-Flash * (~20 s today on OpenRouter) without trimming p90 (~12 s). */ turnTimeoutMs?: number; } /** * Unified system negotiation agent that advocates for its user. * Adapts behavior based on turn position (first turn = propose, subsequent = respond). * @remarks Uses structured output constrained to NegotiationTurnSchema (without question action). */ export declare class IndexNegotiator { private readonly turnTimeoutMs; constructor(config?: IndexNegotiatorConfig); /** * Generate a negotiation turn. * @param input - User contexts, seed assessment, history, and final turn flag * @returns A structured NegotiationTurn * @throws If the per-turn timeout fires before the LLM responds. */ invoke(input: NegotiationAgentInput): Promise; /** * Raw structured-model round trip. Split out as a seam so tests can drive * the validate→retry→fallback loop without a live provider. */ protected callModel(model: ReturnType, chatMessages: Array<{ role: string; content: string; }>, executionId?: string): Promise; }