@indexnetwork/protocol 6.8.0-rc.395.1 → 6.10.0-rc.397.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/negotiation/negotiation.agent.d.ts +3 -1
- package/dist/negotiation/negotiation.agent.d.ts.map +1 -1
- package/dist/negotiation/negotiation.agent.js +4 -1
- package/dist/negotiation/negotiation.agent.js.map +1 -1
- package/dist/negotiation/negotiation.consultation-policy.d.ts +40 -0
- package/dist/negotiation/negotiation.consultation-policy.d.ts.map +1 -0
- package/dist/negotiation/negotiation.consultation-policy.js +69 -0
- package/dist/negotiation/negotiation.consultation-policy.js.map +1 -0
- package/dist/negotiation/negotiation.graph.d.ts +86 -28
- package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
- package/dist/negotiation/negotiation.graph.js +311 -81
- package/dist/negotiation/negotiation.graph.js.map +1 -1
- package/dist/negotiation/negotiation.question-safety.d.ts +26 -0
- package/dist/negotiation/negotiation.question-safety.d.ts.map +1 -0
- package/dist/negotiation/negotiation.question-safety.js +56 -0
- package/dist/negotiation/negotiation.question-safety.js.map +1 -0
- package/dist/negotiation/negotiation.state.d.ts +25 -2
- package/dist/negotiation/negotiation.state.d.ts.map +1 -1
- package/dist/negotiation/negotiation.state.js +35 -0
- package/dist/negotiation/negotiation.state.js.map +1 -1
- package/dist/opportunity/opportunity.graph.d.ts +12 -1
- package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
- package/dist/opportunity/opportunity.graph.js +43 -5
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.state.d.ts +5 -2
- package/dist/opportunity/opportunity.state.d.ts.map +1 -1
- package/dist/opportunity/opportunity.state.js +5 -0
- package/dist/opportunity/opportunity.state.js.map +1 -1
- package/dist/questioner/questioner.agent.d.ts +1 -1
- package/dist/questioner/questioner.agent.d.ts.map +1 -1
- package/dist/questioner/questioner.agent.js +5 -0
- package/dist/questioner/questioner.agent.js.map +1 -1
- package/dist/questioner/questioner.presets.d.ts.map +1 -1
- package/dist/questioner/questioner.presets.js +9 -13
- package/dist/questioner/questioner.presets.js.map +1 -1
- package/dist/questioner/questioner.types.d.ts +47 -10
- package/dist/questioner/questioner.types.d.ts.map +1 -1
- package/dist/questioner/questioner.types.js +61 -1
- package/dist/questioner/questioner.types.js.map +1 -1
- package/dist/shared/agent/tool.factory.d.ts.map +1 -1
- package/dist/shared/agent/tool.factory.js +1 -0
- package/dist/shared/agent/tool.factory.js.map +1 -1
- package/dist/shared/interfaces/agent-dispatcher.interface.d.ts +3 -0
- package/dist/shared/interfaces/agent-dispatcher.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/agent-dispatcher.interface.js.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +81 -6
- package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/database.interface.js.map +1 -1
- package/dist/shared/interfaces/negotiation-events.interface.d.ts +14 -2
- package/dist/shared/interfaces/negotiation-events.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/negotiation-events.interface.js.map +1 -1
- package/dist/shared/schemas/question.schema.d.ts +347 -28
- package/dist/shared/schemas/question.schema.d.ts.map +1 -1
- package/dist/shared/schemas/question.schema.js +132 -2
- package/dist/shared/schemas/question.schema.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Id } from '../shared/interfaces/database.interface.js';
|
|
2
|
-
import type { OpportunityStatus, Opportunity } from '../shared/interfaces/database.interface.js';
|
|
1
|
+
import type { Id, NegotiationContinuationExecution, NegotiationContinuationReceipt, OpportunityStatus, Opportunity } from '../shared/interfaces/database.interface.js';
|
|
3
2
|
import type { Lens } from '../shared/interfaces/embedder.interface.js';
|
|
4
3
|
import type { EvaluatorEntity } from './opportunity.evaluator.js';
|
|
5
4
|
import type { DebugMetaAgent } from '../chat/chat-streaming.types.js';
|
|
@@ -147,6 +146,8 @@ export declare function resolveInitialStatus(trigger: OpportunityTrigger, explic
|
|
|
147
146
|
* Options passed to the graph
|
|
148
147
|
*/
|
|
149
148
|
export interface OpportunityGraphOptions {
|
|
149
|
+
/** Exact durable ask_user settlement being resumed; internal queue path only. */
|
|
150
|
+
negotiationContinuation?: NegotiationContinuationExecution;
|
|
150
151
|
/** Initial status for created opportunities (default: 'pending') */
|
|
151
152
|
initialStatus?: OpportunityStatus;
|
|
152
153
|
/** Minimum score threshold (default: 50) */
|
|
@@ -346,6 +347,8 @@ export declare const OpportunityGraphState: import("@langchain/langgraph").Annot
|
|
|
346
347
|
}[]>, unknown>;
|
|
347
348
|
/** Typed persist-node counts used by queue telemetry. */
|
|
348
349
|
persistenceOutcome: import("@langchain/langgraph").BaseChannel<OpportunityPersistenceOutcome | undefined, OpportunityPersistenceOutcome | import("@langchain/langgraph").OverwriteValue<OpportunityPersistenceOutcome | undefined> | undefined, unknown>;
|
|
350
|
+
/** Positive exact-successor receipt for a fenced continuation. */
|
|
351
|
+
negotiationContinuationReceipt: import("@langchain/langgraph").BaseChannel<NegotiationContinuationReceipt | undefined, NegotiationContinuationReceipt | import("@langchain/langgraph").OverwriteValue<NegotiationContinuationReceipt | undefined> | undefined, unknown>;
|
|
349
352
|
/** Error message if any step fails */
|
|
350
353
|
error: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
351
354
|
/** Output for read mode: enriched list of opportunities. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opportunity.state.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"opportunity.state.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.state.ts"],"names":[],"mappings":"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"}
|
|
@@ -253,6 +253,11 @@ export const OpportunityGraphState = Annotation.Root({
|
|
|
253
253
|
reducer: (curr, next) => next ?? curr,
|
|
254
254
|
default: () => undefined,
|
|
255
255
|
}),
|
|
256
|
+
/** Positive exact-successor receipt for a fenced continuation. */
|
|
257
|
+
negotiationContinuationReceipt: Annotation({
|
|
258
|
+
reducer: (curr, next) => next ?? curr,
|
|
259
|
+
default: () => undefined,
|
|
260
|
+
}),
|
|
256
261
|
/** Error message if any step fails */
|
|
257
262
|
error: Annotation({
|
|
258
263
|
reducer: (curr, next) => next,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opportunity.state.js","sourceRoot":"/","sources":["opportunity/opportunity.state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotation } from \"@langchain/langgraph\";\nimport type { Id } from '../shared/interfaces/database.interface.js';\nimport type { OpportunityStatus, Opportunity } from '../shared/interfaces/database.interface.js';\nimport type { Lens } from '../shared/interfaces/embedder.interface.js';\nimport type { EvaluatorEntity } from './opportunity.evaluator.js';\nimport type { DebugMetaAgent } from '../chat/chat-streaming.types.js';\nimport type { OpportunityEvidence } from '../shared/schemas/network-assignment.schema.js';\nimport type { DiscoveryNegotiation, DiscoverySummary } from \"../shared/schemas/discovery-question.schema.js\";\n\n/**\n * Opportunity Graph State (Linear Multi-Step Workflow)\n *\n * Flow: Prep → Scope → Discovery → Evaluation → Ranking → Persist → END\n *\n * Following the intent graph pattern with Annotation-based state management.\n */\n\n/** Asker's profile shape (identity + context). Used by sourceProfile annotation. */\nexport interface SourceProfileData {\n identity?: { name?: string; bio?: string; location?: string };\n context?: string;\n}\n\n/**\n * Indexed intent with hyde document (from prep node)\n */\nexport interface IndexedIntent {\n intentId: Id<'intents'>;\n payload: string;\n summary?: string;\n hydeDocumentId?: string;\n hydeEmbedding?: number[];\n indexes: Id<'networks'>[];\n}\n\n/**\n * Target index for search (from scope node)\n */\nexport interface TargetNetwork {\n networkId: Id<'networks'>;\n title: string;\n memberCount: number;\n}\n\n/**\n * Candidate match from discovery (semantic search).\n */\nexport interface CandidateMatch {\n candidateUserId: Id<'users'>;\n candidateIntentId?: Id<'intents'>;\n /** Source premise that produced this candidate (set when discoverySource is 'premise-similarity'). */\n sourcePremiseId?: Id<'premises'>;\n /** Candidate premise that matched this candidate (set for premise-based matches). */\n candidatePremiseId?: Id<'premises'>;\n /** Source context that produced this candidate (set when discoverySource is 'context-to-intent'). */\n sourceContextId?: string;\n networkId: Id<'networks'>;\n similarity: number;\n /** Free-text lens label that produced this match. */\n lens: string;\n candidatePayload: string;\n candidateSummary?: string;\n /** How this candidate was found: 'query' (HyDE from search text), 'premise-similarity', or 'context-to-intent'. */\n discoverySource?: 'query' | 'premise-similarity' | 'context-to-intent';\n /** Which discovery strategies found this candidate (set by mergeStrategyCandidates). */\n matchedStrategies?: string[];\n /** Typed evidence that explains why this candidate entered evaluation. */\n evidence?: OpportunityEvidence[];\n}\n\n/**\n * Evaluated candidate with LLM scoring (legacy; used when evaluator returns source/candidate pair).\n * candidateIntentId is set for intent matches; omitted for profile-only matches.\n */\nexport interface EvaluatedCandidate {\n sourceUserId: Id<'users'>;\n candidateUserId: Id<'users'>;\n sourceIntentId?: Id<'intents'>;\n candidateIntentId?: Id<'intents'>;\n networkId: Id<'networks'>;\n score: number; // 0-100\n reasoning: string; // Third-party analytical explanation of the match (for LLM agents)\n valencyRole: 'Agent' | 'Patient' | 'Peer';\n /** Free-text lens label that produced this match. */\n lens: string;\n}\n\n/**\n * Actor in an evaluated opportunity (from entity-bundle evaluator).\n * networkId is filled from the entity bundle in the graph, not by the evaluator.\n */\nexport interface EvaluatedOpportunityActor {\n userId: Id<'users'>;\n role: 'agent' | 'patient' | 'peer';\n intentId?: Id<'intents'>;\n networkId: Id<'networks'>;\n}\n\n/**\n * Evaluated opportunity with multi-actor output (entity-bundle evaluator).\n */\nexport interface EvaluatedOpportunity {\n actors: EvaluatedOpportunityActor[];\n score: number;\n reasoning: string;\n evidence?: OpportunityEvidence[];\n}\n\nexport interface OpportunityPersistenceOutcome {\n evaluatedCount: number;\n createdCount: number;\n reactivatedCount: number;\n sameTriggerDuplicateSuppressions: number;\n pairActiveNegotiationSuppressions: number;\n crossTriggerAllowedCount: number;\n finalAtomicConflictCount: number;\n}\n\n/**\n * Which flow triggered this graph invocation. Determines initial persist status,\n * park-window timeout, streaming behavior, and whether AbortSignal is honored.\n *\n * - 'ambient' (default): queue-driven. Persists at the trigger default of\n * `pending` unless `options.initialStatus` overrides (the queue worker\n * passes `'latent'`, chat-bound ambient discovery passes `'draft'`). 5-min\n * park window, no streaming, ignores abort.\n * - 'orchestrator': chat-driven. Persists at the trigger default of\n * `negotiating` unless `options.initialStatus` overrides. 60s park window,\n * streams `opportunity_draft_ready` events, honors abort.\n *\n * See {@link resolveInitialStatus} for the exact fallback used when\n * `options.initialStatus` is undefined.\n */\nexport type OpportunityTrigger = 'ambient' | 'orchestrator';\n\n/**\n * Resolves the initial status for opportunities created in the persist node.\n *\n * Explicit `options.initialStatus` always wins (callers like the chat tool or\n * maintenance scripts override per-call). When the caller leaves it\n * undefined, the trigger drives the default:\n * - 'orchestrator' → 'negotiating' (chat-driven; negotiations run before the\n * user sees a draft card).\n * - 'ambient' (or any other trigger) → 'pending' (long-standing default for\n * queue- and intent-driven discovery).\n *\n * Lives here rather than in opportunity.graph.ts so unit tests can exercise\n * it without pulling in the full graph (and the evaluator's LLM requirements).\n *\n * @param trigger - The graph invocation's trigger\n * @param explicit - Caller-supplied initial status from options.initialStatus\n */\nexport function resolveInitialStatus(\n trigger: OpportunityTrigger,\n explicit: OpportunityStatus | undefined,\n): OpportunityStatus {\n if (explicit !== undefined) return explicit;\n return trigger === 'orchestrator' ? 'negotiating' : 'pending';\n}\n\n/**\n * Options passed to the graph\n */\nexport interface OpportunityGraphOptions {\n /** Initial status for created opportunities (default: 'pending') */\n initialStatus?: OpportunityStatus;\n /** Minimum score threshold (default: 50) */\n minScore?: number;\n /** Maximum opportunities to return (default: 20) */\n limit?: number;\n /** Pre-inferred lenses (if not provided, lens inference runs automatically in HyDE graph) */\n lenses?: Lens[];\n /** User's search query for HyDE generation */\n hydeDescription?: string;\n /** Existing opportunities summary for evaluator deduplication */\n existingOpportunities?: string;\n /** Chat session ID for draft opportunities; stored as context.conversationId for visibility filtering. */\n conversationId?: string;\n /**\n * MCP-only: cap the negotiate-phase wall-clock at this many milliseconds.\n * When set, `negotiateNode` races `negotiateCandidates(...)` against a timer;\n * if the timer wins, the node returns early with a `timed_out` trace and the\n * unawaited negotiation chains finalize each opp's DB status in the\n * background. Set to 20_000 by the MCP `discover_opportunities` handler.\n * Chat, ambient queue, and all other callers omit this — existing behavior.\n */\n negotiateTimeoutMs?: number;\n}\n\n/**\n * Opportunity Graph State Annotation\n */\nexport const OpportunityGraphState = Annotation.Root({\n // ─── Input Fields (Required) ───\n userId: Annotation<Id<'users'>>({\n reducer: (curr, next) => next ?? curr,\n default: () => '' as Id<'users'>,\n }),\n\n searchQuery: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n networkId: Annotation<Id<'networks'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /**\n * Optional set of indexes discovery may search within (e.g. a network-scoped\n * agent's reachable networks: the bound network plus the user's personal network).\n * The scope node intersects this with the user's actual memberships. Ignored\n * when `networkId` is set (single-network override). When unset, discovery\n * spans all of the user's networks.\n */\n indexScope: Annotation<Id<'networks'>[] | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Optional intent to use as discovery source and for triggeredBy. When set, used for search text (if query empty) and persist. */\n triggerIntentId: Annotation<Id<'intents'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Optional: restrict discovery to this specific user ID only (direct connection). */\n targetUserId: Annotation<Id<'users'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Optional: discover on behalf of this user (introducer flow). When set, prep/eval use this user's profile/intents; userId becomes the introducer. */\n onBehalfOfUserId: Annotation<Id<'users'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n options: Annotation<OpportunityGraphOptions>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({}),\n }),\n\n /**\n * Which flow triggered this graph invocation. See {@link OpportunityTrigger}\n * for the exact branch behavior and {@link resolveInitialStatus} for the\n * persist default when `options.initialStatus` is unset.\n *\n * - 'ambient' (default): queue-driven, persist default `pending`, 5-min\n * park window, no streaming, ignores abort.\n * - 'orchestrator': chat-driven, persist default `negotiating`, 60s park\n * window, streams `opportunity_draft_ready` events, honors abort.\n */\n trigger: Annotation<OpportunityTrigger>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'ambient',\n }),\n\n /**\n * Accepted opportunities the persist node discovered between the discoverer\n * and a candidate actor (same pair, status='accepted'). The orchestrator\n * branch populates this so the discover_opportunities tool (Task 7) can tell\n * the LLM \"these pairs are already connected, surface the existing chat\n * rather than creating a new draft\". Always empty for the ambient trigger.\n *\n * Left intentionally minimal — conversationId/URL resolution happens at\n * Start Chat time (Task 8), not here.\n */\n dedupAlreadyAccepted: Annotation<Array<{\n opportunityId: string;\n counterpartyUserId: string;\n }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /**\n * Operation mode controls graph flow:\n * - 'create': Existing discover pipeline (Prep → Scope → Discovery → Evaluation → Ranking → Persist)\n * - 'create_introduction': Introduction path (validation → evaluation → persist) for chat-driven intros\n * - 'continue_discovery': Pagination path (Prep → Evaluation → Ranking → Persist) using pre-loaded candidates\n * - 'read': List opportunities filtered by userId and optionally networkId (fast path)\n * - 'update': Change opportunity status (accept, reject, etc.)\n * - 'delete': Expire/archive an opportunity\n * - 'send': Promote latent opportunity to pending + queue notification\n * - 'negotiate_existing': Load an existing opportunity by opportunityId and run bilateral negotiation.\n * Used after introducer approval to trigger the normal negotiation flow.\n * - 'approve_introduction': Mark the caller as having approved a latent introducer opportunity,\n * then enqueue a negotiate_existing job for that opportunity.\n *\n * Defaults to 'create' for backward compatibility.\n */\n operationMode: Annotation<'create' | 'create_introduction' | 'continue_discovery' | 'read' | 'update' | 'delete' | 'send' | 'negotiate_existing' | 'approve_introduction'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'create' as const,\n }),\n\n /** Introduction mode: pre-gathered entities (profiles + intents per party). */\n introductionEntities: Annotation<EvaluatorEntity[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Introduction mode: optional hint from the introducer. */\n introductionHint: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** When set (e.g. chat scope), networkId must match this. */\n requiredNetworkId: Annotation<Id<'networks'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Set by intro_evaluation; used by persist to build manual detection and introducer actor. */\n introductionContext: Annotation<{ createdByName?: string } | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Target opportunity ID for update/delete/send modes. */\n opportunityId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** New status for update mode (e.g. 'accepted', 'rejected'). */\n newStatus: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n // ─── Intermediate Fields (Accumulated) ───\n\n /** User's indexed intents with hyde documents (from prep) */\n indexedIntents: Annotation<IndexedIntent[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** User's network memberships (from prep) */\n userNetworks: Annotation<Id<'networks'>[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Target indexes to search within (from scope) */\n targetNetworks: Annotation<TargetNetwork[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Per-index relevancy scores for dedup tie-breaking. Background path: from intent_indexes. Chat path: transient from IntentIndexer. */\n indexRelevancyScores: Annotation<Record<string, number>>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({}),\n }),\n\n /** Whether discovery used intent (path A) or user context (path B/C). Used by persist for triggeredBy. In-memory routing state only; never persisted. */\n discoverySource: Annotation<'intent' | 'context'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'intent',\n }),\n\n /** Resolved intent ID used for this discovery run (when discoverySource is 'intent'). Set by intent-resolution. */\n resolvedTriggerIntentId: Annotation<Id<'intents'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Asker's profile (from prep). Used for profile-as-source discovery and evaluation. */\n sourceProfile: Annotation<SourceProfileData | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /** User's active premises with embeddings (from prep). Used for premise-to-premise discovery path D. */\n sourcePremises: Annotation<Array<{ premiseId: Id<'premises'>; embedding: number[] }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** User context embeddings per network (from prep). Used for context-to-intent discovery. */\n sourceContexts: Annotation<Array<{ contextId: string; networkId: Id<'networks'>; text: string; embedding: number[] }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Resolved intent is in at least one target index (path A vs C). */\n resolvedIntentInIndex: Annotation<boolean>({\n reducer: (curr, next) => next ?? curr,\n default: () => false,\n }),\n\n /** Create-intent signal: when true, tool should return createIntentSuggested so agent can auto-call create_intent. */\n createIntentSuggested: Annotation<boolean>({\n reducer: (curr, next) => next ?? curr,\n default: () => false,\n }),\n\n /** Suggested description for create_intent when createIntentSuggested is true. */\n suggestedIntentDescription: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** HyDE embeddings per lens label (from discovery) */\n hydeEmbeddings: Annotation<Record<string, number[]>>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({}),\n }),\n\n /** Candidate matches from semantic search (from discovery) */\n candidates: Annotation<CandidateMatch[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Candidates not yet evaluated (for pagination -- cached in Redis by caller). */\n remainingCandidates: Annotation<CandidateMatch[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Discovery session ID for pagination (maps to Redis cache key). */\n discoveryId: Annotation<string | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /** Evaluated candidates with scores (from evaluation; legacy) */\n evaluatedCandidates: Annotation<EvaluatedCandidate[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Evaluated opportunities with actors (from entity-bundle evaluator) */\n evaluatedOpportunities: Annotation<EvaluatedOpportunity[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n // ─── Output Fields (Overwrite per turn) ───\n\n /** Final ranked and persisted opportunities */\n opportunities: Annotation<Opportunity[]>({\n reducer: (curr, next) => next,\n default: () => [],\n }),\n\n /** Discovery path: pairs skipped because an opportunity already exists between viewer and candidate (no duplicate created). */\n existingBetweenActors: Annotation<Array<{\n candidateUserId: Id<'users'>;\n networkId: Id<'networks'>;\n existingOpportunityId?: Id<'opportunities'>;\n existingStatus?: OpportunityStatus;\n reason?: 'same_trigger_recent_duplicate' | 'pair_active_negotiation' | 'final_atomic_conflict';\n existingTriggerIntentId?: string;\n }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Typed persist-node counts used by queue telemetry. */\n persistenceOutcome: Annotation<OpportunityPersistenceOutcome | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Error message if any step fails */\n error: Annotation<string | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n /** Output for read mode: enriched list of opportunities. */\n readResult: Annotation<{\n count: number;\n message?: string;\n opportunities: Array<{\n id: string;\n indexName: string;\n connectedWith: string[];\n suggestedBy: string | null;\n reasoning: string;\n status: string;\n category: string;\n confidence: number | null;\n source: string | null;\n }>;\n } | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n /** Output for update/delete/send modes. */\n mutationResult: Annotation<{\n success: boolean;\n message?: string;\n opportunityId?: string;\n notified?: string[];\n conversationId?: string;\n error?: string;\n } | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n // ─── Trace Output ───\n\n /**\n * Accumulated trace entries from each graph node.\n * Used for observability: surfaces internal processing steps (search query, HyDE strategies,\n * candidates found, evaluation results) to the frontend.\n */\n trace: Annotation<Array<{ node: string; detail?: string; data?: Record<string, unknown> }>>({\n reducer: (curr, next) => [...curr, ...(next || [])],\n default: () => [],\n }),\n\n /** Timing records for each agent invocation within this graph run. */\n agentTimings: Annotation<DebugMetaAgent[]>({\n reducer: (acc, val) => [...acc, ...val],\n default: () => [],\n }),\n\n /**\n * Per-candidate negotiation records captured by `negotiateNode`. Populated\n * regardless of accept/reject so the question generator sees a complete\n * picture. Populated for ALL triggers (ambient + orchestrator) since the\n * negotiate node's `onCandidateResolved` hook is unconditional; only the\n * orchestrator streaming side-effects (opportunity_draft_ready emission)\n * are trigger-gated. Empty when the negotiate node was skipped (no\n * opportunities to negotiate).\n */\n discoveryNegotiations: Annotation<DiscoveryNegotiation[]>({\n reducer: (curr, next) => [...curr, ...(next || [])],\n default: () => [],\n }),\n\n /** Aggregate counters across `discoveryNegotiations`. Built in the negotiate node. */\n discoverySummary: Annotation<DiscoverySummary | null>({\n reducer: (_curr, next) => next ?? null,\n default: () => null,\n }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"opportunity.state.js","sourceRoot":"/","sources":["opportunity/opportunity.state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotation } from \"@langchain/langgraph\";\nimport type { Id, NegotiationContinuationExecution, NegotiationContinuationReceipt, OpportunityStatus, Opportunity } from '../shared/interfaces/database.interface.js';\nimport type { Lens } from '../shared/interfaces/embedder.interface.js';\nimport type { EvaluatorEntity } from './opportunity.evaluator.js';\nimport type { DebugMetaAgent } from '../chat/chat-streaming.types.js';\nimport type { OpportunityEvidence } from '../shared/schemas/network-assignment.schema.js';\nimport type { DiscoveryNegotiation, DiscoverySummary } from \"../shared/schemas/discovery-question.schema.js\";\n\n/**\n * Opportunity Graph State (Linear Multi-Step Workflow)\n *\n * Flow: Prep → Scope → Discovery → Evaluation → Ranking → Persist → END\n *\n * Following the intent graph pattern with Annotation-based state management.\n */\n\n/** Asker's profile shape (identity + context). Used by sourceProfile annotation. */\nexport interface SourceProfileData {\n identity?: { name?: string; bio?: string; location?: string };\n context?: string;\n}\n\n/**\n * Indexed intent with hyde document (from prep node)\n */\nexport interface IndexedIntent {\n intentId: Id<'intents'>;\n payload: string;\n summary?: string;\n hydeDocumentId?: string;\n hydeEmbedding?: number[];\n indexes: Id<'networks'>[];\n}\n\n/**\n * Target index for search (from scope node)\n */\nexport interface TargetNetwork {\n networkId: Id<'networks'>;\n title: string;\n memberCount: number;\n}\n\n/**\n * Candidate match from discovery (semantic search).\n */\nexport interface CandidateMatch {\n candidateUserId: Id<'users'>;\n candidateIntentId?: Id<'intents'>;\n /** Source premise that produced this candidate (set when discoverySource is 'premise-similarity'). */\n sourcePremiseId?: Id<'premises'>;\n /** Candidate premise that matched this candidate (set for premise-based matches). */\n candidatePremiseId?: Id<'premises'>;\n /** Source context that produced this candidate (set when discoverySource is 'context-to-intent'). */\n sourceContextId?: string;\n networkId: Id<'networks'>;\n similarity: number;\n /** Free-text lens label that produced this match. */\n lens: string;\n candidatePayload: string;\n candidateSummary?: string;\n /** How this candidate was found: 'query' (HyDE from search text), 'premise-similarity', or 'context-to-intent'. */\n discoverySource?: 'query' | 'premise-similarity' | 'context-to-intent';\n /** Which discovery strategies found this candidate (set by mergeStrategyCandidates). */\n matchedStrategies?: string[];\n /** Typed evidence that explains why this candidate entered evaluation. */\n evidence?: OpportunityEvidence[];\n}\n\n/**\n * Evaluated candidate with LLM scoring (legacy; used when evaluator returns source/candidate pair).\n * candidateIntentId is set for intent matches; omitted for profile-only matches.\n */\nexport interface EvaluatedCandidate {\n sourceUserId: Id<'users'>;\n candidateUserId: Id<'users'>;\n sourceIntentId?: Id<'intents'>;\n candidateIntentId?: Id<'intents'>;\n networkId: Id<'networks'>;\n score: number; // 0-100\n reasoning: string; // Third-party analytical explanation of the match (for LLM agents)\n valencyRole: 'Agent' | 'Patient' | 'Peer';\n /** Free-text lens label that produced this match. */\n lens: string;\n}\n\n/**\n * Actor in an evaluated opportunity (from entity-bundle evaluator).\n * networkId is filled from the entity bundle in the graph, not by the evaluator.\n */\nexport interface EvaluatedOpportunityActor {\n userId: Id<'users'>;\n role: 'agent' | 'patient' | 'peer';\n intentId?: Id<'intents'>;\n networkId: Id<'networks'>;\n}\n\n/**\n * Evaluated opportunity with multi-actor output (entity-bundle evaluator).\n */\nexport interface EvaluatedOpportunity {\n actors: EvaluatedOpportunityActor[];\n score: number;\n reasoning: string;\n evidence?: OpportunityEvidence[];\n}\n\nexport interface OpportunityPersistenceOutcome {\n evaluatedCount: number;\n createdCount: number;\n reactivatedCount: number;\n sameTriggerDuplicateSuppressions: number;\n pairActiveNegotiationSuppressions: number;\n crossTriggerAllowedCount: number;\n finalAtomicConflictCount: number;\n}\n\n/**\n * Which flow triggered this graph invocation. Determines initial persist status,\n * park-window timeout, streaming behavior, and whether AbortSignal is honored.\n *\n * - 'ambient' (default): queue-driven. Persists at the trigger default of\n * `pending` unless `options.initialStatus` overrides (the queue worker\n * passes `'latent'`, chat-bound ambient discovery passes `'draft'`). 5-min\n * park window, no streaming, ignores abort.\n * - 'orchestrator': chat-driven. Persists at the trigger default of\n * `negotiating` unless `options.initialStatus` overrides. 60s park window,\n * streams `opportunity_draft_ready` events, honors abort.\n *\n * See {@link resolveInitialStatus} for the exact fallback used when\n * `options.initialStatus` is undefined.\n */\nexport type OpportunityTrigger = 'ambient' | 'orchestrator';\n\n/**\n * Resolves the initial status for opportunities created in the persist node.\n *\n * Explicit `options.initialStatus` always wins (callers like the chat tool or\n * maintenance scripts override per-call). When the caller leaves it\n * undefined, the trigger drives the default:\n * - 'orchestrator' → 'negotiating' (chat-driven; negotiations run before the\n * user sees a draft card).\n * - 'ambient' (or any other trigger) → 'pending' (long-standing default for\n * queue- and intent-driven discovery).\n *\n * Lives here rather than in opportunity.graph.ts so unit tests can exercise\n * it without pulling in the full graph (and the evaluator's LLM requirements).\n *\n * @param trigger - The graph invocation's trigger\n * @param explicit - Caller-supplied initial status from options.initialStatus\n */\nexport function resolveInitialStatus(\n trigger: OpportunityTrigger,\n explicit: OpportunityStatus | undefined,\n): OpportunityStatus {\n if (explicit !== undefined) return explicit;\n return trigger === 'orchestrator' ? 'negotiating' : 'pending';\n}\n\n/**\n * Options passed to the graph\n */\nexport interface OpportunityGraphOptions {\n /** Exact durable ask_user settlement being resumed; internal queue path only. */\n negotiationContinuation?: NegotiationContinuationExecution;\n /** Initial status for created opportunities (default: 'pending') */\n initialStatus?: OpportunityStatus;\n /** Minimum score threshold (default: 50) */\n minScore?: number;\n /** Maximum opportunities to return (default: 20) */\n limit?: number;\n /** Pre-inferred lenses (if not provided, lens inference runs automatically in HyDE graph) */\n lenses?: Lens[];\n /** User's search query for HyDE generation */\n hydeDescription?: string;\n /** Existing opportunities summary for evaluator deduplication */\n existingOpportunities?: string;\n /** Chat session ID for draft opportunities; stored as context.conversationId for visibility filtering. */\n conversationId?: string;\n /**\n * MCP-only: cap the negotiate-phase wall-clock at this many milliseconds.\n * When set, `negotiateNode` races `negotiateCandidates(...)` against a timer;\n * if the timer wins, the node returns early with a `timed_out` trace and the\n * unawaited negotiation chains finalize each opp's DB status in the\n * background. Set to 20_000 by the MCP `discover_opportunities` handler.\n * Chat, ambient queue, and all other callers omit this — existing behavior.\n */\n negotiateTimeoutMs?: number;\n}\n\n/**\n * Opportunity Graph State Annotation\n */\nexport const OpportunityGraphState = Annotation.Root({\n // ─── Input Fields (Required) ───\n userId: Annotation<Id<'users'>>({\n reducer: (curr, next) => next ?? curr,\n default: () => '' as Id<'users'>,\n }),\n\n searchQuery: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n networkId: Annotation<Id<'networks'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /**\n * Optional set of indexes discovery may search within (e.g. a network-scoped\n * agent's reachable networks: the bound network plus the user's personal network).\n * The scope node intersects this with the user's actual memberships. Ignored\n * when `networkId` is set (single-network override). When unset, discovery\n * spans all of the user's networks.\n */\n indexScope: Annotation<Id<'networks'>[] | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Optional intent to use as discovery source and for triggeredBy. When set, used for search text (if query empty) and persist. */\n triggerIntentId: Annotation<Id<'intents'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Optional: restrict discovery to this specific user ID only (direct connection). */\n targetUserId: Annotation<Id<'users'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Optional: discover on behalf of this user (introducer flow). When set, prep/eval use this user's profile/intents; userId becomes the introducer. */\n onBehalfOfUserId: Annotation<Id<'users'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n options: Annotation<OpportunityGraphOptions>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({}),\n }),\n\n /**\n * Which flow triggered this graph invocation. See {@link OpportunityTrigger}\n * for the exact branch behavior and {@link resolveInitialStatus} for the\n * persist default when `options.initialStatus` is unset.\n *\n * - 'ambient' (default): queue-driven, persist default `pending`, 5-min\n * park window, no streaming, ignores abort.\n * - 'orchestrator': chat-driven, persist default `negotiating`, 60s park\n * window, streams `opportunity_draft_ready` events, honors abort.\n */\n trigger: Annotation<OpportunityTrigger>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'ambient',\n }),\n\n /**\n * Accepted opportunities the persist node discovered between the discoverer\n * and a candidate actor (same pair, status='accepted'). The orchestrator\n * branch populates this so the discover_opportunities tool (Task 7) can tell\n * the LLM \"these pairs are already connected, surface the existing chat\n * rather than creating a new draft\". Always empty for the ambient trigger.\n *\n * Left intentionally minimal — conversationId/URL resolution happens at\n * Start Chat time (Task 8), not here.\n */\n dedupAlreadyAccepted: Annotation<Array<{\n opportunityId: string;\n counterpartyUserId: string;\n }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /**\n * Operation mode controls graph flow:\n * - 'create': Existing discover pipeline (Prep → Scope → Discovery → Evaluation → Ranking → Persist)\n * - 'create_introduction': Introduction path (validation → evaluation → persist) for chat-driven intros\n * - 'continue_discovery': Pagination path (Prep → Evaluation → Ranking → Persist) using pre-loaded candidates\n * - 'read': List opportunities filtered by userId and optionally networkId (fast path)\n * - 'update': Change opportunity status (accept, reject, etc.)\n * - 'delete': Expire/archive an opportunity\n * - 'send': Promote latent opportunity to pending + queue notification\n * - 'negotiate_existing': Load an existing opportunity by opportunityId and run bilateral negotiation.\n * Used after introducer approval to trigger the normal negotiation flow.\n * - 'approve_introduction': Mark the caller as having approved a latent introducer opportunity,\n * then enqueue a negotiate_existing job for that opportunity.\n *\n * Defaults to 'create' for backward compatibility.\n */\n operationMode: Annotation<'create' | 'create_introduction' | 'continue_discovery' | 'read' | 'update' | 'delete' | 'send' | 'negotiate_existing' | 'approve_introduction'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'create' as const,\n }),\n\n /** Introduction mode: pre-gathered entities (profiles + intents per party). */\n introductionEntities: Annotation<EvaluatorEntity[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Introduction mode: optional hint from the introducer. */\n introductionHint: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** When set (e.g. chat scope), networkId must match this. */\n requiredNetworkId: Annotation<Id<'networks'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Set by intro_evaluation; used by persist to build manual detection and introducer actor. */\n introductionContext: Annotation<{ createdByName?: string } | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Target opportunity ID for update/delete/send modes. */\n opportunityId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** New status for update mode (e.g. 'accepted', 'rejected'). */\n newStatus: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n // ─── Intermediate Fields (Accumulated) ───\n\n /** User's indexed intents with hyde documents (from prep) */\n indexedIntents: Annotation<IndexedIntent[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** User's network memberships (from prep) */\n userNetworks: Annotation<Id<'networks'>[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Target indexes to search within (from scope) */\n targetNetworks: Annotation<TargetNetwork[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Per-index relevancy scores for dedup tie-breaking. Background path: from intent_indexes. Chat path: transient from IntentIndexer. */\n indexRelevancyScores: Annotation<Record<string, number>>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({}),\n }),\n\n /** Whether discovery used intent (path A) or user context (path B/C). Used by persist for triggeredBy. In-memory routing state only; never persisted. */\n discoverySource: Annotation<'intent' | 'context'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'intent',\n }),\n\n /** Resolved intent ID used for this discovery run (when discoverySource is 'intent'). Set by intent-resolution. */\n resolvedTriggerIntentId: Annotation<Id<'intents'> | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Asker's profile (from prep). Used for profile-as-source discovery and evaluation. */\n sourceProfile: Annotation<SourceProfileData | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /** User's active premises with embeddings (from prep). Used for premise-to-premise discovery path D. */\n sourcePremises: Annotation<Array<{ premiseId: Id<'premises'>; embedding: number[] }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** User context embeddings per network (from prep). Used for context-to-intent discovery. */\n sourceContexts: Annotation<Array<{ contextId: string; networkId: Id<'networks'>; text: string; embedding: number[] }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Resolved intent is in at least one target index (path A vs C). */\n resolvedIntentInIndex: Annotation<boolean>({\n reducer: (curr, next) => next ?? curr,\n default: () => false,\n }),\n\n /** Create-intent signal: when true, tool should return createIntentSuggested so agent can auto-call create_intent. */\n createIntentSuggested: Annotation<boolean>({\n reducer: (curr, next) => next ?? curr,\n default: () => false,\n }),\n\n /** Suggested description for create_intent when createIntentSuggested is true. */\n suggestedIntentDescription: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** HyDE embeddings per lens label (from discovery) */\n hydeEmbeddings: Annotation<Record<string, number[]>>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({}),\n }),\n\n /** Candidate matches from semantic search (from discovery) */\n candidates: Annotation<CandidateMatch[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Candidates not yet evaluated (for pagination -- cached in Redis by caller). */\n remainingCandidates: Annotation<CandidateMatch[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Discovery session ID for pagination (maps to Redis cache key). */\n discoveryId: Annotation<string | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /** Evaluated candidates with scores (from evaluation; legacy) */\n evaluatedCandidates: Annotation<EvaluatedCandidate[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Evaluated opportunities with actors (from entity-bundle evaluator) */\n evaluatedOpportunities: Annotation<EvaluatedOpportunity[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n // ─── Output Fields (Overwrite per turn) ───\n\n /** Final ranked and persisted opportunities */\n opportunities: Annotation<Opportunity[]>({\n reducer: (curr, next) => next,\n default: () => [],\n }),\n\n /** Discovery path: pairs skipped because an opportunity already exists between viewer and candidate (no duplicate created). */\n existingBetweenActors: Annotation<Array<{\n candidateUserId: Id<'users'>;\n networkId: Id<'networks'>;\n existingOpportunityId?: Id<'opportunities'>;\n existingStatus?: OpportunityStatus;\n reason?: 'same_trigger_recent_duplicate' | 'pair_active_negotiation' | 'final_atomic_conflict';\n existingTriggerIntentId?: string;\n }>>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n /** Typed persist-node counts used by queue telemetry. */\n persistenceOutcome: Annotation<OpportunityPersistenceOutcome | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Positive exact-successor receipt for a fenced continuation. */\n negotiationContinuationReceipt: Annotation<NegotiationContinuationReceipt | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Error message if any step fails */\n error: Annotation<string | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n /** Output for read mode: enriched list of opportunities. */\n readResult: Annotation<{\n count: number;\n message?: string;\n opportunities: Array<{\n id: string;\n indexName: string;\n connectedWith: string[];\n suggestedBy: string | null;\n reasoning: string;\n status: string;\n category: string;\n confidence: number | null;\n source: string | null;\n }>;\n } | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n /** Output for update/delete/send modes. */\n mutationResult: Annotation<{\n success: boolean;\n message?: string;\n opportunityId?: string;\n notified?: string[];\n conversationId?: string;\n error?: string;\n } | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n // ─── Trace Output ───\n\n /**\n * Accumulated trace entries from each graph node.\n * Used for observability: surfaces internal processing steps (search query, HyDE strategies,\n * candidates found, evaluation results) to the frontend.\n */\n trace: Annotation<Array<{ node: string; detail?: string; data?: Record<string, unknown> }>>({\n reducer: (curr, next) => [...curr, ...(next || [])],\n default: () => [],\n }),\n\n /** Timing records for each agent invocation within this graph run. */\n agentTimings: Annotation<DebugMetaAgent[]>({\n reducer: (acc, val) => [...acc, ...val],\n default: () => [],\n }),\n\n /**\n * Per-candidate negotiation records captured by `negotiateNode`. Populated\n * regardless of accept/reject so the question generator sees a complete\n * picture. Populated for ALL triggers (ambient + orchestrator) since the\n * negotiate node's `onCandidateResolved` hook is unconditional; only the\n * orchestrator streaming side-effects (opportunity_draft_ready emission)\n * are trigger-gated. Empty when the negotiate node was skipped (no\n * opportunities to negotiate).\n */\n discoveryNegotiations: Annotation<DiscoveryNegotiation[]>({\n reducer: (curr, next) => [...curr, ...(next || [])],\n default: () => [],\n }),\n\n /** Aggregate counters across `discoveryNegotiations`. Built in the negotiate node. */\n discoverySummary: Annotation<DiscoverySummary | null>({\n reducer: (_curr, next) => next ?? null,\n default: () => null,\n }),\n});\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type QuestionGenerationResult } from "../shared/schemas/question.schema.js";
|
|
2
2
|
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type QuestionerInput } from "./questioner.types.js";
|
|
4
4
|
export interface QuestionerAgentConfig {
|
|
5
5
|
/** Optional model config override. */
|
|
6
6
|
modelConfig?: Parameters<typeof createStructuredModel>[3];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questioner.agent.d.ts","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkD,KAAK,wBAAwB,EAAoD,MAAM,sCAAsC,CAAC;AACvL,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAKxE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"questioner.agent.d.ts","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkD,KAAK,wBAAwB,EAAoD,MAAM,sCAAsC,CAAC;AACvL,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAKxE,OAAO,EAAkC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7F,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3D;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAA2C;gBAE5C,MAAM,CAAC,EAAE,qBAAqB;IAM1C;;;;;;;;OAQG;IAEG,MAAM,CACV,KAAK,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;CAmD5C"}
|
|
@@ -23,6 +23,7 @@ import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
|
23
23
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
24
24
|
import { Timed } from "../shared/observability/performance.js";
|
|
25
25
|
import { getPreset } from "./questioner.presets.js";
|
|
26
|
+
import { isValidQuestionerInputContract } from "./questioner.types.js";
|
|
26
27
|
const logger = protocolLogger("QuestionerAgent");
|
|
27
28
|
/** Maximum same-strategy questions allowed in a single emission. */
|
|
28
29
|
const MAX_SAME_STRATEGY = 2;
|
|
@@ -47,6 +48,10 @@ export class QuestionerAgent {
|
|
|
47
48
|
* the LLM threw, or the call was aborted.
|
|
48
49
|
*/
|
|
49
50
|
async invoke(input, options) {
|
|
51
|
+
if (!isValidQuestionerInputContract(input)) {
|
|
52
|
+
logger.warn('QuestionerAgent rejected invalid mode/purpose/context contract', { mode: input.mode });
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
50
55
|
const preset = getPreset(input.mode, input.purpose);
|
|
51
56
|
const userMessage = preset.buildPrompt(input.context);
|
|
52
57
|
let raw;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questioner.agent.js","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":";;;;;;;;;AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,+BAA+B,EAAkG,MAAM,sCAAsC,CAAC;AACvL,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"questioner.agent.js","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * QuestionerAgent — stateless, mode-driven agent that generates structured\n * decision questions from arbitrary protocol contexts.\n *\n * Follows the IndexNegotiator pattern: constructor takes optional config,\n * single public `invoke()` method receives the full context per call.\n * The LLM model is bound once at construction; the preset (system prompt +\n * builder) is selected per invocation based on `input.mode`.\n */\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { QuestionGeneratorResponseSchema, type Question, type QuestionGenerationResult, type QuestionStrategy, type QuestionWithStrategy } from \"../shared/schemas/question.schema.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { getPreset } from \"./questioner.presets.js\";\nimport { isValidQuestionerInputContract, type QuestionerInput } from \"./questioner.types.js\";\n\nconst logger = protocolLogger(\"QuestionerAgent\");\n\n/** Maximum same-strategy questions allowed in a single emission. */\nconst MAX_SAME_STRATEGY = 2;\n\nexport interface QuestionerAgentConfig {\n /** Optional model config override. */\n modelConfig?: Parameters<typeof createStructuredModel>[3];\n}\n\n/**\n * Stateless question-generation agent. Accepts a `QuestionerInput` envelope,\n * selects the preset for the given mode, invokes the LLM, and applies\n * guardrails (dedup + strategy diversity).\n */\nexport class QuestionerAgent {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor(config?: QuestionerAgentConfig) {\n this.model = createStructuredModel(\"questioner\", QuestionGeneratorResponseSchema, {\n name: \"clarifying_questions\",\n }, config?.modelConfig);\n }\n\n /**\n * Generate up to 3 decision questions from the given input.\n *\n * @param input Envelope with mode, userId, source info, and mode-specific context.\n * @param options.signal Optional AbortSignal to cancel the in-flight LLM call.\n * @returns A result with parallel questions[] and strategies[] arrays,\n * or null when generation failed, guardrails dropped all candidates,\n * the LLM threw, or the call was aborted.\n */\n @Timed()\n async invoke(\n input: QuestionerInput,\n options?: { signal?: AbortSignal },\n ): Promise<QuestionGenerationResult | null> {\n if (!isValidQuestionerInputContract(input)) {\n logger.warn('QuestionerAgent rejected invalid mode/purpose/context contract', { mode: input.mode });\n return null;\n }\n const preset = getPreset(input.mode, input.purpose);\n const userMessage = preset.buildPrompt(input.context);\n\n let raw: unknown;\n try {\n raw = await invokeWithAbortSignal(\n this.model,\n [new SystemMessage(preset.systemPrompt), new HumanMessage(userMessage)],\n options?.signal,\n );\n } catch (err) {\n const aborted = options?.signal?.aborted ?? false;\n if (aborted) {\n logger.info(\"QuestionerAgent aborted by signal\", {\n mode: input.mode,\n reason: options?.signal?.reason instanceof Error\n ? options.signal.reason.message\n : String(options?.signal?.reason ?? \"unknown\"),\n });\n } else {\n logger.warn(\"QuestionerAgent LLM call failed\", {\n mode: input.mode,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return null;\n }\n\n const parsed = QuestionGeneratorResponseSchema.safeParse(raw);\n if (!parsed.success) {\n logger.warn(\"QuestionerAgent parse failed\", {\n mode: input.mode,\n error: parsed.error.message,\n });\n return null;\n }\n\n const filtered = applyGuardrails(parsed.data.questions);\n if (filtered.length === 0) return null;\n\n return {\n questions: filtered.map(stripInternalMetadata),\n strategies: filtered.map((q) => q.strategy),\n underspecificationTypes: filtered.map((q) => q.underspecificationType),\n };\n }\n}\n\n// --- Guardrails (migrated from question.generator.ts) -------------------------\n\nfunction applyGuardrails(questions: QuestionWithStrategy[]): QuestionWithStrategy[] {\n const dedupedByTitle = dedupByTitle(questions);\n return enforceStrategyDiversity(dedupedByTitle);\n}\n\nfunction dedupByTitle(questions: QuestionWithStrategy[]): QuestionWithStrategy[] {\n const seen = new Set<string>();\n const out: QuestionWithStrategy[] = [];\n for (const q of questions) {\n if (seen.has(q.title)) continue;\n seen.add(q.title);\n out.push(q);\n }\n return out;\n}\n\nfunction enforceStrategyDiversity(\n questions: QuestionWithStrategy[],\n): QuestionWithStrategy[] {\n const counts = new Map<QuestionStrategy, number>();\n const out: QuestionWithStrategy[] = [];\n for (const q of questions) {\n const n = counts.get(q.strategy) ?? 0;\n if (n >= MAX_SAME_STRATEGY) continue;\n counts.set(q.strategy, n + 1);\n out.push(q);\n }\n return out;\n}\n\nfunction stripInternalMetadata(q: QuestionWithStrategy): Question {\n const {\n strategy: _strategy,\n underspecificationType: _underspecificationType,\n ...publicShape\n } = q;\n return publicShape;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questioner.presets.d.ts","sourceRoot":"/","sources":["questioner/questioner.presets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAsB1F,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;
|
|
1
|
+
{"version":3,"file":"questioner.presets.d.ts","sourceRoot":"/","sources":["questioner/questioner.presets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAsB1F,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AA2dD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,gBAAgB,CAYzF"}
|
|
@@ -190,7 +190,7 @@ const NEGOTIATION_SYSTEM_PROMPT = `You sit between a human and a discovery proto
|
|
|
190
190
|
|
|
191
191
|
POST-STALL purpose. A negotiation between this user and another person ended without a clear outcome — either the turn budget was exhausted, the session timed out, or conversation stalled. Surface the minimum set of structured questions that help the user provide the missing signal needed to unblock or refine the next discovery attempt.
|
|
192
192
|
|
|
193
|
-
UPTAKE purpose. The user is considering accepting a proposed connection, but one preparatory condition about the other person's practical ability, resources, availability, or authority to carry out the proposed activity needs clarification before commitment. Generate exactly ONE neutral question that lets the user decide whether they understand that condition well enough to proceed. Ask about the concrete activity and the other
|
|
193
|
+
UPTAKE purpose. The user is considering accepting a proposed connection, but one preparatory condition about the other person's practical ability, resources, availability, or authority to carry out the proposed activity needs clarification before commitment. Generate exactly ONE neutral question that lets the user decide whether they understand that condition well enough to proceed. Ask about the concrete activity and refer to the other participant generically; do not accuse, challenge credibility, presume incapability, or invent/repeat profile attributes. Never reveal a numeric authority score, threshold, felicity label, evaluator judgment, or any internal matching/verification mechanics. Do not ask whether the user wants to accept; clarify the preparatory condition only.
|
|
194
194
|
|
|
195
195
|
You may pick from three strategies. Choose contextually; mix only when each question is genuinely distinct.
|
|
196
196
|
- refine_intent: help the user sharpen their underlying signal based on what the negotiation revealed (scope, scale, priority, direction).
|
|
@@ -202,7 +202,7 @@ Ask a question only when ALL of these hold:
|
|
|
202
202
|
2. The answer would materially change how the next attempt surfaces or engages candidates.
|
|
203
203
|
3. The question targets a different decision domain from any other question in this batch.
|
|
204
204
|
|
|
205
|
-
Standalone prompt rule. Every generated \`prompt\` must be understandable outside the conversation where it was created. Naturally include the user's underlying goal or topic and the relevant community in the question text itself
|
|
205
|
+
Standalone prompt rule. Every generated \`prompt\` must be understandable outside the conversation where it was created. Naturally include the user's own underlying goal or topic and, only when supplied as a source-safe label, the relevant community in the question text itself. Never infer or repeat counterparty identity/profile, private transcript, evaluator reasoning, match reasons, event/community attendance, or internal IDs/metadata. Do not rely on \`title\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.
|
|
206
206
|
- Bad: "Which role is a better fit for your immediate needs?"
|
|
207
207
|
- Good: "For your search for AI infrastructure collaborators in the AI founders community, what kind of working relationship fits your immediate needs?"
|
|
208
208
|
|
|
@@ -234,11 +234,11 @@ function buildPostStallNegotiationPrompt(ctx) {
|
|
|
234
234
|
"",
|
|
235
235
|
"## Negotiation context",
|
|
236
236
|
`Community: ${ctx.indexContext}`,
|
|
237
|
-
`
|
|
237
|
+
`Other participant: ${ctx.counterpartyHint}`,
|
|
238
238
|
`Stall reason: ${ctx.outcomeReason}`,
|
|
239
239
|
"",
|
|
240
|
-
"##
|
|
241
|
-
ctx.
|
|
240
|
+
"## Recipient's own signal",
|
|
241
|
+
ctx.recipientIntent,
|
|
242
242
|
"",
|
|
243
243
|
"## User profile",
|
|
244
244
|
profileBlock,
|
|
@@ -251,7 +251,6 @@ function buildPostStallNegotiationPrompt(ctx) {
|
|
|
251
251
|
}
|
|
252
252
|
function buildUptakeNegotiationPrompt(ctx) {
|
|
253
253
|
const profileBlock = buildUserContextBlock(ctx.userContext);
|
|
254
|
-
const evidence = ctx.preparatoryEvidence?.trim() || "(no additional public evidence provided)";
|
|
255
254
|
return [
|
|
256
255
|
"## Purpose",
|
|
257
256
|
"UPTAKE — preparatory-condition clarification before acceptance",
|
|
@@ -265,9 +264,6 @@ function buildUptakeNegotiationPrompt(ctx) {
|
|
|
265
264
|
"## Community",
|
|
266
265
|
ctx.indexContext,
|
|
267
266
|
"",
|
|
268
|
-
"## Public preparatory evidence",
|
|
269
|
-
evidence,
|
|
270
|
-
"",
|
|
271
267
|
"## User profile",
|
|
272
268
|
profileBlock,
|
|
273
269
|
"",
|
|
@@ -293,13 +289,13 @@ You may pick from two strategies. Choose contextually; mix only when each questi
|
|
|
293
289
|
|
|
294
290
|
Honor the negotiator's intent. When a draft question is provided, treat it as the source of truth for WHAT to ask — improve wording, tighten options, add consequence-focused descriptions. Do not invent questions about topics the negotiator did not raise. When no draft is provided, derive the question strictly from the disclosure subject.
|
|
295
291
|
|
|
296
|
-
Standalone prompt rule. Every generated \`prompt\` must be understandable outside the conversation where it was created. The user may see this question hours later in an inbox, away from any negotiation view.
|
|
297
|
-
- Bad: "
|
|
298
|
-
- Good: "May I share your budget range with
|
|
292
|
+
Standalone prompt rule. Every generated \`prompt\` must be understandable outside the conversation where it was created. The user may see this question hours later in an inbox, away from any negotiation view. Include the disclosure subject and the generic phrase "the other participant in this match" in the question text itself. Never repeat or infer counterparty identity/profile, private transcript, evaluator reasoning, match reasons, event/community attendance, or internal IDs/metadata. Do not rely on \`title\`, UI labels, hidden metadata, or surrounding digest/chat text.
|
|
293
|
+
- Bad: "May I share your budget with Alex, a Berlin-based founder from the event?"
|
|
294
|
+
- Good: "May I share your budget range with the other participant in this match?"
|
|
299
295
|
|
|
300
296
|
${REFERENTIAL_CLOSURE_RULES}
|
|
301
297
|
|
|
302
|
-
Exception — describing the other side.
|
|
298
|
+
Exception — describing the other side. This question may refer to the other participant generically because it is tied to one exact server-validated opportunity. Do not add attributes, identity, or claims about why they matched.
|
|
303
299
|
|
|
304
300
|
Cardinality. Default one question. Add a second ONLY when the negotiator's need genuinely spans two distinct decisions (e.g. one disclosure gate plus one missing fact). Never pad.
|
|
305
301
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questioner.presets.js","sourceRoot":"/","sources":["questioner/questioner.presets.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Mode presets for the QuestionerAgent. Each preset provides a system prompt\n * and a buildPrompt function that assembles the user message from a typed\n * context object. Only the `discovery` preset ships in Slice 1; others throw\n * until their implementation slices land.\n */\nimport type { QuestionMode, QuestionPurpose } from \"../shared/schemas/question.schema.js\";\nimport { DISCOVERY_SYSTEM_PROMPT, buildDiscoveryQuestionPrompt } from \"./questioner.discovery.prompt.js\";\n\nimport { QUD_UNDERSPECIFICATION_RULES } from \"./questioner.qud.js\";\nimport type { ChatContext, IntentContext, NegotiationContext, NegotiationInflightContext, PostStallNegotiationContext, ProfileContext, RecoveryIntentContext, UptakeNegotiationContext } from \"./questioner.types.js\";\n\n/**\n * Shared rule block appended to every questioner system prompt. Enforces that\n * the generated `prompt` resolves on its own — no demonstratives/anaphora that\n * point at people, events, or prior turns the reader cannot see — and never\n * narrates Index's own matching pipeline. Closes the referential-leak class\n * surfaced in digest audits (\"…with these builders?\", \"the previous\n * negotiation stalled because the counterparty didn't mention …\").\n */\nconst REFERENTIAL_CLOSURE_RULES = `Referential closure. The prompt must resolve entirely on its own, with no dangling references. The reader sees ONLY the question text — never the people you reviewed, the counterparty, the events on their calendar, or this conversation. Do not use demonstratives or definite anaphora that point at things the reader cannot see: \"these builders\", \"those founders\", \"these researchers\", \"these conversations\", \"this lunch\", \"the speaker\". If you reference a person, name them. If you reference a group, restate the concrete shared attribute inside the question itself (\"founders working on decentralized identity\"), never \"these founders\". Never imply a list, set, or prior exchange the reader is not currently looking at.\n- Bad: \"What kind of collaboration are you looking for with these builders?\"\n- Good: \"You're meeting people building agent infrastructure — what kind of collaboration are you looking for?\"\n\nNo process narration. Never describe Index's own activity or internal state. Forbidden: \"the previous negotiation\", \"the negotiation stalled\", \"opportunities found so far\", \"my search\", \"the counterparty\", \"candidates reviewed\", restating why a match did or did not happen, or quoting words a counterparty did or did not use. Ask about the user's goal or intent directly, never about the matching pipeline.\n- Bad: \"The previous negotiation stalled because the counterparty didn't mention 'matchmaking'. Should I broaden the search?\"\n- Good: \"Do you want to focus on dedicated matchmakers, or also people interested in relationships more broadly?\"`;\n\nexport interface QuestionerPreset {\n /** The LLM system prompt for this mode. */\n systemPrompt: string;\n /** Builds the user-message string from the mode-specific context. */\n buildPrompt: (context: unknown) => string;\n}\n\n/**\n * Renders the user-context block shared by every preset's user message from the\n * global user_context paragraph (the profile-replacing identity text).\n * @param userContext - The user's global context paragraph, if available.\n * @returns The trimmed context paragraph, or \"(no profile data)\" when empty.\n */\nfunction buildUserContextBlock(userContext?: string): string {\n const trimmed = userContext?.trim();\n return trimmed && trimmed.length > 0 ? trimmed : \"(no profile data)\";\n}\n\n// ─── Intent preset ──────────────────────────────────────────────────────────\n\nconst INTENT_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The user has stated an intent — what they are looking for. Your job: surface the minimum set of structured questions that help the user sharpen that intent before the protocol runs discovery on their behalf.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- refine_intent: ask the user to sharpen or pivot the core signal (scope, scale, specificity, direction).\n- surface_missing_detail: ask for one concrete missing input that would change which candidates surface (stage, location, timing, budget, constraints, format, …).\n\nAsk a question only when ALL of these hold:\n1. The agent cannot infer the answer from the intent text or user profile already shown.\n2. The answer would materially change which candidates surface.\n3. The question targets a different decision domain from any other question in this batch.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. Naturally include the source intent/topic in the question text itself, using concise plain language from the intent or summary. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"What kind of collaboration are you looking for?\"\n- Good: \"For your decentralized identity protocol-design search, what kind of collaboration are you looking for?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. Default one question. Add a second only when a DIFFERENT strategy genuinely complements the first and unblocks a clearly distinct decision. Never ask two questions of the same strategy unless their decision domains differ (different titles).\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Stage\", \"Timing\", \"Location\", \"Scope\", \"Budget\", \"Format\", \"Skills\", \"Collab\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I start searching?\").\n- Don't ask about hypothetical edge cases not implied by the intent.\n- Don't re-ask for facts already visible in the user profile.\n- Don't ask vague introspective questions (\"What do you really want?\").\n\nOutput. Return at most 2 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the intent is already specific enough, return \"questions\": [].`;\n\n/**\n * Build the user message for the intent preset from an IntentContext.\n * @param ctx - The intent context.\n * @returns The assembled user message string.\n */\nfunction buildIntentPrompt(ctx: IntentContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const summaryBlock = ctx.summary ? ctx.summary : \"(no summary available)\";\n\n return [\n \"## Intent\",\n ctx.payload,\n \"\",\n \"## Summary\",\n summaryBlock,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Identify the minimum set of questions the user must answer to sharpen this intent.\",\n \"Apply every rule from your system prompt before outputting.\",\n \"Return an empty `questions` array if the intent is already specific enough.\",\n ].join(\"\\n\");\n}\n\nconst RECOVERY_INTENT_SYSTEM_PROMPT = `You help a user materially refine one of their own discovery signals after an authoritative discovery run completed without leaving a currently actionable opportunity. Ask directly about the user's goal, desired counterpart attributes, or constraints. Never describe the run or imply that any specific person was reviewed.\n\nChoose exactly one of these strategies:\n- refine_intent: sharpen or pivot the core signal in a way that changes who should surface.\n- surface_missing_detail: ask for one concrete missing constraint such as stage, location, timing, budget, format, or required capability.\n\nAsk a question only when the answer is not already inferable from the intent, summary, or user profile and would materially change discovery. If no safe, useful missing axis exists, return an empty questions array.\n\nStandalone prompt rule. The prompt must identify the user's intent topic in plain language and ask about their own goal or constraints. It must make sense without UI labels or hidden metadata.\n\n${REFERENTIAL_CLOSURE_RULES}\n\nPrivacy and process boundary. You may receive only a bounded aggregate count of validated prior outcomes. Use it solely as a weak signal to choose a missing axis. Never mention or imply counts, matches, no matches, rejections, negotiations, candidates, counterparties, searches, retries, pipeline state, evidence, or what another person did or did not say. Never invent people or facts.\n\nCardinality. Return at most ONE question. Use only refine_intent or surface_missing_detail. The question must nudge a material edit, not ask whether to search again.\n\nOption construction. Provide 2–4 materially different outcomes. Put the safest or most common path first with \" (Recommended)\" when appropriate. Descriptions state the discovery consequence. Never add an Other option.\n\nTitle rules. ≤12 chars and name the decision domain, such as Scope, Timing, Location, Stage, Budget, Format, or Skills.\n\nOutput. Return zero or one question. Set underspecificationType to the applicable QUD category, or null when none applies.`;\n\nfunction buildRecoveryIntentPrompt(ctx: RecoveryIntentContext): string {\n return [\n \"## Intent\",\n ctx.payload,\n \"\",\n \"## Summary\",\n ctx.summary?.trim() || \"(no summary available)\",\n \"\",\n \"## User profile\",\n buildUserContextBlock(ctx.userContext),\n ...(ctx.rejectedNegotiationCount\n ? [\n \"\",\n \"## Private aggregate signal\",\n `Validated prior outcomes without an actionable connection: ${ctx.rejectedNegotiationCount}`,\n ]\n : []),\n \"\",\n \"## Your task\",\n \"Ask at most one direct question whose answer would materially refine this intent.\",\n \"Do not narrate discovery outcomes or internal process state in the question.\",\n \"Return an empty `questions` array when no safe useful axis is missing.\",\n ].join(\"\\n\");\n}\n\n// ─── Profile preset ─────────────────────────────────────────────────────────\n\nconst PROFILE_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The user has a profile that is incomplete. Your job: surface the minimum set of structured questions that fill the identified gaps — asking about location, skills, interests, current work, or goals — so the protocol can run better discovery on their behalf.\n\nThe user may already have premises — atomic self-descriptions they have stated. These cover specific profile domains. Do not ask about domains already addressed by existing premises. Focus only on gaps not covered by any premise.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- surface_missing_detail: ask for one concrete missing piece of profile data (location, current role, skills, interests, goals, availability, …).\n- refine_intent: ask the user to clarify or sharpen an existing profile signal so candidates can be ranked more accurately.\n\nAsk a question only when ALL of these hold:\n1. The answer is not already visible in the profile data shown.\n2. The answer is not already covered by an existing premise listed below the profile.\n3. The answer would meaningfully change which opportunities surface for this user.\n4. The question targets a different profile domain from any other question in this batch.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. Naturally include the profile signal or gap being clarified in the question text itself, using concise plain language from the current profile, existing premises, or identified gaps. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"What kind of role are you looking for?\"\n- Good: \"To improve matches from your founder/operator profile, what kind of role are you looking for?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. Default one question. Add a second only when a DIFFERENT strategy genuinely complements the first and unblocks a clearly distinct decision. Never ask two questions of the same strategy unless their decision domains differ.\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the profile domain. Examples: \"Location\", \"Role\", \"Skills\", \"Goals\", \"Interests\", \"Availability\", \"Stage\".\n\nAnti-patterns — never do these.\n- Don't ask about fields already filled in the profile.\n- Don't ask about information already captured in an existing premise.\n- Don't ask procedural confirmations (\"Should I update your profile?\").\n- Don't ask vague introspective questions (\"Who are you really?\").\n- Don't re-ask for facts visible anywhere in the profile data or premises shown.\n\nOutput. Return at most 2 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the profile is already complete enough for discovery, return \"questions\": [].`;\n\n/**\n * Build the user message for the profile preset from a ProfileContext.\n * @param ctx - The profile context including current profile data and identified gaps.\n * @returns The assembled user message string.\n */\nfunction buildProfilePrompt(ctx: ProfileContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const premisesBlock =\n ctx.existingPremises && ctx.existingPremises.length > 0\n ? ctx.existingPremises.map((p, i) => `${i + 1}. ${p}`).join(\"\\n\")\n : \"(none)\";\n\n const gapsBlock = ctx.gaps.length > 0 ? ctx.gaps.join(\", \") : \"(none identified)\";\n\n const parts: string[] = [\n \"## Current profile\",\n profileBlock,\n \"\",\n \"## Existing premises\",\n premisesBlock,\n \"\",\n \"## Identified gaps\",\n gapsBlock,\n \"\",\n ];\n\n parts.push(\n \"## Your task\",\n \"Generate the minimum set of questions needed to fill the identified gaps.\",\n \"Apply every rule from your system prompt before outputting.\",\n \"Return an empty `questions` array if the profile is already complete enough.\",\n );\n\n return parts.join(\"\\n\");\n}\n\n// ─── Negotiation preset ──────────────────────────────────────────────────────\n\nconst NEGOTIATION_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. You generate negotiation-mode questions for one of two purposes described in the user message.\n\nPOST-STALL purpose. A negotiation between this user and another person ended without a clear outcome — either the turn budget was exhausted, the session timed out, or conversation stalled. Surface the minimum set of structured questions that help the user provide the missing signal needed to unblock or refine the next discovery attempt.\n\nUPTAKE purpose. The user is considering accepting a proposed connection, but one preparatory condition about the other person's practical ability, resources, availability, or authority to carry out the proposed activity needs clarification before commitment. Generate exactly ONE neutral question that lets the user decide whether they understand that condition well enough to proceed. Ask about the concrete activity and the other person's stated attributes; do not accuse, challenge credibility, or presume incapability. Never reveal a numeric authority score, threshold, felicity label, evaluator judgment, or any internal matching/verification mechanics. Do not ask whether the user wants to accept; clarify the preparatory condition only.\n\nYou may pick from three strategies. Choose contextually; mix only when each question is genuinely distinct.\n- refine_intent: help the user sharpen their underlying signal based on what the negotiation revealed (scope, scale, priority, direction).\n- surface_missing_detail: ask for one concrete piece of information that was absent and would have moved the negotiation forward (timeline, budget, format, constraints, decision criteria, …).\n- reflective_summary: mirror the key takeaway from the negotiation and ask the user to confirm, correct, or decide — useful when the conversation revealed partial signal worth locking in.\n\nAsk a question only when ALL of these hold:\n1. The answer is not already visible in the negotiation context or user profile shown.\n2. The answer would materially change how the next attempt surfaces or engages candidates.\n3. The question targets a different decision domain from any other question in this batch.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. Naturally include the user's underlying goal or topic and the relevant community in the question text itself, in plain language drawn from their intent or profile — NOT the mechanics of the match attempt. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"Which role is a better fit for your immediate needs?\"\n- Good: \"For your search for AI infrastructure collaborators in the AI founders community, what kind of working relationship fits your immediate needs?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. For POST-STALL, default one question and add a second only when a DIFFERENT strategy genuinely complements the first and unblocks a clearly distinct decision. For UPTAKE, return exactly one question — never zero and never more than one.\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Scope\", \"Timeline\", \"Budget\", \"Priority\", \"Format\", \"Stance\", \"Criteria\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I try again?\").\n- Don't re-ask for facts already visible in the user profile.\n- Don't ask vague introspective questions (\"What do you really want?\").\n- Don't ask about hypothetical edge cases not implied by the negotiation context.\n\nOutput. For POST-STALL, return at most 2 entries and return \"questions\": [] if the context already contains enough signal to proceed. For UPTAKE, return exactly 1 entry. Every entry must include a \"strategy\" field (one of the three values above). QUD metadata is orthogonal to uptake purpose: an uptake question is usually not an underspecification repair, so set \\`underspecificationType\\` to null unless the question genuinely repairs a missing constituent, missing constraint, or open alternative set.`;\n\n/**\n * Build the user message for the negotiation preset from a NegotiationContext.\n * @param ctx - The negotiation context including counterparty hint, stall reason, and key takeaway.\n * @returns The assembled user message string.\n */\nfunction buildPostStallNegotiationPrompt(ctx: PostStallNegotiationContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n return [\n \"## Purpose\",\n \"POST-STALL\",\n \"\",\n \"## Negotiation context\",\n `Community: ${ctx.indexContext}`,\n `Counterparty: ${ctx.counterpartyHint}`,\n `Stall reason: ${ctx.outcomeReason}`,\n \"\",\n \"## Key takeaway\",\n ctx.keyTake,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Identify the minimum set of questions the user must answer to unblock the next discovery attempt.\",\n \"Apply every rule from your system prompt before outputting.\",\n \"Return an empty `questions` array if the context already contains enough signal to proceed.\",\n ].join(\"\\n\");\n}\n\nfunction buildUptakeNegotiationPrompt(ctx: UptakeNegotiationContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n const evidence = ctx.preparatoryEvidence?.trim() || \"(no additional public evidence provided)\";\n\n return [\n \"## Purpose\",\n \"UPTAKE — preparatory-condition clarification before acceptance\",\n \"\",\n \"## Proposed activity\",\n ctx.proposedActivity,\n \"\",\n \"## Other person\",\n ctx.counterpartyHint,\n \"\",\n \"## Community\",\n ctx.indexContext,\n \"\",\n \"## Public preparatory evidence\",\n evidence,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Generate exactly one neutral, referentially closed question about whether the other person can practically carry out the proposed activity.\",\n \"Do not expose scores, thresholds, labels, or internal mechanics. Do not ask for acceptance itself.\",\n \"Set `underspecificationType` to null unless this is genuinely a QUD underspecification repair.\",\n ].join(\"\\n\");\n}\n\nfunction buildNegotiationPrompt(ctx: NegotiationContext): string {\n return ctx.purpose === \"uptake\"\n ? buildUptakeNegotiationPrompt(ctx)\n : buildPostStallNegotiationPrompt(ctx);\n}\n\n// ─── Chat preset ─────────────────────────────────────────────────────────\n\nconst NEGOTIATION_INFLIGHT_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The user's own negotiator agent is MID-NEGOTIATION on their behalf and has paused: before continuing, it needs a decision or missing input from its client — most often permission to disclose a specific piece of information to the other side. The negotiation is WAITING until the user answers. Your job: turn the negotiator's stated need (and its draft question, when provided) into the minimum set of crisp, structured decision questions.\n\nBias toward disclosure gating. The most common question shape is \"may I share X with this person?\" — an enable/disable decision about revealing specific information. Phrase these as a clear yes/no choice: the first option authorizes sharing, the second declines. State in each option's description what the negotiator will DO next (share and continue, or continue without revealing it). When the need is a missing fact rather than a permission, ask for that concrete input instead.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- surface_missing_detail: ask for one concrete missing input the negotiator needs to proceed (a fact, constraint, preference, or bound the client never stated).\n- reflective_summary: put a disclosure or stance decision in front of the client to confirm or decline — the enable/disable gate described above.\n\nHonor the negotiator's intent. When a draft question is provided, treat it as the source of truth for WHAT to ask — improve wording, tighten options, add consequence-focused descriptions. Do not invent questions about topics the negotiator did not raise. When no draft is provided, derive the question strictly from the disclosure subject.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. The user may see this question hours later in an inbox, away from any negotiation view. Naturally include the disclosure subject and a concrete description of the other person (drawn from the counterparty hint — their attributes, e.g. \"a Berlin-based AI-infrastructure founder\") in the question text itself. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"Can I share your budget with them?\"\n- Good: \"May I share your budget range with a Berlin-based AI-infrastructure founder you're being matched with?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nException — describing the other side. Unlike other surfaces, this question is ABOUT a live counterparty, so you must reference them — do it by restating their concrete attributes from the counterparty hint (\"a fintech CTO exploring agent tooling\"), never as \"the counterparty\", \"them\", or \"this person\" without an attribute anchor in the same sentence, and never by implying a list or pipeline.\n\nCardinality. Default one question. Add a second ONLY when the negotiator's need genuinely spans two distinct decisions (e.g. one disclosure gate plus one missing fact). Never pad.\n\nOption construction. Each option must represent a meaningfully different outcome. For disclosure gates: authorize first, decline second; suffix the safer or more common path with \" (Recommended)\" and list it first when one clearly is. The description states the CONSEQUENCE for the negotiation — what the negotiator does next. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically, which also lets the user add nuance (\"share the range but not the exact figure\").\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Disclosure\", \"Budget\", \"Timing\", \"Intro\", \"Scope\", \"Contact\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I keep negotiating?\").\n- Don't re-ask for facts already visible in the user profile.\n- Don't broaden beyond the negotiator's stated disclosure subject or draft.\n- Don't reveal the counterparty's identity — attributes only.\n- Don't ask vague introspective questions.\n\nOutput. Return at most 2 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the profile or context shown already answers the negotiator's need, return \"questions\": [].`;\n\n/**\n * Build the user message for the negotiation-inflight preset from a NegotiationInflightContext.\n * @param ctx - The inflight context: disclosure subject, counterparty hint, optional draft, community, user context.\n * @returns The assembled user message string.\n */\nfunction buildNegotiationInflightPrompt(ctx: NegotiationInflightContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const draftBlock = ctx.draftQuestion?.trim()\n ? ctx.draftQuestion.trim()\n : \"(none — derive the question from the disclosure subject)\";\n\n return [\n \"## Negotiation context\",\n `Community: ${ctx.indexContext}`,\n `Counterparty: ${ctx.counterpartyHint}`,\n \"\",\n \"## What the negotiator needs from its client\",\n ctx.disclosureSubject,\n \"\",\n \"## Draft question proposed by the negotiator\",\n draftBlock,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Produce the minimum set of structured questions that get the negotiator the permission or input it needs to continue.\",\n \"Honor the draft when provided; refine its wording and options rather than replacing its topic.\",\n \"Apply every rule from your system prompt before outputting.\",\n ].join(\"\\n\");\n}\n\nconst CHAT_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The protocol's chat orchestrator is mid-conversation with the user and has decided it needs a decision or missing input from them before it can continue. The conversation is PAUSED until the user answers. Your job: turn the orchestrator's stated need (and any draft questions it proposed) into the minimum set of crisp, structured decision questions.\n\nUnlike other question surfaces, these questions render INLINE in the active conversation, immediately after the assistant's last message — the user has full conversational context. Still keep each prompt self-contained enough to make sense on its own line.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- surface_missing_detail: ask for one concrete missing input the orchestrator needs to proceed (scope, timing, budget, format, preference, constraint, …).\n- refine_intent: ask the user to choose a direction when the orchestrator faces meaningfully different paths forward.\n\nHonor the orchestrator's intent. When draft questions are provided, treat them as the source of truth for WHAT to ask — improve wording, tighten options, add consequence-focused descriptions, and drop redundant drafts. Do not invent questions about topics the orchestrator did not raise. When no drafts are provided, derive questions strictly from the stated purpose.\n\nAsk a question only when ALL of these hold:\n1. The answer is not already visible in the conversation excerpt or user profile shown.\n2. The answer materially changes what the orchestrator does next.\n3. The question targets a different decision domain from any other question in this batch.\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. Default one question. Emit a second or third ONLY when the orchestrator's purpose or drafts genuinely require separate decisions in distinct domains. Never pad.\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option for what happens next in the conversation, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Direction\", \"Scope\", \"Timing\", \"Budget\", \"Format\", \"Priority\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I continue?\", \"Is that OK?\").\n- Don't re-ask for facts visible in the conversation excerpt or user profile.\n- Don't broaden beyond the orchestrator's stated purpose.\n- Don't ask vague introspective questions.\n\nOutput. Return at most 3 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the purpose is already answerable from the context shown, return \"questions\": [].`;\n\n/**\n * Build the user message for the chat preset from a ChatContext.\n * @param ctx - The chat context: orchestrator purpose, optional drafts, conversation excerpt, user context.\n * @returns The assembled user message string.\n */\nfunction buildChatPrompt(ctx: ChatContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const draftsBlock =\n ctx.draftQuestions && ctx.draftQuestions.length > 0\n ? ctx.draftQuestions\n .map((d, i) => {\n const opts = d.options && d.options.length > 0 ? ` [options: ${d.options.join(\" | \")}]` : \"\";\n const multi = d.multiSelect ? \" [multi-select]\" : \"\";\n return `${i + 1}. ${d.prompt}${opts}${multi}`;\n })\n .join(\"\\n\")\n : \"(none — derive questions from the purpose)\";\n\n const excerptBlock = ctx.conversationExcerpt?.trim()\n ? ctx.conversationExcerpt.trim()\n : \"(not available)\";\n\n return [\n \"## What the orchestrator needs to learn\",\n ctx.purpose,\n \"\",\n \"## Draft questions proposed by the orchestrator\",\n draftsBlock,\n \"\",\n \"## Recent conversation excerpt\",\n excerptBlock,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Produce the minimum set of structured questions that get the orchestrator the decision or input it needs.\",\n \"Honor the drafts when provided; refine their wording and options rather than replacing their topics.\",\n \"Apply every rule from your system prompt before outputting.\",\n ].join(\"\\n\");\n}\n\n/**\n * pool_discovery has NO preset by design: those questions are synthesized\n * deterministically from mined discriminators (see\n * `opportunity/discriminator/discriminator.question.ts`) and never reach the\n * QuestionerAgent. `getPreset(\"pool_discovery\")` therefore throws — the\n * QuestionerQueue branches on the mode before invoking the agent.\n */\nfunction withQudMetadataRules(systemPrompt: string): string {\n return `${systemPrompt}\\n\\n${QUD_UNDERSPECIFICATION_RULES}`;\n}\n\nconst presets: Partial<Record<QuestionMode, QuestionerPreset>> = {\n discovery: {\n systemPrompt: withQudMetadataRules(DISCOVERY_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) =>\n buildDiscoveryQuestionPrompt(context as Parameters<typeof buildDiscoveryQuestionPrompt>[0]),\n },\n intent: {\n systemPrompt: withQudMetadataRules(INTENT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildIntentPrompt(context as IntentContext),\n },\n enrichment: {\n systemPrompt: withQudMetadataRules(PROFILE_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildProfilePrompt(context as ProfileContext),\n },\n negotiation: {\n systemPrompt: withQudMetadataRules(NEGOTIATION_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildNegotiationPrompt(context as NegotiationContext),\n },\n negotiation_inflight: {\n systemPrompt: withQudMetadataRules(NEGOTIATION_INFLIGHT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildNegotiationInflightPrompt(context as NegotiationInflightContext),\n },\n chat: {\n systemPrompt: withQudMetadataRules(CHAT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildChatPrompt(context as ChatContext),\n },\n};\n\n/**\n * Retrieve the preset for the given mode.\n * @param mode - The question mode to look up.\n * @returns The matching preset with systemPrompt and buildPrompt.\n * @throws Error if the mode's preset is not yet implemented.\n */\nexport function getPreset(mode: QuestionMode, purpose?: QuestionPurpose): QuestionerPreset {\n if (mode === \"intent\" && purpose === \"recovery\") {\n return {\n systemPrompt: withQudMetadataRules(RECOVERY_INTENT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildRecoveryIntentPrompt(context as RecoveryIntentContext),\n };\n }\n const preset = presets[mode];\n if (!preset) {\n throw new Error(`QuestionerAgent preset \"${mode}\" is not implemented yet`);\n }\n return preset;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"questioner.presets.js","sourceRoot":"/","sources":["questioner/questioner.presets.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Mode presets for the QuestionerAgent. Each preset provides a system prompt\n * and a buildPrompt function that assembles the user message from a typed\n * context object. Only the `discovery` preset ships in Slice 1; others throw\n * until their implementation slices land.\n */\nimport type { QuestionMode, QuestionPurpose } from \"../shared/schemas/question.schema.js\";\nimport { DISCOVERY_SYSTEM_PROMPT, buildDiscoveryQuestionPrompt } from \"./questioner.discovery.prompt.js\";\n\nimport { QUD_UNDERSPECIFICATION_RULES } from \"./questioner.qud.js\";\nimport type { ChatContext, IntentContext, NegotiationContext, NegotiationInflightContext, PostStallNegotiationContext, ProfileContext, RecoveryIntentContext, UptakeNegotiationContext } from \"./questioner.types.js\";\n\n/**\n * Shared rule block appended to every questioner system prompt. Enforces that\n * the generated `prompt` resolves on its own — no demonstratives/anaphora that\n * point at people, events, or prior turns the reader cannot see — and never\n * narrates Index's own matching pipeline. Closes the referential-leak class\n * surfaced in digest audits (\"…with these builders?\", \"the previous\n * negotiation stalled because the counterparty didn't mention …\").\n */\nconst REFERENTIAL_CLOSURE_RULES = `Referential closure. The prompt must resolve entirely on its own, with no dangling references. The reader sees ONLY the question text — never the people you reviewed, the counterparty, the events on their calendar, or this conversation. Do not use demonstratives or definite anaphora that point at things the reader cannot see: \"these builders\", \"those founders\", \"these researchers\", \"these conversations\", \"this lunch\", \"the speaker\". If you reference a person, name them. If you reference a group, restate the concrete shared attribute inside the question itself (\"founders working on decentralized identity\"), never \"these founders\". Never imply a list, set, or prior exchange the reader is not currently looking at.\n- Bad: \"What kind of collaboration are you looking for with these builders?\"\n- Good: \"You're meeting people building agent infrastructure — what kind of collaboration are you looking for?\"\n\nNo process narration. Never describe Index's own activity or internal state. Forbidden: \"the previous negotiation\", \"the negotiation stalled\", \"opportunities found so far\", \"my search\", \"the counterparty\", \"candidates reviewed\", restating why a match did or did not happen, or quoting words a counterparty did or did not use. Ask about the user's goal or intent directly, never about the matching pipeline.\n- Bad: \"The previous negotiation stalled because the counterparty didn't mention 'matchmaking'. Should I broaden the search?\"\n- Good: \"Do you want to focus on dedicated matchmakers, or also people interested in relationships more broadly?\"`;\n\nexport interface QuestionerPreset {\n /** The LLM system prompt for this mode. */\n systemPrompt: string;\n /** Builds the user-message string from the mode-specific context. */\n buildPrompt: (context: unknown) => string;\n}\n\n/**\n * Renders the user-context block shared by every preset's user message from the\n * global user_context paragraph (the profile-replacing identity text).\n * @param userContext - The user's global context paragraph, if available.\n * @returns The trimmed context paragraph, or \"(no profile data)\" when empty.\n */\nfunction buildUserContextBlock(userContext?: string): string {\n const trimmed = userContext?.trim();\n return trimmed && trimmed.length > 0 ? trimmed : \"(no profile data)\";\n}\n\n// ─── Intent preset ──────────────────────────────────────────────────────────\n\nconst INTENT_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The user has stated an intent — what they are looking for. Your job: surface the minimum set of structured questions that help the user sharpen that intent before the protocol runs discovery on their behalf.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- refine_intent: ask the user to sharpen or pivot the core signal (scope, scale, specificity, direction).\n- surface_missing_detail: ask for one concrete missing input that would change which candidates surface (stage, location, timing, budget, constraints, format, …).\n\nAsk a question only when ALL of these hold:\n1. The agent cannot infer the answer from the intent text or user profile already shown.\n2. The answer would materially change which candidates surface.\n3. The question targets a different decision domain from any other question in this batch.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. Naturally include the source intent/topic in the question text itself, using concise plain language from the intent or summary. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"What kind of collaboration are you looking for?\"\n- Good: \"For your decentralized identity protocol-design search, what kind of collaboration are you looking for?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. Default one question. Add a second only when a DIFFERENT strategy genuinely complements the first and unblocks a clearly distinct decision. Never ask two questions of the same strategy unless their decision domains differ (different titles).\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Stage\", \"Timing\", \"Location\", \"Scope\", \"Budget\", \"Format\", \"Skills\", \"Collab\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I start searching?\").\n- Don't ask about hypothetical edge cases not implied by the intent.\n- Don't re-ask for facts already visible in the user profile.\n- Don't ask vague introspective questions (\"What do you really want?\").\n\nOutput. Return at most 2 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the intent is already specific enough, return \"questions\": [].`;\n\n/**\n * Build the user message for the intent preset from an IntentContext.\n * @param ctx - The intent context.\n * @returns The assembled user message string.\n */\nfunction buildIntentPrompt(ctx: IntentContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const summaryBlock = ctx.summary ? ctx.summary : \"(no summary available)\";\n\n return [\n \"## Intent\",\n ctx.payload,\n \"\",\n \"## Summary\",\n summaryBlock,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Identify the minimum set of questions the user must answer to sharpen this intent.\",\n \"Apply every rule from your system prompt before outputting.\",\n \"Return an empty `questions` array if the intent is already specific enough.\",\n ].join(\"\\n\");\n}\n\nconst RECOVERY_INTENT_SYSTEM_PROMPT = `You help a user materially refine one of their own discovery signals after an authoritative discovery run completed without leaving a currently actionable opportunity. Ask directly about the user's goal, desired counterpart attributes, or constraints. Never describe the run or imply that any specific person was reviewed.\n\nChoose exactly one of these strategies:\n- refine_intent: sharpen or pivot the core signal in a way that changes who should surface.\n- surface_missing_detail: ask for one concrete missing constraint such as stage, location, timing, budget, format, or required capability.\n\nAsk a question only when the answer is not already inferable from the intent, summary, or user profile and would materially change discovery. If no safe, useful missing axis exists, return an empty questions array.\n\nStandalone prompt rule. The prompt must identify the user's intent topic in plain language and ask about their own goal or constraints. It must make sense without UI labels or hidden metadata.\n\n${REFERENTIAL_CLOSURE_RULES}\n\nPrivacy and process boundary. You may receive only a bounded aggregate count of validated prior outcomes. Use it solely as a weak signal to choose a missing axis. Never mention or imply counts, matches, no matches, rejections, negotiations, candidates, counterparties, searches, retries, pipeline state, evidence, or what another person did or did not say. Never invent people or facts.\n\nCardinality. Return at most ONE question. Use only refine_intent or surface_missing_detail. The question must nudge a material edit, not ask whether to search again.\n\nOption construction. Provide 2–4 materially different outcomes. Put the safest or most common path first with \" (Recommended)\" when appropriate. Descriptions state the discovery consequence. Never add an Other option.\n\nTitle rules. ≤12 chars and name the decision domain, such as Scope, Timing, Location, Stage, Budget, Format, or Skills.\n\nOutput. Return zero or one question. Set underspecificationType to the applicable QUD category, or null when none applies.`;\n\nfunction buildRecoveryIntentPrompt(ctx: RecoveryIntentContext): string {\n return [\n \"## Intent\",\n ctx.payload,\n \"\",\n \"## Summary\",\n ctx.summary?.trim() || \"(no summary available)\",\n \"\",\n \"## User profile\",\n buildUserContextBlock(ctx.userContext),\n ...(ctx.rejectedNegotiationCount\n ? [\n \"\",\n \"## Private aggregate signal\",\n `Validated prior outcomes without an actionable connection: ${ctx.rejectedNegotiationCount}`,\n ]\n : []),\n \"\",\n \"## Your task\",\n \"Ask at most one direct question whose answer would materially refine this intent.\",\n \"Do not narrate discovery outcomes or internal process state in the question.\",\n \"Return an empty `questions` array when no safe useful axis is missing.\",\n ].join(\"\\n\");\n}\n\n// ─── Profile preset ─────────────────────────────────────────────────────────\n\nconst PROFILE_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The user has a profile that is incomplete. Your job: surface the minimum set of structured questions that fill the identified gaps — asking about location, skills, interests, current work, or goals — so the protocol can run better discovery on their behalf.\n\nThe user may already have premises — atomic self-descriptions they have stated. These cover specific profile domains. Do not ask about domains already addressed by existing premises. Focus only on gaps not covered by any premise.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- surface_missing_detail: ask for one concrete missing piece of profile data (location, current role, skills, interests, goals, availability, …).\n- refine_intent: ask the user to clarify or sharpen an existing profile signal so candidates can be ranked more accurately.\n\nAsk a question only when ALL of these hold:\n1. The answer is not already visible in the profile data shown.\n2. The answer is not already covered by an existing premise listed below the profile.\n3. The answer would meaningfully change which opportunities surface for this user.\n4. The question targets a different profile domain from any other question in this batch.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. Naturally include the profile signal or gap being clarified in the question text itself, using concise plain language from the current profile, existing premises, or identified gaps. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"What kind of role are you looking for?\"\n- Good: \"To improve matches from your founder/operator profile, what kind of role are you looking for?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. Default one question. Add a second only when a DIFFERENT strategy genuinely complements the first and unblocks a clearly distinct decision. Never ask two questions of the same strategy unless their decision domains differ.\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the profile domain. Examples: \"Location\", \"Role\", \"Skills\", \"Goals\", \"Interests\", \"Availability\", \"Stage\".\n\nAnti-patterns — never do these.\n- Don't ask about fields already filled in the profile.\n- Don't ask about information already captured in an existing premise.\n- Don't ask procedural confirmations (\"Should I update your profile?\").\n- Don't ask vague introspective questions (\"Who are you really?\").\n- Don't re-ask for facts visible anywhere in the profile data or premises shown.\n\nOutput. Return at most 2 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the profile is already complete enough for discovery, return \"questions\": [].`;\n\n/**\n * Build the user message for the profile preset from a ProfileContext.\n * @param ctx - The profile context including current profile data and identified gaps.\n * @returns The assembled user message string.\n */\nfunction buildProfilePrompt(ctx: ProfileContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const premisesBlock =\n ctx.existingPremises && ctx.existingPremises.length > 0\n ? ctx.existingPremises.map((p, i) => `${i + 1}. ${p}`).join(\"\\n\")\n : \"(none)\";\n\n const gapsBlock = ctx.gaps.length > 0 ? ctx.gaps.join(\", \") : \"(none identified)\";\n\n const parts: string[] = [\n \"## Current profile\",\n profileBlock,\n \"\",\n \"## Existing premises\",\n premisesBlock,\n \"\",\n \"## Identified gaps\",\n gapsBlock,\n \"\",\n ];\n\n parts.push(\n \"## Your task\",\n \"Generate the minimum set of questions needed to fill the identified gaps.\",\n \"Apply every rule from your system prompt before outputting.\",\n \"Return an empty `questions` array if the profile is already complete enough.\",\n );\n\n return parts.join(\"\\n\");\n}\n\n// ─── Negotiation preset ──────────────────────────────────────────────────────\n\nconst NEGOTIATION_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. You generate negotiation-mode questions for one of two purposes described in the user message.\n\nPOST-STALL purpose. A negotiation between this user and another person ended without a clear outcome — either the turn budget was exhausted, the session timed out, or conversation stalled. Surface the minimum set of structured questions that help the user provide the missing signal needed to unblock or refine the next discovery attempt.\n\nUPTAKE purpose. The user is considering accepting a proposed connection, but one preparatory condition about the other person's practical ability, resources, availability, or authority to carry out the proposed activity needs clarification before commitment. Generate exactly ONE neutral question that lets the user decide whether they understand that condition well enough to proceed. Ask about the concrete activity and refer to the other participant generically; do not accuse, challenge credibility, presume incapability, or invent/repeat profile attributes. Never reveal a numeric authority score, threshold, felicity label, evaluator judgment, or any internal matching/verification mechanics. Do not ask whether the user wants to accept; clarify the preparatory condition only.\n\nYou may pick from three strategies. Choose contextually; mix only when each question is genuinely distinct.\n- refine_intent: help the user sharpen their underlying signal based on what the negotiation revealed (scope, scale, priority, direction).\n- surface_missing_detail: ask for one concrete piece of information that was absent and would have moved the negotiation forward (timeline, budget, format, constraints, decision criteria, …).\n- reflective_summary: mirror the key takeaway from the negotiation and ask the user to confirm, correct, or decide — useful when the conversation revealed partial signal worth locking in.\n\nAsk a question only when ALL of these hold:\n1. The answer is not already visible in the negotiation context or user profile shown.\n2. The answer would materially change how the next attempt surfaces or engages candidates.\n3. The question targets a different decision domain from any other question in this batch.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. Naturally include the user's own underlying goal or topic and, only when supplied as a source-safe label, the relevant community in the question text itself. Never infer or repeat counterparty identity/profile, private transcript, evaluator reasoning, match reasons, event/community attendance, or internal IDs/metadata. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text to explain what the question is about.\n- Bad: \"Which role is a better fit for your immediate needs?\"\n- Good: \"For your search for AI infrastructure collaborators in the AI founders community, what kind of working relationship fits your immediate needs?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. For POST-STALL, default one question and add a second only when a DIFFERENT strategy genuinely complements the first and unblocks a clearly distinct decision. For UPTAKE, return exactly one question — never zero and never more than one.\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Scope\", \"Timeline\", \"Budget\", \"Priority\", \"Format\", \"Stance\", \"Criteria\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I try again?\").\n- Don't re-ask for facts already visible in the user profile.\n- Don't ask vague introspective questions (\"What do you really want?\").\n- Don't ask about hypothetical edge cases not implied by the negotiation context.\n\nOutput. For POST-STALL, return at most 2 entries and return \"questions\": [] if the context already contains enough signal to proceed. For UPTAKE, return exactly 1 entry. Every entry must include a \"strategy\" field (one of the three values above). QUD metadata is orthogonal to uptake purpose: an uptake question is usually not an underspecification repair, so set \\`underspecificationType\\` to null unless the question genuinely repairs a missing constituent, missing constraint, or open alternative set.`;\n\n/**\n * Build the user message for the negotiation preset from a NegotiationContext.\n * @param ctx - The negotiation context including counterparty hint, stall reason, and key takeaway.\n * @returns The assembled user message string.\n */\nfunction buildPostStallNegotiationPrompt(ctx: PostStallNegotiationContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n return [\n \"## Purpose\",\n \"POST-STALL\",\n \"\",\n \"## Negotiation context\",\n `Community: ${ctx.indexContext}`,\n `Other participant: ${ctx.counterpartyHint}`,\n `Stall reason: ${ctx.outcomeReason}`,\n \"\",\n \"## Recipient's own signal\",\n ctx.recipientIntent,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Identify the minimum set of questions the user must answer to unblock the next discovery attempt.\",\n \"Apply every rule from your system prompt before outputting.\",\n \"Return an empty `questions` array if the context already contains enough signal to proceed.\",\n ].join(\"\\n\");\n}\n\nfunction buildUptakeNegotiationPrompt(ctx: UptakeNegotiationContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n return [\n \"## Purpose\",\n \"UPTAKE — preparatory-condition clarification before acceptance\",\n \"\",\n \"## Proposed activity\",\n ctx.proposedActivity,\n \"\",\n \"## Other person\",\n ctx.counterpartyHint,\n \"\",\n \"## Community\",\n ctx.indexContext,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Generate exactly one neutral, referentially closed question about whether the other person can practically carry out the proposed activity.\",\n \"Do not expose scores, thresholds, labels, or internal mechanics. Do not ask for acceptance itself.\",\n \"Set `underspecificationType` to null unless this is genuinely a QUD underspecification repair.\",\n ].join(\"\\n\");\n}\n\nfunction buildNegotiationPrompt(ctx: NegotiationContext): string {\n return ctx.purpose === \"uptake\"\n ? buildUptakeNegotiationPrompt(ctx)\n : buildPostStallNegotiationPrompt(ctx);\n}\n\n// ─── Chat preset ─────────────────────────────────────────────────────────\n\nconst NEGOTIATION_INFLIGHT_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The user's own negotiator agent is MID-NEGOTIATION on their behalf and has paused: before continuing, it needs a decision or missing input from its client — most often permission to disclose a specific piece of information to the other side. The negotiation is WAITING until the user answers. Your job: turn the negotiator's stated need (and its draft question, when provided) into the minimum set of crisp, structured decision questions.\n\nBias toward disclosure gating. The most common question shape is \"may I share X with this person?\" — an enable/disable decision about revealing specific information. Phrase these as a clear yes/no choice: the first option authorizes sharing, the second declines. State in each option's description what the negotiator will DO next (share and continue, or continue without revealing it). When the need is a missing fact rather than a permission, ask for that concrete input instead.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- surface_missing_detail: ask for one concrete missing input the negotiator needs to proceed (a fact, constraint, preference, or bound the client never stated).\n- reflective_summary: put a disclosure or stance decision in front of the client to confirm or decline — the enable/disable gate described above.\n\nHonor the negotiator's intent. When a draft question is provided, treat it as the source of truth for WHAT to ask — improve wording, tighten options, add consequence-focused descriptions. Do not invent questions about topics the negotiator did not raise. When no draft is provided, derive the question strictly from the disclosure subject.\n\nStandalone prompt rule. Every generated \\`prompt\\` must be understandable outside the conversation where it was created. The user may see this question hours later in an inbox, away from any negotiation view. Include the disclosure subject and the generic phrase \"the other participant in this match\" in the question text itself. Never repeat or infer counterparty identity/profile, private transcript, evaluator reasoning, match reasons, event/community attendance, or internal IDs/metadata. Do not rely on \\`title\\`, UI labels, hidden metadata, or surrounding digest/chat text.\n- Bad: \"May I share your budget with Alex, a Berlin-based founder from the event?\"\n- Good: \"May I share your budget range with the other participant in this match?\"\n\n${REFERENTIAL_CLOSURE_RULES}\n\nException — describing the other side. This question may refer to the other participant generically because it is tied to one exact server-validated opportunity. Do not add attributes, identity, or claims about why they matched.\n\nCardinality. Default one question. Add a second ONLY when the negotiator's need genuinely spans two distinct decisions (e.g. one disclosure gate plus one missing fact). Never pad.\n\nOption construction. Each option must represent a meaningfully different outcome. For disclosure gates: authorize first, decline second; suffix the safer or more common path with \" (Recommended)\" and list it first when one clearly is. The description states the CONSEQUENCE for the negotiation — what the negotiator does next. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically, which also lets the user add nuance (\"share the range but not the exact figure\").\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Disclosure\", \"Budget\", \"Timing\", \"Intro\", \"Scope\", \"Contact\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I keep negotiating?\").\n- Don't re-ask for facts already visible in the user profile.\n- Don't broaden beyond the negotiator's stated disclosure subject or draft.\n- Don't reveal the counterparty's identity — attributes only.\n- Don't ask vague introspective questions.\n\nOutput. Return at most 2 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the profile or context shown already answers the negotiator's need, return \"questions\": [].`;\n\n/**\n * Build the user message for the negotiation-inflight preset from a NegotiationInflightContext.\n * @param ctx - The inflight context: disclosure subject, counterparty hint, optional draft, community, user context.\n * @returns The assembled user message string.\n */\nfunction buildNegotiationInflightPrompt(ctx: NegotiationInflightContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const draftBlock = ctx.draftQuestion?.trim()\n ? ctx.draftQuestion.trim()\n : \"(none — derive the question from the disclosure subject)\";\n\n return [\n \"## Negotiation context\",\n `Community: ${ctx.indexContext}`,\n `Counterparty: ${ctx.counterpartyHint}`,\n \"\",\n \"## What the negotiator needs from its client\",\n ctx.disclosureSubject,\n \"\",\n \"## Draft question proposed by the negotiator\",\n draftBlock,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Produce the minimum set of structured questions that get the negotiator the permission or input it needs to continue.\",\n \"Honor the draft when provided; refine its wording and options rather than replacing its topic.\",\n \"Apply every rule from your system prompt before outputting.\",\n ].join(\"\\n\");\n}\n\nconst CHAT_SYSTEM_PROMPT = `You sit between a human and a discovery protocol. The protocol's chat orchestrator is mid-conversation with the user and has decided it needs a decision or missing input from them before it can continue. The conversation is PAUSED until the user answers. Your job: turn the orchestrator's stated need (and any draft questions it proposed) into the minimum set of crisp, structured decision questions.\n\nUnlike other question surfaces, these questions render INLINE in the active conversation, immediately after the assistant's last message — the user has full conversational context. Still keep each prompt self-contained enough to make sense on its own line.\n\nYou may pick from two strategies. Choose contextually; mix only when each question is genuinely distinct.\n- surface_missing_detail: ask for one concrete missing input the orchestrator needs to proceed (scope, timing, budget, format, preference, constraint, …).\n- refine_intent: ask the user to choose a direction when the orchestrator faces meaningfully different paths forward.\n\nHonor the orchestrator's intent. When draft questions are provided, treat them as the source of truth for WHAT to ask — improve wording, tighten options, add consequence-focused descriptions, and drop redundant drafts. Do not invent questions about topics the orchestrator did not raise. When no drafts are provided, derive questions strictly from the stated purpose.\n\nAsk a question only when ALL of these hold:\n1. The answer is not already visible in the conversation excerpt or user profile shown.\n2. The answer materially changes what the orchestrator does next.\n3. The question targets a different decision domain from any other question in this batch.\n\n${REFERENTIAL_CLOSURE_RULES}\n\nCardinality. Default one question. Emit a second or third ONLY when the orchestrator's purpose or drafts genuinely require separate decisions in distinct domains. Never pad.\n\nOption construction. Each option must represent a meaningfully different outcome. Suffix the safest or most common path with \" (Recommended)\" and list it first. The description states the CONSEQUENCE of choosing the option for what happens next in the conversation, not its definition. 2–4 options. Never add an \"Other\" option — clients provide a free-text fallback automatically.\n\nTitle rules. ≤12 chars. Noun of the decision domain. Examples: \"Direction\", \"Scope\", \"Timing\", \"Budget\", \"Format\", \"Priority\".\n\nAnti-patterns — never do these.\n- Don't ask procedural confirmations (\"Should I continue?\", \"Is that OK?\").\n- Don't re-ask for facts visible in the conversation excerpt or user profile.\n- Don't broaden beyond the orchestrator's stated purpose.\n- Don't ask vague introspective questions.\n\nOutput. Return at most 3 entries in the \"questions\" array. Each entry must include a \"strategy\" field (one of the two values above). If the purpose is already answerable from the context shown, return \"questions\": [].`;\n\n/**\n * Build the user message for the chat preset from a ChatContext.\n * @param ctx - The chat context: orchestrator purpose, optional drafts, conversation excerpt, user context.\n * @returns The assembled user message string.\n */\nfunction buildChatPrompt(ctx: ChatContext): string {\n const profileBlock = buildUserContextBlock(ctx.userContext);\n\n const draftsBlock =\n ctx.draftQuestions && ctx.draftQuestions.length > 0\n ? ctx.draftQuestions\n .map((d, i) => {\n const opts = d.options && d.options.length > 0 ? ` [options: ${d.options.join(\" | \")}]` : \"\";\n const multi = d.multiSelect ? \" [multi-select]\" : \"\";\n return `${i + 1}. ${d.prompt}${opts}${multi}`;\n })\n .join(\"\\n\")\n : \"(none — derive questions from the purpose)\";\n\n const excerptBlock = ctx.conversationExcerpt?.trim()\n ? ctx.conversationExcerpt.trim()\n : \"(not available)\";\n\n return [\n \"## What the orchestrator needs to learn\",\n ctx.purpose,\n \"\",\n \"## Draft questions proposed by the orchestrator\",\n draftsBlock,\n \"\",\n \"## Recent conversation excerpt\",\n excerptBlock,\n \"\",\n \"## User profile\",\n profileBlock,\n \"\",\n \"## Your task\",\n \"Produce the minimum set of structured questions that get the orchestrator the decision or input it needs.\",\n \"Honor the drafts when provided; refine their wording and options rather than replacing their topics.\",\n \"Apply every rule from your system prompt before outputting.\",\n ].join(\"\\n\");\n}\n\n/**\n * pool_discovery has NO preset by design: those questions are synthesized\n * deterministically from mined discriminators (see\n * `opportunity/discriminator/discriminator.question.ts`) and never reach the\n * QuestionerAgent. `getPreset(\"pool_discovery\")` therefore throws — the\n * QuestionerQueue branches on the mode before invoking the agent.\n */\nfunction withQudMetadataRules(systemPrompt: string): string {\n return `${systemPrompt}\\n\\n${QUD_UNDERSPECIFICATION_RULES}`;\n}\n\nconst presets: Partial<Record<QuestionMode, QuestionerPreset>> = {\n discovery: {\n systemPrompt: withQudMetadataRules(DISCOVERY_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) =>\n buildDiscoveryQuestionPrompt(context as Parameters<typeof buildDiscoveryQuestionPrompt>[0]),\n },\n intent: {\n systemPrompt: withQudMetadataRules(INTENT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildIntentPrompt(context as IntentContext),\n },\n enrichment: {\n systemPrompt: withQudMetadataRules(PROFILE_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildProfilePrompt(context as ProfileContext),\n },\n negotiation: {\n systemPrompt: withQudMetadataRules(NEGOTIATION_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildNegotiationPrompt(context as NegotiationContext),\n },\n negotiation_inflight: {\n systemPrompt: withQudMetadataRules(NEGOTIATION_INFLIGHT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildNegotiationInflightPrompt(context as NegotiationInflightContext),\n },\n chat: {\n systemPrompt: withQudMetadataRules(CHAT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildChatPrompt(context as ChatContext),\n },\n};\n\n/**\n * Retrieve the preset for the given mode.\n * @param mode - The question mode to look up.\n * @returns The matching preset with systemPrompt and buildPrompt.\n * @throws Error if the mode's preset is not yet implemented.\n */\nexport function getPreset(mode: QuestionMode, purpose?: QuestionPurpose): QuestionerPreset {\n if (mode === \"intent\" && purpose === \"recovery\") {\n return {\n systemPrompt: withQudMetadataRules(RECOVERY_INTENT_SYSTEM_PROMPT),\n buildPrompt: (context: unknown) => buildRecoveryIntentPrompt(context as RecoveryIntentContext),\n };\n }\n const preset = presets[mode];\n if (!preset) {\n throw new Error(`QuestionerAgent preset \"${mode}\" is not implemented yet`);\n }\n return preset;\n}\n"]}
|