@indexnetwork/protocol 6.10.0-rc.398.1 → 6.10.2-rc.400.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.
@@ -490,9 +490,11 @@ export declare function readProtocolVersion(metadata: {
490
490
  protocolVersion?: unknown;
491
491
  } | null | undefined): NegotiationProtocolVersion | null;
492
492
  /**
493
- * Protocol version for genuinely fresh negotiations, from the
494
- * `NEGOTIATION_PROTOCOL_VERSION` env switch. Defaults to `v1` when unset —
495
- * v2 is opt-in per environment, and rolling back is the same single switch.
493
+ * Protocol version for negotiations without a prior task for the same
494
+ * opportunity, from the `NEGOTIATION_PROTOCOL_VERSION` env switch. Defaults
495
+ * to `v1` when unset — v2 is opt-in per environment, and rolling back is the
496
+ * same single switch (only in-flight negotiations stay pinned to their
497
+ * stamped version).
496
498
  */
497
499
  export declare function configuredProtocolVersion(): NegotiationProtocolVersion;
498
500
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"negotiation.protocol.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"negotiation.protocol.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.protocol.ts"],"names":[],"mappings":"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"}
@@ -137,9 +137,11 @@ export function readProtocolVersion(metadata) {
137
137
  return v === "v2" ? "v2" : v === "v1" ? "v1" : null;
138
138
  }
139
139
  /**
140
- * Protocol version for genuinely fresh negotiations, from the
141
- * `NEGOTIATION_PROTOCOL_VERSION` env switch. Defaults to `v1` when unset —
142
- * v2 is opt-in per environment, and rolling back is the same single switch.
140
+ * Protocol version for negotiations without a prior task for the same
141
+ * opportunity, from the `NEGOTIATION_PROTOCOL_VERSION` env switch. Defaults
142
+ * to `v1` when unset — v2 is opt-in per environment, and rolling back is the
143
+ * same single switch (only in-flight negotiations stay pinned to their
144
+ * stamped version).
143
145
  */
