@indexnetwork/protocol 4.17.0-rc.360.1 → 4.18.0-rc.362.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/index.d.ts +13 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +8 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
  7. package/dist/negotiation/negotiation.graph.js +25 -0
  8. package/dist/negotiation/negotiation.graph.js.map +1 -1
  9. package/dist/opportunity/discriminator/discriminator.adjustments.d.ts +10 -1
  10. package/dist/opportunity/discriminator/discriminator.adjustments.d.ts.map +1 -1
  11. package/dist/opportunity/discriminator/discriminator.adjustments.js +12 -4
  12. package/dist/opportunity/discriminator/discriminator.adjustments.js.map +1 -1
  13. package/dist/opportunity/discriminator/discriminator.question.d.ts +2 -0
  14. package/dist/opportunity/discriminator/discriminator.question.d.ts.map +1 -1
  15. package/dist/opportunity/discriminator/discriminator.question.js +1 -0
  16. package/dist/opportunity/discriminator/discriminator.question.js.map +1 -1
  17. package/dist/opportunity/feed/feed.graph.js +2 -2
  18. package/dist/opportunity/feed/feed.graph.js.map +1 -1
  19. package/dist/opportunity/negotiation-evidence/negotiation-evidence.env.d.ts +44 -0
  20. package/dist/opportunity/negotiation-evidence/negotiation-evidence.env.d.ts.map +1 -0
  21. package/dist/opportunity/negotiation-evidence/negotiation-evidence.env.js +45 -0
  22. package/dist/opportunity/negotiation-evidence/negotiation-evidence.env.js.map +1 -0
  23. package/dist/opportunity/negotiation-evidence/negotiation-evidence.extractor.d.ts +25 -0
  24. package/dist/opportunity/negotiation-evidence/negotiation-evidence.extractor.d.ts.map +1 -0
  25. package/dist/opportunity/negotiation-evidence/negotiation-evidence.extractor.js +189 -0
  26. package/dist/opportunity/negotiation-evidence/negotiation-evidence.extractor.js.map +1 -0
  27. package/dist/opportunity/negotiation-evidence/negotiation-evidence.miner.d.ts +27 -0
  28. package/dist/opportunity/negotiation-evidence/negotiation-evidence.miner.d.ts.map +1 -0
  29. package/dist/opportunity/negotiation-evidence/negotiation-evidence.miner.js +113 -0
  30. package/dist/opportunity/negotiation-evidence/negotiation-evidence.miner.js.map +1 -0
  31. package/dist/opportunity/negotiation-evidence/negotiation-evidence.shadow.d.ts +20 -0
  32. package/dist/opportunity/negotiation-evidence/negotiation-evidence.shadow.d.ts.map +1 -0
  33. package/dist/opportunity/negotiation-evidence/negotiation-evidence.shadow.js +62 -0
  34. package/dist/opportunity/negotiation-evidence/negotiation-evidence.shadow.js.map +1 -0
  35. package/dist/opportunity/negotiation-evidence/negotiation-evidence.types.d.ts +192 -0
  36. package/dist/opportunity/negotiation-evidence/negotiation-evidence.types.d.ts.map +1 -0
  37. package/dist/opportunity/negotiation-evidence/negotiation-evidence.types.js +19 -0
  38. package/dist/opportunity/negotiation-evidence/negotiation-evidence.types.js.map +1 -0
  39. package/dist/opportunity/negotiation-evidence/negotiation-evidence.verifier.d.ts +35 -0
  40. package/dist/opportunity/negotiation-evidence/negotiation-evidence.verifier.d.ts.map +1 -0
  41. package/dist/opportunity/negotiation-evidence/negotiation-evidence.verifier.js +92 -0
  42. package/dist/opportunity/negotiation-evidence/negotiation-evidence.verifier.js.map +1 -0
  43. package/dist/opportunity/opportunity.graph.d.ts +9 -1
  44. package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
  45. package/dist/opportunity/opportunity.graph.js +64 -31
  46. package/dist/opportunity/opportunity.graph.js.map +1 -1
  47. package/dist/questioner/questioner.types.d.ts +2 -0
  48. package/dist/questioner/questioner.types.d.ts.map +1 -1
  49. package/dist/questioner/questioner.types.js.map +1 -1
  50. package/dist/shared/agent/model.config.d.ts +5 -0
  51. package/dist/shared/agent/model.config.d.ts.map +1 -1
  52. package/dist/shared/agent/model.config.js +1 -0
  53. package/dist/shared/agent/model.config.js.map +1 -1
  54. package/dist/shared/schemas/question.schema.d.ts +21 -0
  55. package/dist/shared/schemas/question.schema.d.ts.map +1 -1
  56. package/dist/shared/schemas/question.schema.js +6 -0
  57. package/dist/shared/schemas/question.schema.js.map +1 -1
  58. package/package.json +1 -1
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Centralized accessor + constants for the Lens C negotiation-evidence
3
+ * question producer (IND-433).
4
+ *
5
+ * NEGOTIATION_EVIDENCE_QUESTIONS_MODE off | shadow | on — default off.
6
+ * - off (default): the lens never runs; zero reads, zero telemetry.
7
+ * - shadow (this issue): mine + verify neutral hypotheses over allowlisted
8
+ * negotiation evidence, emit AGGREGATE telemetry only. Persists no
9
+ * questions and changes no ranking, intent, premise, memory, policy,
10
+ * newborn-stamping, or push behavior.
11
+ * - on (future, IND-437): additionally synthesize/enqueue questions and
12
+ * suppress the older IND-296–299 transcript-question producer.
13
+ *
14
+ * All reads go through this module — do not read the variable via
15
+ * `process.env` elsewhere. The value is read on every call (no caching) so
16
+ * tests and long-lived processes observe changes.
17
+ */
18
+ /** Documented modes for the Lens C negotiation-evidence question producer. */
19
+ export declare const NEGOTIATION_EVIDENCE_QUESTIONS_MODES: readonly ["off", "shadow", "on"];
20
+ /** Mode for the Lens C negotiation-evidence question producer. */
21
+ export type NegotiationEvidenceQuestionsMode = (typeof NEGOTIATION_EVIDENCE_QUESTIONS_MODES)[number];
22
+ /**
23
+ * Current NEGOTIATION_EVIDENCE_QUESTIONS_MODE (default off). Any value other
24
+ * than an exact documented mode (after trimming) coerces to off — an
25
+ * unrecognized flag must never silently enable the lens.
26
+ */
27
+ export declare function negotiationEvidenceQuestionsMode(): NegotiationEvidenceQuestionsMode;
28
+ /**
29
+ * Recurrence floor: a mined hypothesis is only retained when its verified
30
+ * support spans at least this many DISTINCT opportunities (IND-433 `k=5`).
31
+ * Continuation segments of one opportunity are grouped and count once, so this
32
+ * is genuinely a cross-opportunity threshold — same-pair repetition cannot
33
+ * inflate it.
34
+ */
35
+ export declare const NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES = 5;
36
+ /**
37
+ * Max opportunities (already grouped, one per opportunity) whose allowlisted
38
+ * evidence is sent to the miner in one pass. Bounds prompt size the same way
39
+ * the Lens A miner caps its candidate pool.
40
+ */
41
+ export declare const NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES = 24;
42
+ /** Max chars of allowlisted evidence content retained per evidence unit. */
43
+ export declare const NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS = 400;
44
+ //# sourceMappingURL=negotiation-evidence.env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.env.d.ts","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,oCAAoC,kCAAmC,CAAC;AAErF,kEAAkE;AAClE,MAAM,MAAM,gCAAgC,GAAG,CAAC,OAAO,oCAAoC,CAAC,CAAC,MAAM,CAAC,CAAC;AAErG;;;;GAIG;AACH,wBAAgB,gCAAgC,IAAI,gCAAgC,CAGnF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,+CAA+C,IAAI,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,sCAAsC,KAAK,CAAC;AAEzD,4EAA4E;AAC5E,eAAO,MAAM,sCAAsC,MAAM,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Centralized accessor + constants for the Lens C negotiation-evidence
3
+ * question producer (IND-433).
4
+ *
5
+ * NEGOTIATION_EVIDENCE_QUESTIONS_MODE off | shadow | on — default off.
6
+ * - off (default): the lens never runs; zero reads, zero telemetry.
7
+ * - shadow (this issue): mine + verify neutral hypotheses over allowlisted
8
+ * negotiation evidence, emit AGGREGATE telemetry only. Persists no
9
+ * questions and changes no ranking, intent, premise, memory, policy,
10
+ * newborn-stamping, or push behavior.
11
+ * - on (future, IND-437): additionally synthesize/enqueue questions and
12
+ * suppress the older IND-296–299 transcript-question producer.
13
+ *
14
+ * All reads go through this module — do not read the variable via
15
+ * `process.env` elsewhere. The value is read on every call (no caching) so
16
+ * tests and long-lived processes observe changes.
17
+ */
18
+ /** Documented modes for the Lens C negotiation-evidence question producer. */
19
+ export const NEGOTIATION_EVIDENCE_QUESTIONS_MODES = ["off", "shadow", "on"];
20
+ /**
21
+ * Current NEGOTIATION_EVIDENCE_QUESTIONS_MODE (default off). Any value other
22
+ * than an exact documented mode (after trimming) coerces to off — an
23
+ * unrecognized flag must never silently enable the lens.
24
+ */
25
+ export function negotiationEvidenceQuestionsMode() {
26
+ const raw = process.env.NEGOTIATION_EVIDENCE_QUESTIONS_MODE?.trim();
27
+ return raw === "shadow" || raw === "on" ? raw : "off";
28
+ }
29
+ /**
30
+ * Recurrence floor: a mined hypothesis is only retained when its verified
31
+ * support spans at least this many DISTINCT opportunities (IND-433 `k=5`).
32
+ * Continuation segments of one opportunity are grouped and count once, so this
33
+ * is genuinely a cross-opportunity threshold — same-pair repetition cannot
34
+ * inflate it.
35
+ */
36
+ export const NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES = 5;
37
+ /**
38
+ * Max opportunities (already grouped, one per opportunity) whose allowlisted
39
+ * evidence is sent to the miner in one pass. Bounds prompt size the same way
40
+ * the Lens A miner caps its candidate pool.
41
+ */
42
+ export const NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES = 24;
43
+ /** Max chars of allowlisted evidence content retained per evidence unit. */
44
+ export const NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS = 400;
45
+ //# sourceMappingURL=negotiation-evidence.env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.env.js","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAU,CAAC;AAKrF;;;;GAIG;AACH,MAAM,UAAU,gCAAgC;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,mCAAmC,EAAE,IAAI,EAAE,CAAC;IACpE,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;AACxD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+CAA+C,GAAG,CAAC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG,EAAE,CAAC;AAEzD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,sCAAsC,GAAG,GAAG,CAAC","sourcesContent":["/**\n * Centralized accessor + constants for the Lens C negotiation-evidence\n * question producer (IND-433).\n *\n * NEGOTIATION_EVIDENCE_QUESTIONS_MODE off | shadow | on — default off.\n * - off (default): the lens never runs; zero reads, zero telemetry.\n * - shadow (this issue): mine + verify neutral hypotheses over allowlisted\n * negotiation evidence, emit AGGREGATE telemetry only. Persists no\n * questions and changes no ranking, intent, premise, memory, policy,\n * newborn-stamping, or push behavior.\n * - on (future, IND-437): additionally synthesize/enqueue questions and\n * suppress the older IND-296–299 transcript-question producer.\n *\n * All reads go through this module — do not read the variable via\n * `process.env` elsewhere. The value is read on every call (no caching) so\n * tests and long-lived processes observe changes.\n */\n\n/** Documented modes for the Lens C negotiation-evidence question producer. */\nexport const NEGOTIATION_EVIDENCE_QUESTIONS_MODES = [\"off\", \"shadow\", \"on\"] as const;\n\n/** Mode for the Lens C negotiation-evidence question producer. */\nexport type NegotiationEvidenceQuestionsMode = (typeof NEGOTIATION_EVIDENCE_QUESTIONS_MODES)[number];\n\n/**\n * Current NEGOTIATION_EVIDENCE_QUESTIONS_MODE (default off). Any value other\n * than an exact documented mode (after trimming) coerces to off — an\n * unrecognized flag must never silently enable the lens.\n */\nexport function negotiationEvidenceQuestionsMode(): NegotiationEvidenceQuestionsMode {\n const raw = process.env.NEGOTIATION_EVIDENCE_QUESTIONS_MODE?.trim();\n return raw === \"shadow\" || raw === \"on\" ? raw : \"off\";\n}\n\n/**\n * Recurrence floor: a mined hypothesis is only retained when its verified\n * support spans at least this many DISTINCT opportunities (IND-433 `k=5`).\n * Continuation segments of one opportunity are grouped and count once, so this\n * is genuinely a cross-opportunity threshold — same-pair repetition cannot\n * inflate it.\n */\nexport const NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES = 5;\n\n/**\n * Max opportunities (already grouped, one per opportunity) whose allowlisted\n * evidence is sent to the miner in one pass. Bounds prompt size the same way\n * the Lens A miner caps its candidate pool.\n */\nexport const NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES = 24;\n\n/** Max chars of allowlisted evidence content retained per evidence unit. */\nexport const NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS = 400;\n"]}
@@ -0,0 +1,25 @@
1
+ import type { AllowlistedEvidence, EvidenceKind, EvidenceMiningScope, RawEvidenceSegment } from "./negotiation-evidence.types.js";
2
+ /** Result of one extraction pass over a recipient+intent's segments. */
3
+ export interface ExtractionResult {
4
+ /** Allowlisted evidence, grouped/deduped, one logical set per opportunity. */
5
+ evidence: AllowlistedEvidence[];
6
+ /** Distinct opportunities that produced at least one evidence unit. */
7
+ distinctOpportunities: number;
8
+ /** Candidate records rejected by allowlist / exclusion / provenance rules. */
9
+ excludedRecords: number;
10
+ /** Allowlisted evidence unit counts, by kind. */
11
+ evidenceCounts: Record<EvidenceKind, number>;
12
+ }
13
+ /**
14
+ * Extract allowlisted evidence for one (recipient, intent) mining scope.
15
+ *
16
+ * Continuation grouping: segments are grouped by `opportunityId`; a single
17
+ * opportunity contributes ONE deduped evidence set regardless of how many
18
+ * continuation tasks it spans, so an opportunity counts once toward recurrence.
19
+ *
20
+ * Contamination guard: a segment whose recipient / intent / fingerprint /
21
+ * network do not match the scope, or whose own recipient equals its
22
+ * counterparty, is rejected wholesale (all records counted as excluded).
23
+ */
24
+ export declare function extractAllowlistedEvidence(scope: EvidenceMiningScope, segments: RawEvidenceSegment[]): ExtractionResult;
25
+ //# sourceMappingURL=negotiation-evidence.extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.extractor.d.ts","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.extractor.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,mBAAmB,EAAmB,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEnJ,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;CAC9C;AA+CD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,kBAAkB,EAAE,GAC7B,gBAAgB,CAsIlB"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Lens C in-place evidence extraction (IND-433).
3
+ *
4
+ * Reads raw negotiation segments and projects ONLY the allowlist:
5
+ * - authoritative owner answers (answerer === recipient),
6
+ * - structured bilateral actions and coarse outcomes,
7
+ * - explicitly shared (tagged) message content.
8
+ *
9
+ * Everything else is dropped by construction. The function is pure and
10
+ * deterministic so adversarial fixtures can prove that every excluded source
11
+ * and every recipient / intent / opportunity / network / task / speaker
12
+ * mismatch is rejected, and that same-pair continuations cannot inflate the
13
+ * distinct-opportunity count.
14
+ */
15
+ import { NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS, NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES } from "./negotiation-evidence.env.js";
16
+ const EMPTY_COUNTS = () => ({
17
+ owner_answer: 0,
18
+ bilateral_action: 0,
19
+ coarse_outcome: 0,
20
+ shared_message: 0,
21
+ });
22
+ /** Collapse whitespace + lowercase for dedup only (not for stored content). */
23
+ function dedupKey(kind, speaker, content) {
24
+ return `${kind}\u0000${speaker}\u0000${content.toLowerCase().replace(/\s+/g, " ").trim()}`;
25
+ }
26
+ /** Bound + whitespace-normalize stored content. */
27
+ function boundContent(raw) {
28
+ return raw.replace(/\s+/g, " ").trim().slice(0, NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS);
29
+ }
30
+ /**
31
+ * A segment matches the pass scope only when EVERY provenance field is exactly
32
+ * equal. A single mismatch rejects the whole segment (cross-recipient,
33
+ * cross-intent, cross-fingerprint, or cross-network contamination).
34
+ */
35
+ function segmentMatchesScope(segment, scope) {
36
+ return (segment.recipientUserId === scope.recipientUserId &&
37
+ segment.intentId === scope.intentId &&
38
+ segment.intentFingerprint === scope.intentFingerprint &&
39
+ segment.networkId === scope.networkId);
40
+ }
41
+ /** Map a turn sender to owner/counterparty, or null when it is neither. */
42
+ function mapSpeaker(senderUserId, segment) {
43
+ if (senderUserId === segment.recipientUserId)
44
+ return "owner";
45
+ if (senderUserId === segment.counterpartyUserId)
46
+ return "counterparty";
47
+ return null;
48
+ }
49
+ /** Count every raw record a segment carries (for excluded-record accounting). */
50
+ function countSegmentRecords(segment) {
51
+ let n = segment.turns.length + (segment.ownerAnswers?.length ?? 0);
52
+ if (segment.outcome)
53
+ n += 1;
54
+ return n;
55
+ }
56
+ /**
57
+ * Extract allowlisted evidence for one (recipient, intent) mining scope.
58
+ *
59
+ * Continuation grouping: segments are grouped by `opportunityId`; a single
60
+ * opportunity contributes ONE deduped evidence set regardless of how many
61
+ * continuation tasks it spans, so an opportunity counts once toward recurrence.
62
+ *
63
+ * Contamination guard: a segment whose recipient / intent / fingerprint /
64
+ * network do not match the scope, or whose own recipient equals its
65
+ * counterparty, is rejected wholesale (all records counted as excluded).
66
+ */
67
+ export function extractAllowlistedEvidence(scope, segments) {
68
+ const evidenceCounts = EMPTY_COUNTS();
69
+ let excludedRecords = 0;
70
+ // Group in-scope segments by opportunity (continuation grouping).
71
+ const byOpportunity = new Map();
72
+ for (const segment of segments) {
73
+ if (!segmentMatchesScope(segment, scope) || segment.recipientUserId === segment.counterpartyUserId) {
74
+ excludedRecords += countSegmentRecords(segment);
75
+ continue;
76
+ }
77
+ const group = byOpportunity.get(segment.opportunityId);
78
+ if (group)
79
+ group.push(segment);
80
+ else
81
+ byOpportunity.set(segment.opportunityId, [segment]);
82
+ }
83
+ const evidence = [];
84
+ let distinctOpportunities = 0;
85
+ for (const [opportunityId, group] of byOpportunity) {
86
+ if (distinctOpportunities >= NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES) {
87
+ // Cap opportunities per pass; remaining segments simply are not mined.
88
+ excludedRecords += group.reduce((n, s) => n + countSegmentRecords(s), 0);
89
+ continue;
90
+ }
91
+ const seen = new Set();
92
+ const opportunityEvidence = [];
93
+ const push = (segment, kind, speaker, rawContent) => {
94
+ const content = boundContent(rawContent);
95
+ if (content.length === 0)
96
+ return false;
97
+ const key = dedupKey(kind, speaker, content);
98
+ if (seen.has(key))
99
+ return true; // duplicate continuation content retains its first source
100
+ seen.add(key);
101
+ opportunityEvidence.push({
102
+ kind,
103
+ speaker,
104
+ content,
105
+ taskId: segment.taskId,
106
+ conversationId: segment.conversationId,
107
+ });
108
+ return true;
109
+ };
110
+ for (const segment of group) {
111
+ // Authoritative owner answers — answerer MUST be the recipient.
112
+ for (const answer of segment.ownerAnswers ?? []) {
113
+ if (answer.answererUserId !== segment.recipientUserId) {
114
+ excludedRecords += 1;
115
+ continue;
116
+ }
117
+ const parts = [...answer.selectedOptions, ...(answer.freeText ? [answer.freeText] : [])];
118
+ const content = parts.join(" | ");
119
+ if (!push(segment, "owner_answer", "owner", content))
120
+ excludedRecords += 1;
121
+ }
122
+ // Turns → structured bilateral action + explicitly shared message.
123
+ for (const turn of segment.turns) {
124
+ const speaker = mapSpeaker(turn.senderUserId, segment);
125
+ if (speaker === null) {
126
+ // Speaker mismatch: neither owner nor counterparty. Nothing from
127
+ // this turn (action AND message) may be trusted.
128
+ excludedRecords += 1;
129
+ continue;
130
+ }
131
+ // Structured bilateral action (label only; reasoning never read).
132
+ const action = turn.action?.trim();
133
+ if (action)
134
+ push(segment, "bilateral_action", "system", action);
135
+ // Explicitly shared message content only. Untagged → excluded.
136
+ const message = turn.message?.trim();
137
+ if (message) {
138
+ if (turn.sharedTagged === true)
139
+ push(segment, "shared_message", speaker, message);
140
+ else
141
+ excludedRecords += 1;
142
+ }
143
+ }
144
+ // Coarse outcome. `screened_out` is a private client gate → excluded.
145
+ const outcome = segment.outcome;
146
+ if (outcome) {
147
+ if (outcome.reason === "screened_out") {
148
+ excludedRecords += 1;
149
+ }
150
+ else {
151
+ const roles = (outcome.agreedRoles ?? [])
152
+ .map((r) => `${r.role}`)
153
+ .sort()
154
+ .join(",");
155
+ const content = [
156
+ `hasOpportunity=${outcome.hasOpportunity}`,
157
+ outcome.reason ? `reason=${outcome.reason}` : "",
158
+ roles ? `roles=${roles}` : "",
159
+ ]
160
+ .filter(Boolean)
161
+ .join(" ");
162
+ push(segment, "coarse_outcome", "system", content);
163
+ }
164
+ }
165
+ }
166
+ if (opportunityEvidence.length === 0)
167
+ continue;
168
+ distinctOpportunities += 1;
169
+ const first = group[0];
170
+ opportunityEvidence.forEach((e, ordinal) => {
171
+ evidenceCounts[e.kind] += 1;
172
+ evidence.push({
173
+ evidenceId: `${e.kind}:${opportunityId}:${ordinal}`,
174
+ kind: e.kind,
175
+ speaker: e.speaker,
176
+ content: e.content,
177
+ recipientUserId: first.recipientUserId,
178
+ intentId: first.intentId,
179
+ intentFingerprint: first.intentFingerprint,
180
+ opportunityId,
181
+ taskId: e.taskId,
182
+ conversationId: e.conversationId,
183
+ networkId: first.networkId,
184
+ });
185
+ });
186
+ }
187
+ return { evidence, distinctOpportunities, excludedRecords, evidenceCounts };
188
+ }
189
+ //# sourceMappingURL=negotiation-evidence.extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.extractor.js","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Lens C in-place evidence extraction (IND-433).\n *\n * Reads raw negotiation segments and projects ONLY the allowlist:\n * - authoritative owner answers (answerer === recipient),\n * - structured bilateral actions and coarse outcomes,\n * - explicitly shared (tagged) message content.\n *\n * Everything else is dropped by construction. The function is pure and\n * deterministic so adversarial fixtures can prove that every excluded source\n * and every recipient / intent / opportunity / network / task / speaker\n * mismatch is rejected, and that same-pair continuations cannot inflate the\n * distinct-opportunity count.\n */\nimport { NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS, NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES } from \"./negotiation-evidence.env.js\";\nimport type { AllowlistedEvidence, EvidenceKind, EvidenceMiningScope, EvidenceSpeaker, RawEvidenceSegment } from \"./negotiation-evidence.types.js\";\n\n/** Result of one extraction pass over a recipient+intent's segments. */\nexport interface ExtractionResult {\n /** Allowlisted evidence, grouped/deduped, one logical set per opportunity. */\n evidence: AllowlistedEvidence[];\n /** Distinct opportunities that produced at least one evidence unit. */\n distinctOpportunities: number;\n /** Candidate records rejected by allowlist / exclusion / provenance rules. */\n excludedRecords: number;\n /** Allowlisted evidence unit counts, by kind. */\n evidenceCounts: Record<EvidenceKind, number>;\n}\n\nconst EMPTY_COUNTS = (): Record<EvidenceKind, number> => ({\n owner_answer: 0,\n bilateral_action: 0,\n coarse_outcome: 0,\n shared_message: 0,\n});\n\n/** Collapse whitespace + lowercase for dedup only (not for stored content). */\nfunction dedupKey(kind: EvidenceKind, speaker: EvidenceSpeaker, content: string): string {\n return `${kind}\\u0000${speaker}\\u0000${content.toLowerCase().replace(/\\s+/g, \" \").trim()}`;\n}\n\n/** Bound + whitespace-normalize stored content. */\nfunction boundContent(raw: string): string {\n return raw.replace(/\\s+/g, \" \").trim().slice(0, NEGOTIATION_EVIDENCE_MAX_CONTENT_CHARS);\n}\n\n/**\n * A segment matches the pass scope only when EVERY provenance field is exactly\n * equal. A single mismatch rejects the whole segment (cross-recipient,\n * cross-intent, cross-fingerprint, or cross-network contamination).\n */\nfunction segmentMatchesScope(segment: RawEvidenceSegment, scope: EvidenceMiningScope): boolean {\n return (\n segment.recipientUserId === scope.recipientUserId &&\n segment.intentId === scope.intentId &&\n segment.intentFingerprint === scope.intentFingerprint &&\n segment.networkId === scope.networkId\n );\n}\n\n/** Map a turn sender to owner/counterparty, or null when it is neither. */\nfunction mapSpeaker(senderUserId: string, segment: RawEvidenceSegment): EvidenceSpeaker | null {\n if (senderUserId === segment.recipientUserId) return \"owner\";\n if (senderUserId === segment.counterpartyUserId) return \"counterparty\";\n return null;\n}\n\n/** Count every raw record a segment carries (for excluded-record accounting). */\nfunction countSegmentRecords(segment: RawEvidenceSegment): number {\n let n = segment.turns.length + (segment.ownerAnswers?.length ?? 0);\n if (segment.outcome) n += 1;\n return n;\n}\n\n/**\n * Extract allowlisted evidence for one (recipient, intent) mining scope.\n *\n * Continuation grouping: segments are grouped by `opportunityId`; a single\n * opportunity contributes ONE deduped evidence set regardless of how many\n * continuation tasks it spans, so an opportunity counts once toward recurrence.\n *\n * Contamination guard: a segment whose recipient / intent / fingerprint /\n * network do not match the scope, or whose own recipient equals its\n * counterparty, is rejected wholesale (all records counted as excluded).\n */\nexport function extractAllowlistedEvidence(\n scope: EvidenceMiningScope,\n segments: RawEvidenceSegment[],\n): ExtractionResult {\n const evidenceCounts = EMPTY_COUNTS();\n let excludedRecords = 0;\n\n // Group in-scope segments by opportunity (continuation grouping).\n const byOpportunity = new Map<string, RawEvidenceSegment[]>();\n for (const segment of segments) {\n if (!segmentMatchesScope(segment, scope) || segment.recipientUserId === segment.counterpartyUserId) {\n excludedRecords += countSegmentRecords(segment);\n continue;\n }\n const group = byOpportunity.get(segment.opportunityId);\n if (group) group.push(segment);\n else byOpportunity.set(segment.opportunityId, [segment]);\n }\n\n const evidence: AllowlistedEvidence[] = [];\n let distinctOpportunities = 0;\n\n for (const [opportunityId, group] of byOpportunity) {\n if (distinctOpportunities >= NEGOTIATION_EVIDENCE_MAX_OPPORTUNITIES) {\n // Cap opportunities per pass; remaining segments simply are not mined.\n excludedRecords += group.reduce((n, s) => n + countSegmentRecords(s), 0);\n continue;\n }\n\n const seen = new Set<string>();\n const opportunityEvidence: Array<{\n kind: EvidenceKind;\n speaker: EvidenceSpeaker;\n content: string;\n taskId: string;\n conversationId: string;\n }> = [];\n\n const push = (\n segment: RawEvidenceSegment,\n kind: EvidenceKind,\n speaker: EvidenceSpeaker,\n rawContent: string,\n ): boolean => {\n const content = boundContent(rawContent);\n if (content.length === 0) return false;\n const key = dedupKey(kind, speaker, content);\n if (seen.has(key)) return true; // duplicate continuation content retains its first source\n seen.add(key);\n opportunityEvidence.push({\n kind,\n speaker,\n content,\n taskId: segment.taskId,\n conversationId: segment.conversationId,\n });\n return true;\n };\n\n for (const segment of group) {\n // Authoritative owner answers — answerer MUST be the recipient.\n for (const answer of segment.ownerAnswers ?? []) {\n if (answer.answererUserId !== segment.recipientUserId) {\n excludedRecords += 1;\n continue;\n }\n const parts = [...answer.selectedOptions, ...(answer.freeText ? [answer.freeText] : [])];\n const content = parts.join(\" | \");\n if (!push(segment, \"owner_answer\", \"owner\", content)) excludedRecords += 1;\n }\n\n // Turns → structured bilateral action + explicitly shared message.\n for (const turn of segment.turns) {\n const speaker = mapSpeaker(turn.senderUserId, segment);\n if (speaker === null) {\n // Speaker mismatch: neither owner nor counterparty. Nothing from\n // this turn (action AND message) may be trusted.\n excludedRecords += 1;\n continue;\n }\n // Structured bilateral action (label only; reasoning never read).\n const action = turn.action?.trim();\n if (action) push(segment, \"bilateral_action\", \"system\", action);\n\n // Explicitly shared message content only. Untagged → excluded.\n const message = turn.message?.trim();\n if (message) {\n if (turn.sharedTagged === true) push(segment, \"shared_message\", speaker, message);\n else excludedRecords += 1;\n }\n }\n\n // Coarse outcome. `screened_out` is a private client gate → excluded.\n const outcome = segment.outcome;\n if (outcome) {\n if (outcome.reason === \"screened_out\") {\n excludedRecords += 1;\n } else {\n const roles = (outcome.agreedRoles ?? [])\n .map((r) => `${r.role}`)\n .sort()\n .join(\",\");\n const content = [\n `hasOpportunity=${outcome.hasOpportunity}`,\n outcome.reason ? `reason=${outcome.reason}` : \"\",\n roles ? `roles=${roles}` : \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n push(segment, \"coarse_outcome\", \"system\", content);\n }\n }\n }\n\n if (opportunityEvidence.length === 0) continue;\n\n distinctOpportunities += 1;\n const first = group[0];\n opportunityEvidence.forEach((e, ordinal) => {\n evidenceCounts[e.kind] += 1;\n evidence.push({\n evidenceId: `${e.kind}:${opportunityId}:${ordinal}`,\n kind: e.kind,\n speaker: e.speaker,\n content: e.content,\n recipientUserId: first.recipientUserId,\n intentId: first.intentId,\n intentFingerprint: first.intentFingerprint,\n opportunityId,\n taskId: e.taskId,\n conversationId: e.conversationId,\n networkId: first.networkId,\n });\n });\n }\n\n return { evidence, distinctOpportunities, excludedRecords, evidenceCounts };\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { createStructuredModel } from "../../shared/agent/model.config.js";
2
+ import type { AllowlistedEvidence, MinedEvidenceHypothesis } from "./negotiation-evidence.types.js";
3
+ /** Config for NegotiationEvidenceMiner construction. */
4
+ export interface NegotiationEvidenceMinerConfig {
5
+ /** Optional model config override (API key / base URL / model). */
6
+ modelConfig?: Parameters<typeof createStructuredModel>[3];
7
+ }
8
+ /**
9
+ * Stateless hypothesis-mining agent. One `mine()` call = one structured LLM
10
+ * pass. All support verification happens later, in the verifier.
11
+ */
12
+ export declare class NegotiationEvidenceMiner {
13
+ private model;
14
+ constructor(config?: NegotiationEvidenceMinerConfig);
15
+ /**
16
+ * Mine neutral hypotheses for one allowlisted evidence set.
17
+ *
18
+ * @returns Proposed hypotheses (may be empty). Throws on LLM failure —
19
+ * callers run this fire-and-forget and must catch.
20
+ */
21
+ mine(evidence: AllowlistedEvidence[], options?: {
22
+ signal?: AbortSignal;
23
+ }): Promise<MinedEvidenceHypothesis[]>;
24
+ }
25
+ /** Builds the human message: one block per allowlisted evidence unit. */
26
+ export declare function buildEvidencePrompt(evidence: AllowlistedEvidence[]): string;
27
+ //# sourceMappingURL=negotiation-evidence.miner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.miner.d.ts","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.miner.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAI3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAsDpG,wDAAwD;AACxD,MAAM,WAAW,8BAA8B;IAC7C,mEAAmE;IACnE,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3D;AAED;;;GAGG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,KAAK,CAAqE;gBAEtE,MAAM,CAAC,EAAE,8BAA8B;IASnD;;;;;OAKG;IAEG,IAAI,CACR,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,uBAAuB,EAAE,CAAC;CAmBtC;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAK3E"}
@@ -0,0 +1,113 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ /**
11
+ * NegotiationEvidenceMiner — one structured LLM pass that proposes neutral
12
+ * clarification hypotheses over ALLOWLISTED negotiation evidence, with
13
+ * code-side support verification delegated to the verifier (IND-433).
14
+ *
15
+ * The miner only ever sees the allowlisted projection (owner answers, bilateral
16
+ * actions, coarse outcomes, explicitly shared messages) — never reasoning,
17
+ * memories, disclosure subjects, or provenance. Every proposed hypothesis must
18
+ * cite the `evidenceId`s that support it and quote a verbatim span from each;
19
+ * unsupported or hallucinated references are discarded in code, not trusted
20
+ * from the prompt.
21
+ *
22
+ * Follows the Lens A `PoolDiscriminatorMiner` pattern: constructor binds the
23
+ * structured model once; a single public `mine()` per call.
24
+ */
25
+ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
26
+ import { z } from "zod";
27
+ import { createStructuredModel } from "../../shared/agent/model.config.js";
28
+ import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
29
+ import { protocolLogger } from "../../shared/observability/protocol.logger.js";
30
+ import { Timed } from "../../shared/observability/performance.js";
31
+ const logger = protocolLogger("NegotiationEvidenceMiner");
32
+ /** Max hypotheses requested from (and accepted out of) one mining pass. */
33
+ const MAX_HYPOTHESES = 8;
34
+ const HypothesisMiningResponseSchema = z.object({
35
+ hypotheses: z
36
+ .array(z.object({
37
+ statement: z
38
+ .string()
39
+ .describe("A neutral, non-identifying clarification hypothesis about the intent owner's preferences or context"),
40
+ claimType: z
41
+ .enum(["observation", "recipient_fact", "recipient_preference"])
42
+ .describe("observation = a neutral pattern; recipient_fact/recipient_preference = a claim ABOUT the intent owner (needs owner-authored or structured support)"),
43
+ supportRefs: z
44
+ .array(z.object({
45
+ evidenceId: z.string().describe("An evidenceId exactly as given in the evidence list"),
46
+ span: z
47
+ .string()
48
+ .max(200)
49
+ .describe("VERBATIM span (<=120 chars) copied from that evidence's content"),
50
+ }))
51
+ .min(1)
52
+ .describe("One or more support references; every hypothesis must be grounded"),
53
+ }))
54
+ .max(MAX_HYPOTHESES),
55
+ });
56
+ const SYSTEM_PROMPT = `You review ALLOWLISTED evidence gathered from a user's past negotiations and propose neutral clarification hypotheses about that user's (the "intent owner") preferences or context.
57
+
58
+ You are given only a safe projection of evidence: the owner's own answers, structured bilateral actions, coarse outcomes, and explicitly shared messages. Each item has an id, a kind, and a speaker (owner | counterparty | system).
59
+
60
+ Rules for every hypothesis:
61
+ - State it neutrally and at an aggregate level. NEVER name or describe any specific person, and NEVER infer protected attributes (race, ethnicity, religion, gender, age, nationality, disability, politics, sexual orientation).
62
+ - Ground it: cite the evidenceId(s) that support it and copy a short (<=120 chars) VERBATIM substring of that evidence's content as the span. Copy it exactly — spans are checked mechanically and paraphrased spans are discarded.
63
+ - claimType:
64
+ - Use "recipient_fact" or "recipient_preference" only for a claim ABOUT the intent owner. Such claims may ONLY be supported by owner or system evidence — a counterparty statement can never establish a fact or preference about the owner.
65
+ - Use "observation" for a neutral pattern that a counterparty statement may also support.
66
+ - Do not speculate beyond the evidence. If an item does not support a distinct hypothesis, ignore it.
67
+
68
+ Prefer a few well-supported hypotheses over many weak ones. Propose at most ${MAX_HYPOTHESES}.`;
69
+ /**
70
+ * Stateless hypothesis-mining agent. One `mine()` call = one structured LLM
71
+ * pass. All support verification happens later, in the verifier.
72
+ */
73
+ export class NegotiationEvidenceMiner {
74
+ constructor(config) {
75
+ this.model = createStructuredModel("negotiationEvidenceMiner", HypothesisMiningResponseSchema, { name: "negotiation_evidence_hypotheses" }, config?.modelConfig);
76
+ }
77
+ /**
78
+ * Mine neutral hypotheses for one allowlisted evidence set.
79
+ *
80
+ * @returns Proposed hypotheses (may be empty). Throws on LLM failure —
81
+ * callers run this fire-and-forget and must catch.
82
+ */
83
+ async mine(evidence, options) {
84
+ if (evidence.length === 0)
85
+ return [];
86
+ const prompt = buildEvidencePrompt(evidence);
87
+ const raw = await invokeWithAbortSignal(this.model, [new SystemMessage(SYSTEM_PROMPT), new HumanMessage(prompt)], options?.signal);
88
+ const parsed = HypothesisMiningResponseSchema.safeParse(raw);
89
+ if (!parsed.success) {
90
+ logger.warn("Mining response failed schema validation", { issues: parsed.error.issues.length });
91
+ return [];
92
+ }
93
+ return parsed.data.hypotheses.slice(0, MAX_HYPOTHESES).map((h) => ({
94
+ statement: h.statement,
95
+ claimType: h.claimType,
96
+ supportRefs: h.supportRefs.map((r) => ({ evidenceId: r.evidenceId, span: r.span })),
97
+ }));
98
+ }
99
+ }
100
+ __decorate([
101
+ Timed(),
102
+ __metadata("design:type", Function),
103
+ __metadata("design:paramtypes", [Array, Object]),
104
+ __metadata("design:returntype", Promise)
105
+ ], NegotiationEvidenceMiner.prototype, "mine", null);
106
+ /** Builds the human message: one block per allowlisted evidence unit. */
107
+ export function buildEvidencePrompt(evidence) {
108
+ const blocks = evidence
109
+ .map((e) => `[${e.evidenceId}] (${e.kind}, ${e.speaker})\n${e.content}`)
110
+ .join("\n\n");
111
+ return `Allowlisted evidence (${evidence.length} items, id in brackets):\n\n${blocks}`;
112
+ }
113
+ //# sourceMappingURL=negotiation-evidence.miner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.miner.js","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.miner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * NegotiationEvidenceMiner — one structured LLM pass that proposes neutral\n * clarification hypotheses over ALLOWLISTED negotiation evidence, with\n * code-side support verification delegated to the verifier (IND-433).\n *\n * The miner only ever sees the allowlisted projection (owner answers, bilateral\n * actions, coarse outcomes, explicitly shared messages) — never reasoning,\n * memories, disclosure subjects, or provenance. Every proposed hypothesis must\n * cite the `evidenceId`s that support it and quote a verbatim span from each;\n * unsupported or hallucinated references are discarded in code, not trusted\n * from the prompt.\n *\n * Follows the Lens A `PoolDiscriminatorMiner` pattern: constructor binds the\n * structured model once; a single public `mine()` per call.\n */\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\n\nimport { createStructuredModel } from \"../../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../../shared/agent/model-signal.js\";\nimport { protocolLogger } from \"../../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../../shared/observability/performance.js\";\nimport type { AllowlistedEvidence, MinedEvidenceHypothesis } from \"./negotiation-evidence.types.js\";\n\nconst logger = protocolLogger(\"NegotiationEvidenceMiner\");\n\n/** Max hypotheses requested from (and accepted out of) one mining pass. */\nconst MAX_HYPOTHESES = 8;\n\nconst HypothesisMiningResponseSchema = z.object({\n hypotheses: z\n .array(\n z.object({\n statement: z\n .string()\n .describe(\n \"A neutral, non-identifying clarification hypothesis about the intent owner's preferences or context\",\n ),\n claimType: z\n .enum([\"observation\", \"recipient_fact\", \"recipient_preference\"])\n .describe(\n \"observation = a neutral pattern; recipient_fact/recipient_preference = a claim ABOUT the intent owner (needs owner-authored or structured support)\",\n ),\n supportRefs: z\n .array(\n z.object({\n evidenceId: z.string().describe(\"An evidenceId exactly as given in the evidence list\"),\n span: z\n .string()\n .max(200)\n .describe(\"VERBATIM span (<=120 chars) copied from that evidence's content\"),\n }),\n )\n .min(1)\n .describe(\"One or more support references; every hypothesis must be grounded\"),\n }),\n )\n .max(MAX_HYPOTHESES),\n});\n\ntype HypothesisMiningResponse = z.infer<typeof HypothesisMiningResponseSchema>;\n\nconst SYSTEM_PROMPT = `You review ALLOWLISTED evidence gathered from a user's past negotiations and propose neutral clarification hypotheses about that user's (the \"intent owner\") preferences or context.\n\nYou are given only a safe projection of evidence: the owner's own answers, structured bilateral actions, coarse outcomes, and explicitly shared messages. Each item has an id, a kind, and a speaker (owner | counterparty | system).\n\nRules for every hypothesis:\n- State it neutrally and at an aggregate level. NEVER name or describe any specific person, and NEVER infer protected attributes (race, ethnicity, religion, gender, age, nationality, disability, politics, sexual orientation).\n- Ground it: cite the evidenceId(s) that support it and copy a short (<=120 chars) VERBATIM substring of that evidence's content as the span. Copy it exactly — spans are checked mechanically and paraphrased spans are discarded.\n- claimType:\n - Use \"recipient_fact\" or \"recipient_preference\" only for a claim ABOUT the intent owner. Such claims may ONLY be supported by owner or system evidence — a counterparty statement can never establish a fact or preference about the owner.\n - Use \"observation\" for a neutral pattern that a counterparty statement may also support.\n- Do not speculate beyond the evidence. If an item does not support a distinct hypothesis, ignore it.\n\nPrefer a few well-supported hypotheses over many weak ones. Propose at most ${MAX_HYPOTHESES}.`;\n\n/** Config for NegotiationEvidenceMiner construction. */\nexport interface NegotiationEvidenceMinerConfig {\n /** Optional model config override (API key / base URL / model). */\n modelConfig?: Parameters<typeof createStructuredModel>[3];\n}\n\n/**\n * Stateless hypothesis-mining agent. One `mine()` call = one structured LLM\n * pass. All support verification happens later, in the verifier.\n */\nexport class NegotiationEvidenceMiner {\n private model: ReturnType<typeof createStructuredModel<HypothesisMiningResponse>>;\n\n constructor(config?: NegotiationEvidenceMinerConfig) {\n this.model = createStructuredModel<HypothesisMiningResponse>(\n \"negotiationEvidenceMiner\",\n HypothesisMiningResponseSchema,\n { name: \"negotiation_evidence_hypotheses\" },\n config?.modelConfig,\n );\n }\n\n /**\n * Mine neutral hypotheses for one allowlisted evidence set.\n *\n * @returns Proposed hypotheses (may be empty). Throws on LLM failure —\n * callers run this fire-and-forget and must catch.\n */\n @Timed()\n async mine(\n evidence: AllowlistedEvidence[],\n options?: { signal?: AbortSignal },\n ): Promise<MinedEvidenceHypothesis[]> {\n if (evidence.length === 0) return [];\n const prompt = buildEvidencePrompt(evidence);\n const raw = await invokeWithAbortSignal(\n this.model,\n [new SystemMessage(SYSTEM_PROMPT), new HumanMessage(prompt)],\n options?.signal,\n );\n const parsed = HypothesisMiningResponseSchema.safeParse(raw);\n if (!parsed.success) {\n logger.warn(\"Mining response failed schema validation\", { issues: parsed.error.issues.length });\n return [];\n }\n return parsed.data.hypotheses.slice(0, MAX_HYPOTHESES).map((h) => ({\n statement: h.statement,\n claimType: h.claimType,\n supportRefs: h.supportRefs.map((r) => ({ evidenceId: r.evidenceId, span: r.span })),\n }));\n }\n}\n\n/** Builds the human message: one block per allowlisted evidence unit. */\nexport function buildEvidencePrompt(evidence: AllowlistedEvidence[]): string {\n const blocks = evidence\n .map((e) => `[${e.evidenceId}] (${e.kind}, ${e.speaker})\\n${e.content}`)\n .join(\"\\n\\n\");\n return `Allowlisted evidence (${evidence.length} items, id in brackets):\\n\\n${blocks}`;\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import type { NegotiationEvidenceMiner } from "./negotiation-evidence.miner.js";
2
+ import type { EvidenceMiningScope, NegotiationEvidenceShadowResult, RawEvidenceSegment } from "./negotiation-evidence.types.js";
3
+ /** Input for one shadow mining pass. */
4
+ export interface NegotiationEvidenceShadowInput {
5
+ /** The exact recipient + intent/fingerprint + network this pass is bound to. */
6
+ scope: EvidenceMiningScope;
7
+ /** Raw negotiation segments read in place (one or more per opportunity). */
8
+ segments: RawEvidenceSegment[];
9
+ /** Structured hypothesis miner (injectable seam). */
10
+ miner: Pick<NegotiationEvidenceMiner, "mine">;
11
+ /** Override the recurrence floor (defaults to the IND-433 `k=5`). */
12
+ minDistinctOpportunities?: number;
13
+ signal?: AbortSignal;
14
+ }
15
+ /**
16
+ * Run one Lens C shadow pass. Throws only when *mining* fails (callers run
17
+ * fire-and-forget and must catch); extraction and verification are pure.
18
+ */
19
+ export declare function runNegotiationEvidenceShadow(input: NegotiationEvidenceShadowInput): Promise<NegotiationEvidenceShadowResult>;
20
+ //# sourceMappingURL=negotiation-evidence.shadow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.shadow.d.ts","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.shadow.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAEhF,OAAO,KAAK,EAAE,mBAAmB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEhI,wCAAwC;AACxC,MAAM,WAAW,8BAA8B;IAC7C,gFAAgF;IAChF,KAAK,EAAE,mBAAmB,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,qDAAqD;IACrD,KAAK,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;IAC9C,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CA+C1C"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Lens C shadow orchestrator (IND-433).
3
+ *
4
+ * extract (allowlist, in place) → mine (neutral hypotheses) → verify (support
5
+ * resolves to allowlisted evidence + speaker constraint) → recurrence gate
6
+ * (>= k distinct opportunities). Returns aggregate telemetry plus the retained
7
+ * hypotheses for OFFLINE review.
8
+ *
9
+ * This function performs NO persistence and NO user-visible action. It does
10
+ * not create questions and changes no ranking, intent, premise, memory,
11
+ * policy, newborn-stamping, or push behavior — those belong to the future
12
+ * `on` mode (IND-437). Callers in shadow mode must log only
13
+ * {@link NegotiationEvidenceShadowResult.telemetry} (aggregate-only) and must
14
+ * not persist or route the retained hypotheses.
15
+ */
16
+ import { NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES } from "./negotiation-evidence.env.js";
17
+ import { extractAllowlistedEvidence } from "./negotiation-evidence.extractor.js";
18
+ import { verifyHypotheses } from "./negotiation-evidence.verifier.js";
19
+ /**
20
+ * Run one Lens C shadow pass. Throws only when *mining* fails (callers run
21
+ * fire-and-forget and must catch); extraction and verification are pure.
22
+ */
23
+ export async function runNegotiationEvidenceShadow(input) {
24
+ const { scope, segments } = input;
25
+ const minDistinct = input.minDistinctOpportunities ?? NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES;
26
+ const extraction = extractAllowlistedEvidence(scope, segments);
27
+ const baseTelemetry = {
28
+ recipientUserId: scope.recipientUserId,
29
+ intentId: scope.intentId,
30
+ segments: segments.length,
31
+ distinctOpportunities: extraction.distinctOpportunities,
32
+ evidenceCounts: extraction.evidenceCounts,
33
+ excludedRecords: extraction.excludedRecords,
34
+ };
35
+ // Cheap exit: recurrence is impossible below the distinct-opportunity floor,
36
+ // so never spend an LLM call that could only be discarded.
37
+ if (extraction.distinctOpportunities < minDistinct) {
38
+ return {
39
+ telemetry: {
40
+ ...baseTelemetry,
41
+ hypothesesMined: 0,
42
+ hypothesesSupported: 0,
43
+ hypothesesRecurrent: 0,
44
+ hypothesesDiscarded: 0,
45
+ },
46
+ hypotheses: [],
47
+ };
48
+ }
49
+ const mined = await input.miner.mine(extraction.evidence, input.signal ? { signal: input.signal } : undefined);
50
+ const verification = verifyHypotheses(mined, extraction.evidence, minDistinct);
51
+ return {
52
+ telemetry: {
53
+ ...baseTelemetry,
54
+ hypothesesMined: mined.length,
55
+ hypothesesSupported: verification.supported,
56
+ hypothesesRecurrent: verification.recurrent,
57
+ hypothesesDiscarded: verification.discarded,
58
+ },
59
+ hypotheses: verification.retained,
60
+ };
61
+ }
62
+ //# sourceMappingURL=negotiation-evidence.shadow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiation-evidence.shadow.js","sourceRoot":"/","sources":["opportunity/negotiation-evidence/negotiation-evidence.shadow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,+CAA+C,EAAE,MAAM,+BAA+B,CAAC;AAChG,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAgBtE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,KAAqC;IAErC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAClC,MAAM,WAAW,GAAG,KAAK,CAAC,wBAAwB,IAAI,+CAA+C,CAAC;IAEtG,MAAM,UAAU,GAAG,0BAA0B,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE/D,MAAM,aAAa,GAAG;QACpB,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,QAAQ,CAAC,MAAM;QACzB,qBAAqB,EAAE,UAAU,CAAC,qBAAqB;QACvD,cAAc,EAAE,UAAU,CAAC,cAAc;QACzC,eAAe,EAAE,UAAU,CAAC,eAAe;KACnC,CAAC;IAEX,6EAA6E;IAC7E,2DAA2D;IAC3D,IAAI,UAAU,CAAC,qBAAqB,GAAG,WAAW,EAAE,CAAC;QACnD,OAAO;YACL,SAAS,EAAE;gBACT,GAAG,aAAa;gBAChB,eAAe,EAAE,CAAC;gBAClB,mBAAmB,EAAE,CAAC;gBACtB,mBAAmB,EAAE,CAAC;gBACtB,mBAAmB,EAAE,CAAC;aACvB;YACD,UAAU,EAAE,EAAE;SACf,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,CAClC,UAAU,CAAC,QAAQ,EACnB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CACpD,CAAC;IAEF,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAE/E,OAAO;QACL,SAAS,EAAE;YACT,GAAG,aAAa;YAChB,eAAe,EAAE,KAAK,CAAC,MAAM;YAC7B,mBAAmB,EAAE,YAAY,CAAC,SAAS;YAC3C,mBAAmB,EAAE,YAAY,CAAC,SAAS;YAC3C,mBAAmB,EAAE,YAAY,CAAC,SAAS;SAC5C;QACD,UAAU,EAAE,YAAY,CAAC,QAAQ;KAClC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Lens C shadow orchestrator (IND-433).\n *\n * extract (allowlist, in place) → mine (neutral hypotheses) → verify (support\n * resolves to allowlisted evidence + speaker constraint) → recurrence gate\n * (>= k distinct opportunities). Returns aggregate telemetry plus the retained\n * hypotheses for OFFLINE review.\n *\n * This function performs NO persistence and NO user-visible action. It does\n * not create questions and changes no ranking, intent, premise, memory,\n * policy, newborn-stamping, or push behavior — those belong to the future\n * `on` mode (IND-437). Callers in shadow mode must log only\n * {@link NegotiationEvidenceShadowResult.telemetry} (aggregate-only) and must\n * not persist or route the retained hypotheses.\n */\nimport { NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES } from \"./negotiation-evidence.env.js\";\nimport { extractAllowlistedEvidence } from \"./negotiation-evidence.extractor.js\";\nimport type { NegotiationEvidenceMiner } from \"./negotiation-evidence.miner.js\";\nimport { verifyHypotheses } from \"./negotiation-evidence.verifier.js\";\nimport type { EvidenceMiningScope, NegotiationEvidenceShadowResult, RawEvidenceSegment } from \"./negotiation-evidence.types.js\";\n\n/** Input for one shadow mining pass. */\nexport interface NegotiationEvidenceShadowInput {\n /** The exact recipient + intent/fingerprint + network this pass is bound to. */\n scope: EvidenceMiningScope;\n /** Raw negotiation segments read in place (one or more per opportunity). */\n segments: RawEvidenceSegment[];\n /** Structured hypothesis miner (injectable seam). */\n miner: Pick<NegotiationEvidenceMiner, \"mine\">;\n /** Override the recurrence floor (defaults to the IND-433 `k=5`). */\n minDistinctOpportunities?: number;\n signal?: AbortSignal;\n}\n\n/**\n * Run one Lens C shadow pass. Throws only when *mining* fails (callers run\n * fire-and-forget and must catch); extraction and verification are pure.\n */\nexport async function runNegotiationEvidenceShadow(\n input: NegotiationEvidenceShadowInput,\n): Promise<NegotiationEvidenceShadowResult> {\n const { scope, segments } = input;\n const minDistinct = input.minDistinctOpportunities ?? NEGOTIATION_EVIDENCE_MIN_DISTINCT_OPPORTUNITIES;\n\n const extraction = extractAllowlistedEvidence(scope, segments);\n\n const baseTelemetry = {\n recipientUserId: scope.recipientUserId,\n intentId: scope.intentId,\n segments: segments.length,\n distinctOpportunities: extraction.distinctOpportunities,\n evidenceCounts: extraction.evidenceCounts,\n excludedRecords: extraction.excludedRecords,\n } as const;\n\n // Cheap exit: recurrence is impossible below the distinct-opportunity floor,\n // so never spend an LLM call that could only be discarded.\n if (extraction.distinctOpportunities < minDistinct) {\n return {\n telemetry: {\n ...baseTelemetry,\n hypothesesMined: 0,\n hypothesesSupported: 0,\n hypothesesRecurrent: 0,\n hypothesesDiscarded: 0,\n },\n hypotheses: [],\n };\n }\n\n const mined = await input.miner.mine(\n extraction.evidence,\n input.signal ? { signal: input.signal } : undefined,\n );\n\n const verification = verifyHypotheses(mined, extraction.evidence, minDistinct);\n\n return {\n telemetry: {\n ...baseTelemetry,\n hypothesesMined: mined.length,\n hypothesesSupported: verification.supported,\n hypothesesRecurrent: verification.recurrent,\n hypothesesDiscarded: verification.discarded,\n },\n hypotheses: verification.retained,\n };\n}\n"]}