144
146
  export function configuredProtocolVersion() {
145
147
  return process.env.NEGOTIATION_PROTOCOL_VERSION === "v2" ? "v2" : "v1";
@@ -1 +1 @@
1
- {"version":3,"file":"negotiation.protocol.js","sourceRoot":"/","sources":["negotiation/negotiation.protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Seat-scoped negotiation protocol rules (v2 client-advocate protocol).\n *\n * v2 fixes exactly one initiating seat per match (`metadata.initiatorUserId`,\n * stamped at discovery time — IND-396) and makes consent asymmetric:\n * **accept can only come from the counterparty seat**, schema-enforced.\n *\n * Vocabulary per seat (v2):\n * - initiator: `outreach | counter | question | withdraw` (no accept)\n * - counterparty: `accept | decline | counter | question`\n * - final turn: initiator `withdraw | counter`; counterparty `accept | decline`\n *\n * v1 tasks keep the legacy vocabulary (`propose | accept | reject | counter |\n * question`) — the version is inherited per conversation, never re-stamped, so\n * in-flight v1 negotiations are grandfathered untouched.\n *\n * Outcome mapping is version-independent: `accept` → opportunity `pending`,\n * `reject`/`withdraw`/`decline` → `rejected`, turn-cap → `stalled`.\n */\nimport { z } from \"zod\";\n\nimport { AskUserPayloadSchema } from \"../shared/schemas/negotiation-state.schema.js\";\nimport type { NegotiationAction, NegotiationSeat, NegotiationProtocolVersion } from \"../shared/schemas/negotiation-state.schema.js\";\n\n// ─── Shared assessment fragment ──────────────────────────────────────────────\n\nconst AssessmentSchema = z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n});\n\nfunction turnSchema<T extends [NegotiationAction, ...NegotiationAction[]]>(actions: T) {\n return z.object({\n action: z.enum(actions),\n assessment: AssessmentSchema,\n message: z.string().nullable().optional(),\n /** Present when action is `ask_user` (v2, P3.2). */\n askUser: AskUserPayloadSchema.nullable().optional(),\n });\n}\n\n// ─── v2 seat-scoped turn schemas ─────────────────────────────────────────────\n\n/** Initiator seat, non-final turn: may reach out, push back, ask, or walk away — never accept. */\nexport const InitiatorTurnSchema = turnSchema([\"outreach\", \"counter\", \"question\", \"withdraw\"]);\n\n/** Counterparty seat, non-final turn: the only seat that can accept. */\nexport const CounterpartyTurnSchema = turnSchema([\"accept\", \"decline\", \"counter\", \"question\"]);\n\n/** Initiator seat, final allowed turn: commit to walking away or leave the door open. */\nexport const FinalInitiatorTurnSchema = turnSchema([\"withdraw\", \"counter\"]);\n\n/** Counterparty seat, final allowed turn: must decide. */\nexport const FinalCounterpartyTurnSchema = turnSchema([\"accept\", \"decline\"]);\n\n// ─── v2 ask_user variants (P3.2, flag-gated) ────────────────────────────────\n// Non-final turns only: the final-cap turn must decide, never pause. Selected\n// via the `opts.askUser` parameter on allowedActionsFor/turnSchemaFor — the\n// base schemas above stay byte-identical for every existing caller.\n\n/** Initiator seat, non-final turn, with the client-consult pause available. */\nexport const InitiatorAskUserTurnSchema = turnSchema([\"outreach\", \"counter\", \"question\", \"withdraw\", \"ask_user\"]);\n\n/** Counterparty seat, non-final turn, with the client-consult pause available. */\nexport const CounterpartyAskUserTurnSchema = turnSchema([\"accept\", \"decline\", \"counter\", \"question\", \"ask_user\"]);\n\n// ─── Action vocabulary per version + seat ────────────────────────────────────\n\nconst V1_ACTIONS: readonly NegotiationAction[] = [\"propose\", \"accept\", \"reject\", \"counter\", \"question\"];\nconst V1_FINAL_ACTIONS: readonly NegotiationAction[] = [\"accept\", \"reject\"];\nconst V2_INITIATOR_ACTIONS: readonly NegotiationAction[] = [\"outreach\", \"counter\", \"question\", \"withdraw\"];\nconst V2_COUNTERPARTY_ACTIONS: readonly NegotiationAction[] = [\"accept\", \"decline\", \"counter\", \"question\"];\nconst V2_FINAL_INITIATOR_ACTIONS: readonly NegotiationAction[] = [\"withdraw\", \"counter\"];\nconst V2_FINAL_COUNTERPARTY_ACTIONS: readonly NegotiationAction[] = [\"accept\", \"decline\"];\nconst V2_INITIATOR_ASK_USER_ACTIONS: readonly NegotiationAction[] = [...V2_INITIATOR_ACTIONS, \"ask_user\"];\nconst V2_COUNTERPARTY_ASK_USER_ACTIONS: readonly NegotiationAction[] = [...V2_COUNTERPARTY_ACTIONS, \"ask_user\"];\n\n/**\n * The set of actions a given seat may submit under a given protocol version.\n *\n * v1 ignores the seat entirely (legacy symmetric vocabulary) so pre-v2\n * negotiations behave exactly as before.\n */\nexport function allowedActionsFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n isFinalTurn = false,\n opts?: AskUserOpts,\n): readonly NegotiationAction[] {\n if (version !== \"v2\") return isFinalTurn ? V1_FINAL_ACTIONS : V1_ACTIONS;\n const askUser = opts?.askUser === true && !isFinalTurn;\n if (seat === \"initiator\") {\n return isFinalTurn ? V2_FINAL_INITIATOR_ACTIONS : (askUser ? V2_INITIATOR_ASK_USER_ACTIONS : V2_INITIATOR_ACTIONS);\n }\n return isFinalTurn ? V2_FINAL_COUNTERPARTY_ACTIONS : (askUser ? V2_COUNTERPARTY_ASK_USER_ACTIONS : V2_COUNTERPARTY_ACTIONS);\n}\n\n/**\n * Zod turn schema for a system-agent turn, selected by version + seat +\n * final-turn flag. v1 returns the legacy schemas (seat-agnostic); v2 returns\n * the seat-scoped schemas above, making an initiator `accept` structurally\n * impossible rather than prompt-discouraged.\n *\n * The v1 legacy schemas are passed in by the caller (they live in\n * `negotiation.state.ts`) to keep this module free of a state-module import.\n */\nexport function turnSchemaFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n isFinalTurn: boolean,\n v1Schemas: { system: z.ZodTypeAny; final: z.ZodTypeAny },\n opts?: AskUserOpts,\n): z.ZodTypeAny {\n if (version !== \"v2\") return isFinalTurn ? v1Schemas.final : v1Schemas.system;\n const askUser = opts?.askUser === true && !isFinalTurn;\n if (seat === \"initiator\") {\n return isFinalTurn ? FinalInitiatorTurnSchema : (askUser ? InitiatorAskUserTurnSchema : InitiatorTurnSchema);\n }\n return isFinalTurn ? FinalCounterpartyTurnSchema : (askUser ? CounterpartyAskUserTurnSchema : CounterpartyTurnSchema);\n}\n\n/**\n * Opt-in extension of the seat vocabulary with the `ask_user` client-consult\n * pause (P3.2). Never granted on final-cap turns (the final turn must decide)\n * and never under v1. Callers pass `{ askUser: true }` only when the full\n * pause loop is available on their surface: the ask-user feature flag is on,\n * a questioner enqueue and an answer-window timer are wired, the negotiation\n * has an opportunity to resume against, and the acting side has not already\n * consumed its one client question for this negotiation (rationing).\n */\nexport interface AskUserOpts {\n askUser?: boolean;\n}\n\n// ─── Action semantics (version-independent) ──────────────────────────────────\n\n/** Terminal actions end the negotiation immediately. */\nexport function isTerminalAction(action: string | undefined | null): boolean {\n return action === \"accept\" || action === \"reject\" || action === \"withdraw\" || action === \"decline\";\n}\n\n/** Reject-like actions map the opportunity to `rejected` (v1 reject, v2 withdraw/decline). */\nexport function isRejectLikeAction(action: string | undefined | null): boolean {\n return action === \"reject\" || action === \"withdraw\" || action === \"decline\";\n}\n\n/**\n * Conservative action when an agent produced schema-invalid output (after the\n * retry) or an internal error needs a seat-valid terminal placeholder.\n *\n * Non-final turns fall back to `counter` (keeps the dialogue open — the AC's\n * \"conservative counter\"). Final turns must decide: v1 → `reject`, v2\n * counterparty → `decline`, v2 initiator → `counter` is still legal on the\n * final turn so it stays `counter` (finalizes as turn-cap/stalled).\n */\nexport function fallbackActionFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n isFinalTurn: boolean,\n): NegotiationAction {\n if (!isFinalTurn) return \"counter\";\n if (version !== \"v2\") return \"reject\";\n return seat === \"counterparty\" ? \"decline\" : \"counter\";\n}\n\n/** Seat-appropriate reject-like action for error paths. */\nexport function rejectActionFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n): NegotiationAction {\n if (version !== \"v2\") return \"reject\";\n return seat === \"initiator\" ? \"withdraw\" : \"decline\";\n}\n\n// ─── Metadata readers ────────────────────────────────────────────────────────\n\n/**\n * Read the protocol version off task metadata. Returns null when the task\n * predates version stamping (treat as v1 at the call site when the task is a\n * genuine prior; fresh tasks stamp from {@link configuredProtocolVersion}).\n */\nexport function readProtocolVersion(\n metadata: { protocolVersion?: unknown } | null | undefined,\n): NegotiationProtocolVersion | null {\n const v = metadata?.protocolVersion;\n return v === \"v2\" ? \"v2\" : v === \"v1\" ? \"v1\" : null;\n}\n\n/**\n * Protocol version for genuinely fresh negotiations, from the\n * `NEGOTIATION_PROTOCOL_VERSION` env switch. Defaults to `v1` when unset —\n * v2 is opt-in per environment, and rolling back is the same single switch.\n */\nexport function configuredProtocolVersion(): NegotiationProtocolVersion {\n return process.env.NEGOTIATION_PROTOCOL_VERSION === \"v2\" ? \"v2\" : \"v1\";\n}\n\n/**\n * Whether the `ask_user` client-consult pause (P3.2) is enabled, from the\n * `NEGOTIATION_ASK_USER_ENABLED` env switch. Defaults to off — the deployment\n * is byte-for-byte unchanged until the flag is flipped, and rolling back is\n * the same single switch.\n */\nexport function configuredAskUserEnabled(): boolean {\n return process.env.NEGOTIATION_ASK_USER_ENABLED === \"true\";\n}\n\n/** Default answer window for a paused `ask_user` negotiation: 24 hours. */\nexport const DEFAULT_ASK_USER_WINDOW_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Answer window for a paused `ask_user` negotiation, in ms. Overridable via\n * `NEGOTIATION_ASK_USER_WINDOW_MS` (dev/e2e use shorter windows to exercise\n * the expiry path); invalid or non-positive values fall back to 24 h.\n */\nexport function askUserAnswerWindowMs(): number {\n const raw = process.env.NEGOTIATION_ASK_USER_WINDOW_MS;\n if (raw) {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) return parsed;\n }\n return DEFAULT_ASK_USER_WINDOW_MS;\n}\n\n/**\n * Slack added on top of the answer window when deciding whether a paused\n * (`input_required`) task still holds the conversation lock. Covers expiry\n * worker delay: the lock must outlive the timer slightly, so ambient\n * rediscovery cannot slip in between window expiry and the worker's resume.\n */\nexport const ASK_USER_LOCK_SLACK_MS = 60 * 60 * 1000;\n\n/**\n * Resolve the seat of `userId` on a negotiation task.\n *\n * Keys on `metadata.initiatorUserId` (the rigid v2 stamp), **never on turn\n * parity** — continuations can start with either side speaking first, so\n * parity misattributes seats across sessions. Pre-stamp tasks fall back to\n * `sourceUserId` (the discovery-session opener), which is what the stamp\n * defaults to anyway.\n */\nexport function resolveSeat(\n userId: string,\n metadata: { initiatorUserId?: unknown; sourceUserId?: unknown } | null | undefined,\n): NegotiationSeat {\n const initiator =\n typeof metadata?.initiatorUserId === \"string\" && metadata.initiatorUserId.length > 0\n ? metadata.initiatorUserId\n : typeof metadata?.sourceUserId === \"string\"\n ? metadata.sourceUserId\n : undefined;\n return initiator === userId ? \"initiator\" : \"counterparty\";\n}\n\n/** Human-readable seat-violation message shared by respond surfaces. */\nexport function seatViolationMessage(\n action: string,\n seat: NegotiationSeat,\n version: NegotiationProtocolVersion,\n): string {\n const allowed = allowedActionsFor(version, seat).join(\", \");\n return `Action \"${action}\" is not allowed for your seat (${seat}) under negotiation protocol ${version}. Allowed actions: ${allowed}.`;\n}\n"]}
1
+ {"version":3,"file":"negotiation.protocol.js","sourceRoot":"/","sources":["negotiation/negotiation.protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Seat-scoped negotiation protocol rules (v2 client-advocate protocol).\n *\n * v2 fixes exactly one initiating seat per match (`metadata.initiatorUserId`,\n * stamped at discovery time — IND-396) and makes consent asymmetric:\n * **accept can only come from the counterparty seat**, schema-enforced.\n *\n * Vocabulary per seat (v2):\n * - initiator: `outreach | counter | question | withdraw` (no accept)\n * - counterparty: `accept | decline | counter | question`\n * - final turn: initiator `withdraw | counter`; counterparty `accept | decline`\n *\n * v1 tasks keep the legacy vocabulary (`propose | accept | reject | counter |\n * question`) — the version is inherited per conversation, never re-stamped, so\n * in-flight v1 negotiations are grandfathered untouched.\n *\n * Outcome mapping is version-independent: `accept` → opportunity `pending`,\n * `reject`/`withdraw`/`decline` → `rejected`, turn-cap → `stalled`.\n */\nimport { z } from \"zod\";\n\nimport { AskUserPayloadSchema } from \"../shared/schemas/negotiation-state.schema.js\";\nimport type { NegotiationAction, NegotiationSeat, NegotiationProtocolVersion } from \"../shared/schemas/negotiation-state.schema.js\";\n\n// ─── Shared assessment fragment ──────────────────────────────────────────────\n\nconst AssessmentSchema = z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n});\n\nfunction turnSchema<T extends [NegotiationAction, ...NegotiationAction[]]>(actions: T) {\n return z.object({\n action: z.enum(actions),\n assessment: AssessmentSchema,\n message: z.string().nullable().optional(),\n /** Present when action is `ask_user` (v2, P3.2). */\n askUser: AskUserPayloadSchema.nullable().optional(),\n });\n}\n\n// ─── v2 seat-scoped turn schemas ─────────────────────────────────────────────\n\n/** Initiator seat, non-final turn: may reach out, push back, ask, or walk away — never accept. */\nexport const InitiatorTurnSchema = turnSchema([\"outreach\", \"counter\", \"question\", \"withdraw\"]);\n\n/** Counterparty seat, non-final turn: the only seat that can accept. */\nexport const CounterpartyTurnSchema = turnSchema([\"accept\", \"decline\", \"counter\", \"question\"]);\n\n/** Initiator seat, final allowed turn: commit to walking away or leave the door open. */\nexport const FinalInitiatorTurnSchema = turnSchema([\"withdraw\", \"counter\"]);\n\n/** Counterparty seat, final allowed turn: must decide. */\nexport const FinalCounterpartyTurnSchema = turnSchema([\"accept\", \"decline\"]);\n\n// ─── v2 ask_user variants (P3.2, flag-gated) ────────────────────────────────\n// Non-final turns only: the final-cap turn must decide, never pause. Selected\n// via the `opts.askUser` parameter on allowedActionsFor/turnSchemaFor — the\n// base schemas above stay byte-identical for every existing caller.\n\n/** Initiator seat, non-final turn, with the client-consult pause available. */\nexport const InitiatorAskUserTurnSchema = turnSchema([\"outreach\", \"counter\", \"question\", \"withdraw\", \"ask_user\"]);\n\n/** Counterparty seat, non-final turn, with the client-consult pause available. */\nexport const CounterpartyAskUserTurnSchema = turnSchema([\"accept\", \"decline\", \"counter\", \"question\", \"ask_user\"]);\n\n// ─── Action vocabulary per version + seat ────────────────────────────────────\n\nconst V1_ACTIONS: readonly NegotiationAction[] = [\"propose\", \"accept\", \"reject\", \"counter\", \"question\"];\nconst V1_FINAL_ACTIONS: readonly NegotiationAction[] = [\"accept\", \"reject\"];\nconst V2_INITIATOR_ACTIONS: readonly NegotiationAction[] = [\"outreach\", \"counter\", \"question\", \"withdraw\"];\nconst V2_COUNTERPARTY_ACTIONS: readonly NegotiationAction[] = [\"accept\", \"decline\", \"counter\", \"question\"];\nconst V2_FINAL_INITIATOR_ACTIONS: readonly NegotiationAction[] = [\"withdraw\", \"counter\"];\nconst V2_FINAL_COUNTERPARTY_ACTIONS: readonly NegotiationAction[] = [\"accept\", \"decline\"];\nconst V2_INITIATOR_ASK_USER_ACTIONS: readonly NegotiationAction[] = [...V2_INITIATOR_ACTIONS, \"ask_user\"];\nconst V2_COUNTERPARTY_ASK_USER_ACTIONS: readonly NegotiationAction[] = [...V2_COUNTERPARTY_ACTIONS, \"ask_user\"];\n\n/**\n * The set of actions a given seat may submit under a given protocol version.\n *\n * v1 ignores the seat entirely (legacy symmetric vocabulary) so pre-v2\n * negotiations behave exactly as before.\n */\nexport function allowedActionsFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n isFinalTurn = false,\n opts?: AskUserOpts,\n): readonly NegotiationAction[] {\n if (version !== \"v2\") return isFinalTurn ? V1_FINAL_ACTIONS : V1_ACTIONS;\n const askUser = opts?.askUser === true && !isFinalTurn;\n if (seat === \"initiator\") {\n return isFinalTurn ? V2_FINAL_INITIATOR_ACTIONS : (askUser ? V2_INITIATOR_ASK_USER_ACTIONS : V2_INITIATOR_ACTIONS);\n }\n return isFinalTurn ? V2_FINAL_COUNTERPARTY_ACTIONS : (askUser ? V2_COUNTERPARTY_ASK_USER_ACTIONS : V2_COUNTERPARTY_ACTIONS);\n}\n\n/**\n * Zod turn schema for a system-agent turn, selected by version + seat +\n * final-turn flag. v1 returns the legacy schemas (seat-agnostic); v2 returns\n * the seat-scoped schemas above, making an initiator `accept` structurally\n * impossible rather than prompt-discouraged.\n *\n * The v1 legacy schemas are passed in by the caller (they live in\n * `negotiation.state.ts`) to keep this module free of a state-module import.\n */\nexport function turnSchemaFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n isFinalTurn: boolean,\n v1Schemas: { system: z.ZodTypeAny; final: z.ZodTypeAny },\n opts?: AskUserOpts,\n): z.ZodTypeAny {\n if (version !== \"v2\") return isFinalTurn ? v1Schemas.final : v1Schemas.system;\n const askUser = opts?.askUser === true && !isFinalTurn;\n if (seat === \"initiator\") {\n return isFinalTurn ? FinalInitiatorTurnSchema : (askUser ? InitiatorAskUserTurnSchema : InitiatorTurnSchema);\n }\n return isFinalTurn ? FinalCounterpartyTurnSchema : (askUser ? CounterpartyAskUserTurnSchema : CounterpartyTurnSchema);\n}\n\n/**\n * Opt-in extension of the seat vocabulary with the `ask_user` client-consult\n * pause (P3.2). Never granted on final-cap turns (the final turn must decide)\n * and never under v1. Callers pass `{ askUser: true }` only when the full\n * pause loop is available on their surface: the ask-user feature flag is on,\n * a questioner enqueue and an answer-window timer are wired, the negotiation\n * has an opportunity to resume against, and the acting side has not already\n * consumed its one client question for this negotiation (rationing).\n */\nexport interface AskUserOpts {\n askUser?: boolean;\n}\n\n// ─── Action semantics (version-independent) ──────────────────────────────────\n\n/** Terminal actions end the negotiation immediately. */\nexport function isTerminalAction(action: string | undefined | null): boolean {\n return action === \"accept\" || action === \"reject\" || action === \"withdraw\" || action === \"decline\";\n}\n\n/** Reject-like actions map the opportunity to `rejected` (v1 reject, v2 withdraw/decline). */\nexport function isRejectLikeAction(action: string | undefined | null): boolean {\n return action === \"reject\" || action === \"withdraw\" || action === \"decline\";\n}\n\n/**\n * Conservative action when an agent produced schema-invalid output (after the\n * retry) or an internal error needs a seat-valid terminal placeholder.\n *\n * Non-final turns fall back to `counter` (keeps the dialogue open — the AC's\n * \"conservative counter\"). Final turns must decide: v1 → `reject`, v2\n * counterparty → `decline`, v2 initiator → `counter` is still legal on the\n * final turn so it stays `counter` (finalizes as turn-cap/stalled).\n */\nexport function fallbackActionFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n isFinalTurn: boolean,\n): NegotiationAction {\n if (!isFinalTurn) return \"counter\";\n if (version !== \"v2\") return \"reject\";\n return seat === \"counterparty\" ? \"decline\" : \"counter\";\n}\n\n/** Seat-appropriate reject-like action for error paths. */\nexport function rejectActionFor(\n version: NegotiationProtocolVersion,\n seat: NegotiationSeat,\n): NegotiationAction {\n if (version !== \"v2\") return \"reject\";\n return seat === \"initiator\" ? \"withdraw\" : \"decline\";\n}\n\n// ─── Metadata readers ────────────────────────────────────────────────────────\n\n/**\n * Read the protocol version off task metadata. Returns null when the task\n * predates version stamping (treat as v1 at the call site when the task is a\n * genuine prior; fresh tasks stamp from {@link configuredProtocolVersion}).\n */\nexport function readProtocolVersion(\n metadata: { protocolVersion?: unknown } | null | undefined,\n): NegotiationProtocolVersion | null {\n const v = metadata?.protocolVersion;\n return v === \"v2\" ? \"v2\" : v === \"v1\" ? \"v1\" : null;\n}\n\n/**\n * Protocol version for negotiations without a prior task for the same\n * opportunity, from the `NEGOTIATION_PROTOCOL_VERSION` env switch. Defaults\n * to `v1` when unset — v2 is opt-in per environment, and rolling back is the\n * same single switch (only in-flight negotiations stay pinned to their\n * stamped version).\n */\nexport function configuredProtocolVersion(): NegotiationProtocolVersion {\n return process.env.NEGOTIATION_PROTOCOL_VERSION === \"v2\" ? \"v2\" : \"v1\";\n}\n\n/**\n * Whether the `ask_user` client-consult pause (P3.2) is enabled, from the\n * `NEGOTIATION_ASK_USER_ENABLED` env switch. Defaults to off — the deployment\n * is byte-for-byte unchanged until the flag is flipped, and rolling back is\n * the same single switch.\n */\nexport function configuredAskUserEnabled(): boolean {\n return process.env.NEGOTIATION_ASK_USER_ENABLED === \"true\";\n}\n\n/** Default answer window for a paused `ask_user` negotiation: 24 hours. */\nexport const DEFAULT_ASK_USER_WINDOW_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Answer window for a paused `ask_user` negotiation, in ms. Overridable via\n * `NEGOTIATION_ASK_USER_WINDOW_MS` (dev/e2e use shorter windows to exercise\n * the expiry path); invalid or non-positive values fall back to 24 h.\n */\nexport function askUserAnswerWindowMs(): number {\n const raw = process.env.NEGOTIATION_ASK_USER_WINDOW_MS;\n if (raw) {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) return parsed;\n }\n return DEFAULT_ASK_USER_WINDOW_MS;\n}\n\n/**\n * Slack added on top of the answer window when deciding whether a paused\n * (`input_required`) task still holds the conversation lock. Covers expiry\n * worker delay: the lock must outlive the timer slightly, so ambient\n * rediscovery cannot slip in between window expiry and the worker's resume.\n */\nexport const ASK_USER_LOCK_SLACK_MS = 60 * 60 * 1000;\n\n/**\n * Resolve the seat of `userId` on a negotiation task.\n *\n * Keys on `metadata.initiatorUserId` (the rigid v2 stamp), **never on turn\n * parity** — continuations can start with either side speaking first, so\n * parity misattributes seats across sessions. Pre-stamp tasks fall back to\n * `sourceUserId` (the discovery-session opener), which is what the stamp\n * defaults to anyway.\n */\nexport function resolveSeat(\n userId: string,\n metadata: { initiatorUserId?: unknown; sourceUserId?: unknown } | null | undefined,\n): NegotiationSeat {\n const initiator =\n typeof metadata?.initiatorUserId === \"string\" && metadata.initiatorUserId.length > 0\n ? metadata.initiatorUserId\n : typeof metadata?.sourceUserId === \"string\"\n ? metadata.sourceUserId\n : undefined;\n return initiator === userId ? \"initiator\" : \"counterparty\";\n}\n\n/** Human-readable seat-violation message shared by respond surfaces. */\nexport function seatViolationMessage(\n action: string,\n seat: NegotiationSeat,\n version: NegotiationProtocolVersion,\n): string {\n const allowed = allowedActionsFor(version, seat).join(\", \");\n return `Action \"${action}\" is not allowed for your seat (${seat}) under negotiation protocol ${version}. Allowed actions: ${allowed}.`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"opportunity.persist.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.persist.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,wCAAwC,EAAE,WAAW,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC3N,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAOlE,MAAM,MAAM,0BAA0B,GAAG,gBAAgB,GAAG;IAC1D,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrE,kCAAkC,CAAC,CACjC,IAAI,EAAE,qBAAqB,EAC3B,WAAW,EAAE,6BAA6B,GACzC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC/B,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,GAAG,WAAW,GAAG,IAAI,CAAC,CAAC;IACnG,6BAA6B,CAAC,CAC5B,IAAI,EAAE,qBAAqB,EAC3B,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,CAAC,CAAC;IAC7D,8CAA8C,CAAC,CAC7C,IAAI,EAAE,qBAAqB,EAC3B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,6BAA6B,GACzC,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACpE,+CAA+C,CAAC,CAC9C,IAAI,EAAE,qBAAqB,EAC3B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,6BAA6B,GAAG;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE,EACxE,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,wCAAwC,GAAG,IAAI,CAAC,CAAC;IAC5D,kEAAkE;IAClE,cAAc,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC1D,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,qBAAqB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD,gFAAgF;IAChF,gBAAgB,CAAC,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACvE;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,4BAA6B,SAAQ,wBAAwB;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,SAAS,EAAE,4BAA4B,EAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,yBAAyB,EAAE,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,0BAA0B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4GlH"}
1
+ {"version":3,"file":"opportunity.persist.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.persist.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,wCAAwC,EAAE,WAAW,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC3N,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAuClE,MAAM,MAAM,0BAA0B,GAAG,gBAAgB,GAAG;IAC1D,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrE,kCAAkC,CAAC,CACjC,IAAI,EAAE,qBAAqB,EAC3B,WAAW,EAAE,6BAA6B,GACzC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC/B,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,GAAG,WAAW,GAAG,IAAI,CAAC,CAAC;IACnG,6BAA6B,CAAC,CAC5B,IAAI,EAAE,qBAAqB,EAC3B,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,CAAC,CAAC;IAC7D,8CAA8C,CAAC,CAC7C,IAAI,EAAE,qBAAqB,EAC3B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,6BAA6B,GACzC,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACpE,+CAA+C,CAAC,CAC9C,IAAI,EAAE,qBAAqB,EAC3B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,6BAA6B,GAAG;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE,EACxE,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,wCAAwC,GAAG,IAAI,CAAC,CAAC;IAC5D,kEAAkE;IAClE,cAAc,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC1D,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,qBAAqB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD,gFAAgF;IAChF,gBAAgB,CAAC,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACvE;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,4BAA6B,SAAQ,wBAAwB;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,SAAS,EAAE,4BAA4B,EAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,yBAAyB,EAAE,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,0BAA0B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA0HlH"}
@@ -6,6 +6,31 @@ import { enrichOrCreate } from './opportunity.enricher.js';
6
6
  import { normalizeCreateOpportunityActorIntents } from './opportunity.actor.js';
7
7
  import { protocolLogger } from '../shared/observability/protocol.logger.js';
8
8
  const logger = protocolLogger('OpportunityPersist');
9
+ /** Bind owned-intent discovery actors to the already-authorized trigger intent. */
10
+ function bindOwnedIntentActors(data, ownerUserId, triggerIntentId) {
11
+ const actors = [];
12
+ const boundActorIndexes = new Map();
13
+ for (const actor of data.actors) {
14
+ if (actor.userId !== ownerUserId || actor.role === 'introducer') {
15
+ actors.push(actor);
16
+ continue;
17
+ }
18
+ const boundActor = {
19
+ ...actor,
20
+ intent: triggerIntentId,
21
+ };
22
+ const key = JSON.stringify([boundActor.networkId, boundActor.userId, triggerIntentId]);
23
+ const existingIndex = boundActorIndexes.get(key);
24
+ if (existingIndex === undefined) {
25
+ boundActorIndexes.set(key, actors.length);
26
+ actors.push(boundActor);
27
+ }
28
+ else {
29
+ actors[existingIndex] = boundActor;
30
+ }
31
+ }
32
+ return { ...data, actors };
33
+ }
9
34
  /**
10
35
  * Persist one or more opportunities: enrich (merge overlapping), create, expire replaced, optional chat injection.
11
36
  * When the database has createOpportunityAndExpireIds and enrichment produced expireIds, uses it for atomic create+expire.
@@ -25,7 +50,10 @@ export async function persistOpportunities(params) {
25
50
  && intentDedupScope.triggerIntentId !== networkEligibility?.triggerIntentId) {
26
51
  throw new Error('Intent-scoped dedup trigger must match network eligibility');
27
52
  }
28
- const enrichment = await enrichOrCreate(database, embedder, normalizedData, intentDedupScope && networkEligibility
53
+ const scopedData = intentDedupScope && networkEligibility
54
+ ? bindOwnedIntentActors(normalizedData, networkEligibility.ownerUserId, intentDedupScope.triggerIntentId)
55
+ : normalizedData;
56
+ const enrichment = await enrichOrCreate(database, embedder, scopedData, intentDedupScope && networkEligibility
29
57
  ? {
30
58
  ownedIntentScope: {
31
59
  triggerIntentId: intentDedupScope.triggerIntentId,
@@ -33,7 +61,10 @@ export async function persistOpportunities(params) {
33
61
  },
34
62
  }
35
63
  : undefined);
36
- const toCreate = normalizeCreateOpportunityActorIntents(enrichment.data);
64
+ const normalizedToCreate = normalizeCreateOpportunityActorIntents(enrichment.data);
65
+ const toCreate = intentDedupScope && networkEligibility
66
+ ? bindOwnedIntentActors(normalizedToCreate, networkEligibility.ownerUserId, intentDedupScope.triggerIntentId)
67
+ : normalizedToCreate;
37
68
  if (enrichment.enriched) {
38
69
  toCreate.status = enrichment.resolvedStatus;
39
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"opportunity.persist.js","sourceRoot":"/","sources":["opportunity/opportunity.persist.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared persist phase for opportunity creation: enrichOrCreate → create (or create+expire) → optional chat injection.\n * Used by the opportunity graph persist node and by the manual opportunity service for consistency.\n */\n\nimport type { CreateOpportunityData, IntentScopedOpportunityPersistenceResult, Opportunity, OpportunityDedupConflict, OpportunityNetworkEligibility, OpportunityStatus } from '../shared/interfaces/database.interface.js';\nimport type { Embedder } from '../shared/interfaces/embedder.interface.js';\nimport type { EnricherDatabase } from './opportunity.enricher.js';\nimport { enrichOrCreate } from './opportunity.enricher.js';\nimport { normalizeCreateOpportunityActorIntents } from './opportunity.actor.js';\nimport { protocolLogger } from '../shared/observability/protocol.logger.js';\n\nconst logger = protocolLogger('OpportunityPersist');\n\nexport type PersistOpportunityDatabase = EnricherDatabase & {\n createOpportunity(data: CreateOpportunityData): Promise<Opportunity>;\n createOpportunityIfNetworkEligible?(\n data: CreateOpportunityData,\n eligibility: OpportunityNetworkEligibility,\n ): Promise<Opportunity | null>;\n updateOpportunityStatus(id: string, status: OpportunityStatus): Promise<void | Opportunity | null>;\n createOpportunityAndExpireIds?(\n data: CreateOpportunityData,\n expireIds: string[]\n ): Promise<{ created: Opportunity; expired: Opportunity[] }>;\n createOpportunityAndExpireIdsIfNetworkEligible?(\n data: CreateOpportunityData,\n expireIds: string[],\n eligibility: OpportunityNetworkEligibility,\n ): Promise<{ created: Opportunity; expired: Opportunity[] } | null>;\n persistIntentScopedOpportunityIfNetworkEligible?(\n data: CreateOpportunityData,\n expireIds: string[],\n eligibility: OpportunityNetworkEligibility & { triggerIntentId: string },\n dedupWindowMs: number,\n ): Promise<IntentScopedOpportunityPersistenceResult | null>;\n /** Optional: used to populate expired list in non-atomic path. */\n getOpportunity?(id: string): Promise<Opportunity | null>;\n};\n\nexport interface PersistOpportunitiesParams {\n database: PersistOpportunityDatabase;\n embedder: Embedder;\n items: CreateOpportunityData[];\n injectChat?: (opportunity: Opportunity) => Promise<unknown>;\n /** Require adapter-level membership/assignment locks for discovery-created rows. */\n networkEligibility?: OpportunityNetworkEligibility;\n /** Scope dedup/enrichment and the final atomic re-check to one owned intent. */\n intentDedupScope?: { triggerIntentId: string; dedupWindowMs: number };\n}\n\nexport interface PersistOpportunitiesError {\n itemIndex: number;\n error: unknown;\n}\n\nexport interface PersistOpportunitiesConflict extends OpportunityDedupConflict {\n itemIndex: number;\n finalAtomic: true;\n}\n\nexport interface PersistOpportunitiesResult {\n created: Opportunity[];\n expired: Opportunity[];\n conflicts: PersistOpportunitiesConflict[];\n errors?: PersistOpportunitiesError[];\n}\n\n/**\n * Persist one or more opportunities: enrich (merge overlapping), create, expire replaced, optional chat injection.\n * When the database has createOpportunityAndExpireIds and enrichment produced expireIds, uses it for atomic create+expire.\n * Returns both created and expired so callers can emit events (e.g. manual service).\n */\nexport async function persistOpportunities(params: PersistOpportunitiesParams): Promise<PersistOpportunitiesResult> {\n const { database, embedder, items, injectChat, networkEligibility, intentDedupScope } = params;\n const created: Opportunity[] = [];\n const expired: Opportunity[] = [];\n const conflicts: PersistOpportunitiesConflict[] = [];\n const errors: PersistOpportunitiesError[] = [];\n\n for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {\n const data = items[itemIndex];\n try {\n const normalizedData = normalizeCreateOpportunityActorIntents(data);\n if (\n intentDedupScope\n && intentDedupScope.triggerIntentId !== networkEligibility?.triggerIntentId\n ) {\n throw new Error('Intent-scoped dedup trigger must match network eligibility');\n }\n const enrichment = await enrichOrCreate(database, embedder, normalizedData, intentDedupScope && networkEligibility\n ? {\n ownedIntentScope: {\n triggerIntentId: intentDedupScope.triggerIntentId,\n ownerUserId: networkEligibility.ownerUserId,\n },\n }\n : undefined);\n const toCreate = normalizeCreateOpportunityActorIntents(enrichment.data);\n if (enrichment.enriched) {\n toCreate.status = enrichment.resolvedStatus;\n }\n\n if (intentDedupScope) {\n if (!networkEligibility?.triggerIntentId) {\n throw new Error('Intent-scoped dedup requires trigger-intent network eligibility');\n }\n if (!database.persistIntentScopedOpportunityIfNetworkEligible) {\n throw new Error('Intent-scoped atomic persistence is required for owned-intent discovery');\n }\n const result = await database.persistIntentScopedOpportunityIfNetworkEligible(\n toCreate,\n enrichment.enriched ? enrichment.expiredIds : [],\n { ...networkEligibility, triggerIntentId: networkEligibility.triggerIntentId },\n intentDedupScope.dedupWindowMs,\n );\n if (!result) continue;\n if ('conflict' in result) {\n conflicts.push({ itemIndex, finalAtomic: true, ...result.conflict });\n continue;\n }\n created.push(result.created);\n expired.push(...result.expired);\n } else if (networkEligibility) {\n if (enrichment.enriched && enrichment.expiredIds.length > 0) {\n if (!database.createOpportunityAndExpireIdsIfNetworkEligible) {\n throw new Error('Network-eligible create+expire is required for discovery persistence');\n }\n const result = await database.createOpportunityAndExpireIdsIfNetworkEligible(\n toCreate,\n enrichment.expiredIds,\n networkEligibility,\n );\n if (!result) continue;\n created.push(result.created);\n expired.push(...result.expired);\n } else {\n if (!database.createOpportunityIfNetworkEligible) {\n throw new Error('Network-eligible create is required for discovery persistence');\n }\n const c = await database.createOpportunityIfNetworkEligible(toCreate, networkEligibility);\n if (!c) continue;\n created.push(c);\n }\n } else if (\n database.createOpportunityAndExpireIds &&\n enrichment.enriched &&\n enrichment.expiredIds.length > 0\n ) {\n const result = await database.createOpportunityAndExpireIds(toCreate, enrichment.expiredIds);\n created.push(result.created);\n expired.push(...result.expired);\n } else {\n const c = await database.createOpportunity(toCreate);\n created.push(c);\n if (enrichment.enriched && enrichment.expiredIds.length > 0) {\n for (const id of enrichment.expiredIds) {\n await database.updateOpportunityStatus(id, 'expired');\n }\n if (database.getOpportunity) {\n for (const id of enrichment.expiredIds) {\n const opp = await database.getOpportunity(id);\n if (opp) expired.push(opp);\n }\n }\n }\n }\n\n const lastCreated = created[created.length - 1];\n if (lastCreated?.status === 'pending' && injectChat) {\n await injectChat(lastCreated).catch((err) => {\n logger.warn('Chat injection failed', { opportunityId: lastCreated.id, error: err });\n });\n }\n } catch (err) {\n errors.push({ itemIndex, error: err });\n logger.warn('Item failed', { itemIndex, error: err });\n }\n }\n\n return { created, expired, conflicts, ...(errors.length > 0 ? { errors } : {}) };\n}\n"]}
1
+ {"version":3,"file":"opportunity.persist.js","sourceRoot":"/","sources":["opportunity/opportunity.persist.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared persist phase for opportunity creation: enrichOrCreate → create (or create+expire) → optional chat injection.\n * Used by the opportunity graph persist node and by the manual opportunity service for consistency.\n */\n\nimport type { CreateOpportunityData, IntentScopedOpportunityPersistenceResult, Opportunity, OpportunityDedupConflict, OpportunityNetworkEligibility, OpportunityStatus } from '../shared/interfaces/database.interface.js';\nimport type { Embedder } from '../shared/interfaces/embedder.interface.js';\nimport type { EnricherDatabase } from './opportunity.enricher.js';\nimport { enrichOrCreate } from './opportunity.enricher.js';\nimport { normalizeCreateOpportunityActorIntents } from './opportunity.actor.js';\nimport { protocolLogger } from '../shared/observability/protocol.logger.js';\n\nconst logger = protocolLogger('OpportunityPersist');\n\n/** Bind owned-intent discovery actors to the already-authorized trigger intent. */\nfunction bindOwnedIntentActors(\n data: CreateOpportunityData,\n ownerUserId: string,\n triggerIntentId: string,\n): CreateOpportunityData {\n const actors: CreateOpportunityData['actors'] = [];\n const boundActorIndexes = new Map<string, number>();\n\n for (const actor of data.actors) {\n if (actor.userId !== ownerUserId || actor.role === 'introducer') {\n actors.push(actor);\n continue;\n }\n\n const boundActor = {\n ...actor,\n intent: triggerIntentId as NonNullable<typeof actor.intent>,\n };\n const key = JSON.stringify([boundActor.networkId, boundActor.userId, triggerIntentId]);\n const existingIndex = boundActorIndexes.get(key);\n if (existingIndex === undefined) {\n boundActorIndexes.set(key, actors.length);\n actors.push(boundActor);\n } else {\n actors[existingIndex] = boundActor;\n }\n }\n\n return { ...data, actors };\n}\n\nexport type PersistOpportunityDatabase = EnricherDatabase & {\n createOpportunity(data: CreateOpportunityData): Promise<Opportunity>;\n createOpportunityIfNetworkEligible?(\n data: CreateOpportunityData,\n eligibility: OpportunityNetworkEligibility,\n ): Promise<Opportunity | null>;\n updateOpportunityStatus(id: string, status: OpportunityStatus): Promise<void | Opportunity | null>;\n createOpportunityAndExpireIds?(\n data: CreateOpportunityData,\n expireIds: string[]\n ): Promise<{ created: Opportunity; expired: Opportunity[] }>;\n createOpportunityAndExpireIdsIfNetworkEligible?(\n data: CreateOpportunityData,\n expireIds: string[],\n eligibility: OpportunityNetworkEligibility,\n ): Promise<{ created: Opportunity; expired: Opportunity[] } | null>;\n persistIntentScopedOpportunityIfNetworkEligible?(\n data: CreateOpportunityData,\n expireIds: string[],\n eligibility: OpportunityNetworkEligibility & { triggerIntentId: string },\n dedupWindowMs: number,\n ): Promise<IntentScopedOpportunityPersistenceResult | null>;\n /** Optional: used to populate expired list in non-atomic path. */\n getOpportunity?(id: string): Promise<Opportunity | null>;\n};\n\nexport interface PersistOpportunitiesParams {\n database: PersistOpportunityDatabase;\n embedder: Embedder;\n items: CreateOpportunityData[];\n injectChat?: (opportunity: Opportunity) => Promise<unknown>;\n /** Require adapter-level membership/assignment locks for discovery-created rows. */\n networkEligibility?: OpportunityNetworkEligibility;\n /** Scope dedup/enrichment and the final atomic re-check to one owned intent. */\n intentDedupScope?: { triggerIntentId: string; dedupWindowMs: number };\n}\n\nexport interface PersistOpportunitiesError {\n itemIndex: number;\n error: unknown;\n}\n\nexport interface PersistOpportunitiesConflict extends OpportunityDedupConflict {\n itemIndex: number;\n finalAtomic: true;\n}\n\nexport interface PersistOpportunitiesResult {\n created: Opportunity[];\n expired: Opportunity[];\n conflicts: PersistOpportunitiesConflict[];\n errors?: PersistOpportunitiesError[];\n}\n\n/**\n * Persist one or more opportunities: enrich (merge overlapping), create, expire replaced, optional chat injection.\n * When the database has createOpportunityAndExpireIds and enrichment produced expireIds, uses it for atomic create+expire.\n * Returns both created and expired so callers can emit events (e.g. manual service).\n */\nexport async function persistOpportunities(params: PersistOpportunitiesParams): Promise<PersistOpportunitiesResult> {\n const { database, embedder, items, injectChat, networkEligibility, intentDedupScope } = params;\n const created: Opportunity[] = [];\n const expired: Opportunity[] = [];\n const conflicts: PersistOpportunitiesConflict[] = [];\n const errors: PersistOpportunitiesError[] = [];\n\n for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {\n const data = items[itemIndex];\n try {\n const normalizedData = normalizeCreateOpportunityActorIntents(data);\n if (\n intentDedupScope\n && intentDedupScope.triggerIntentId !== networkEligibility?.triggerIntentId\n ) {\n throw new Error('Intent-scoped dedup trigger must match network eligibility');\n }\n const scopedData = intentDedupScope && networkEligibility\n ? bindOwnedIntentActors(\n normalizedData,\n networkEligibility.ownerUserId,\n intentDedupScope.triggerIntentId,\n )\n : normalizedData;\n const enrichment = await enrichOrCreate(database, embedder, scopedData, intentDedupScope && networkEligibility\n ? {\n ownedIntentScope: {\n triggerIntentId: intentDedupScope.triggerIntentId,\n ownerUserId: networkEligibility.ownerUserId,\n },\n }\n : undefined);\n const normalizedToCreate = normalizeCreateOpportunityActorIntents(enrichment.data);\n const toCreate = intentDedupScope && networkEligibility\n ? bindOwnedIntentActors(\n normalizedToCreate,\n networkEligibility.ownerUserId,\n intentDedupScope.triggerIntentId,\n )\n : normalizedToCreate;\n if (enrichment.enriched) {\n toCreate.status = enrichment.resolvedStatus;\n }\n\n if (intentDedupScope) {\n if (!networkEligibility?.triggerIntentId) {\n throw new Error('Intent-scoped dedup requires trigger-intent network eligibility');\n }\n if (!database.persistIntentScopedOpportunityIfNetworkEligible) {\n throw new Error('Intent-scoped atomic persistence is required for owned-intent discovery');\n }\n const result = await database.persistIntentScopedOpportunityIfNetworkEligible(\n toCreate,\n enrichment.enriched ? enrichment.expiredIds : [],\n { ...networkEligibility, triggerIntentId: networkEligibility.triggerIntentId },\n intentDedupScope.dedupWindowMs,\n );\n if (!result) continue;\n if ('conflict' in result) {\n conflicts.push({ itemIndex, finalAtomic: true, ...result.conflict });\n continue;\n }\n created.push(result.created);\n expired.push(...result.expired);\n } else if (networkEligibility) {\n if (enrichment.enriched && enrichment.expiredIds.length > 0) {\n if (!database.createOpportunityAndExpireIdsIfNetworkEligible) {\n throw new Error('Network-eligible create+expire is required for discovery persistence');\n }\n const result = await database.createOpportunityAndExpireIdsIfNetworkEligible(\n toCreate,\n enrichment.expiredIds,\n networkEligibility,\n );\n if (!result) continue;\n created.push(result.created);\n expired.push(...result.expired);\n } else {\n if (!database.createOpportunityIfNetworkEligible) {\n throw new Error('Network-eligible create is required for discovery persistence');\n }\n const c = await database.createOpportunityIfNetworkEligible(toCreate, networkEligibility);\n if (!c) continue;\n created.push(c);\n }\n } else if (\n database.createOpportunityAndExpireIds &&\n enrichment.enriched &&\n enrichment.expiredIds.length > 0\n ) {\n const result = await database.createOpportunityAndExpireIds(toCreate, enrichment.expiredIds);\n created.push(result.created);\n expired.push(...result.expired);\n } else {\n const c = await database.createOpportunity(toCreate);\n created.push(c);\n if (enrichment.enriched && enrichment.expiredIds.length > 0) {\n for (const id of enrichment.expiredIds) {\n await database.updateOpportunityStatus(id, 'expired');\n }\n if (database.getOpportunity) {\n for (const id of enrichment.expiredIds) {\n const opp = await database.getOpportunity(id);\n if (opp) expired.push(opp);\n }\n }\n }\n }\n\n const lastCreated = created[created.length - 1];\n if (lastCreated?.status === 'pending' && injectChat) {\n await injectChat(lastCreated).catch((err) => {\n logger.warn('Chat injection failed', { opportunityId: lastCreated.id, error: err });\n });\n }\n } catch (err) {\n errors.push({ itemIndex, error: err });\n logger.warn('Item failed', { itemIndex, error: err });\n }\n }\n\n return { created, expired, conflicts, ...(errors.length > 0 ? { errors } : {}) };\n}\n"]}
@@ -80,7 +80,7 @@ function buildIntentPrompt(ctx) {
80
80
  "Return an empty `questions` array if the intent is already specific enough.",
81
81
  ].join("\n");
82
82
  }
83
- const 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.
83
+ const RECOVERY_INTENT_SYSTEM_PROMPT = `You help a user materially refine one of their own discovery signals. Ask directly about the user's goal, desired counterpart attributes, or constraints. Never describe internal processing or imply that any specific person was reviewed.
84
84
 
85
85
  Choose exactly one of these strategies:
86
86
  - refine_intent: sharpen or pivot the core signal in a way that changes who should surface.
@@ -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 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"]}
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. Ask directly about the user's goal, desired counterpart attributes, or constraints. Never describe internal processing 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"]}
@@ -674,7 +674,7 @@ export declare const QuestionPoolPushRequestStatusSchema: z.ZodEnum<["requested"
674
674
  export declare const QuestionRecoverySnapshotSchema: z.ZodObject<{
675
675
  version: z.ZodLiteral<1>;
676
676
  intentFingerprint: z.ZodString;
677
- completionSource: z.ZodEnum<["from_intent", "discovery_run"]>;
677
+ completionSource: z.ZodEnum<["intent_creation", "from_intent", "discovery_run"]>;
678
678
  /** Privacy-safe aggregate only; raw negotiation evidence is never persisted. */
679
679
  rejectedNegotiationCount: z.ZodOptional<z.ZodNumber>;
680
680
  /** Bounded internal correlation id for an asynchronous discovery run. */
@@ -682,13 +682,13 @@ export declare const QuestionRecoverySnapshotSchema: z.ZodObject<{
682
682
  }, "strip", z.ZodTypeAny, {
683
683
  version: 1;
684
684
  intentFingerprint: string;
685
- completionSource: "from_intent" | "discovery_run";
685
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
686
686
  runId?: string | undefined;
687
687
  rejectedNegotiationCount?: number | undefined;
688
688
  }, {
689
689
  version: 1;
690
690
  intentFingerprint: string;
691
- completionSource: "from_intent" | "discovery_run";
691
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
692
692
  runId?: string | undefined;
693
693
  rejectedNegotiationCount?: number | undefined;
694
694
  }>;
@@ -999,7 +999,7 @@ export declare const QuestionDetectionSchema: z.ZodEffects<z.ZodObject<{
999
999
  recovery: z.ZodOptional<z.ZodObject<{
1000
1000
  version: z.ZodLiteral<1>;
1001
1001
  intentFingerprint: z.ZodString;
1002
- completionSource: z.ZodEnum<["from_intent", "discovery_run"]>;
1002
+ completionSource: z.ZodEnum<["intent_creation", "from_intent", "discovery_run"]>;
1003
1003
  /** Privacy-safe aggregate only; raw negotiation evidence is never persisted. */
1004
1004
  rejectedNegotiationCount: z.ZodOptional<z.ZodNumber>;
1005
1005
  /** Bounded internal correlation id for an asynchronous discovery run. */
@@ -1007,13 +1007,13 @@ export declare const QuestionDetectionSchema: z.ZodEffects<z.ZodObject<{
1007
1007
  }, "strip", z.ZodTypeAny, {
1008
1008
  version: 1;
1009
1009
  intentFingerprint: string;
1010
- completionSource: "from_intent" | "discovery_run";
1010
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
1011
1011
  runId?: string | undefined;
1012
1012
  rejectedNegotiationCount?: number | undefined;
1013
1013
  }, {
1014
1014
  version: 1;
1015
1015
  intentFingerprint: string;
1016
- completionSource: "from_intent" | "discovery_run";
1016
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
1017
1017
  runId?: string | undefined;
1018
1018
  rejectedNegotiationCount?: number | undefined;
1019
1019
  }>>;
@@ -1100,7 +1100,7 @@ export declare const QuestionDetectionSchema: z.ZodEffects<z.ZodObject<{
1100
1100
  recovery?: {
1101
1101
  version: 1;
1102
1102
  intentFingerprint: string;
1103
- completionSource: "from_intent" | "discovery_run";
1103
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
1104
1104
  runId?: string | undefined;
1105
1105
  rejectedNegotiationCount?: number | undefined;
1106
1106
  } | undefined;
@@ -1194,7 +1194,7 @@ export declare const QuestionDetectionSchema: z.ZodEffects<z.ZodObject<{
1194
1194
  recovery?: {
1195
1195
  version: 1;
1196
1196
  intentFingerprint: string;
1197
- completionSource: "from_intent" | "discovery_run";
1197
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
1198
1198
  runId?: string | undefined;
1199
1199
  rejectedNegotiationCount?: number | undefined;
1200
1200
  } | undefined;
@@ -1288,7 +1288,7 @@ export declare const QuestionDetectionSchema: z.ZodEffects<z.ZodObject<{
1288
1288
  recovery?: {
1289
1289
  version: 1;
1290
1290
  intentFingerprint: string;
1291
- completionSource: "from_intent" | "discovery_run";
1291
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
1292
1292
  runId?: string | undefined;
1293
1293
  rejectedNegotiationCount?: number | undefined;
1294
1294
  } | undefined;
@@ -1382,7 +1382,7 @@ export declare const QuestionDetectionSchema: z.ZodEffects<z.ZodObject<{
1382
1382
  recovery?: {
1383
1383
  version: 1;
1384
1384
  intentFingerprint: string;
1385
- completionSource: "from_intent" | "discovery_run";
1385
+ completionSource: "intent_creation" | "from_intent" | "discovery_run";
1386
1386
  runId?: string | undefined;
1387
1387
  rejectedNegotiationCount?: number | undefined;
1388
1388
  } | undefined;
@@ -235,7 +235,7 @@ export const QuestionPoolPushRequestStatusSchema = z.enum(["requested", "suppres
235
235
  export const QuestionRecoverySnapshotSchema = z.object({
236
236
  version: z.literal(1),
237
237
  intentFingerprint: z.string().regex(/^[a-f0-9]{64}$/),
238
- completionSource: z.enum(["from_intent", "discovery_run"]),
238
+ completionSource: z.enum(["intent_creation", "from_intent", "discovery_run"]),
239
239
  /** Privacy-safe aggregate only; raw negotiation evidence is never persisted. */
240
240
  rejectedNegotiationCount: z.number().int().min(1).max(50).optional(),
241
241
  /** Bounded internal correlation id for an asynchronous discovery run. */