@indexnetwork/protocol 65.0.0-rc.606.1 → 66.0.0-rc.608.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 66.0.0
4
+
5
+ ### Breaking changes
6
+
7
+ - Opportunity copy comes only from the LLM presenter. `OpportunityPresenter.present()` and `presentCard()` throw on LLM failure, timeout, or invalid output instead of returning fallback copy; `isFallback` and `fallbackReason` are gone from their results.
8
+ - Remove `presentOpportunity`, `stripUuids`, `truncateAtBoundary`, `safeFallbackSummary`, `DEFAULT_FALLBACK_HEADLINE`, `hasUnsupportedOpportunityClaim`, `stripUnsupportedOpportunityClaims`, and `stripUnsupportedOpportunityClaimsText` from the public entry point. The regex claim-safety guard is deleted; the presenter prompts carry the rule.
9
+ - `readOpportunities` items no longer carry `reasoning`. Radar drops a card when its presenter call fails instead of rendering reasoning-derived fallback copy.
10
+
3
11
  ## 65.0.0
4
12
 
5
13
  ### Breaking changes
package/dist/index.d.ts CHANGED
@@ -43,12 +43,9 @@ export type { OpportunityEvidence } from "./protocol/schemas/network-assignment.
43
43
  export type { CreateIntentCounterpartyData, OpenedNegotiation, } from "./internal/opportunities/opportunity.counterparties.js";
44
44
  export { gatherPresenterContext, OpportunityPresenter, } from "./internal/opportunities/opportunity.presentation.js";
45
45
  export type { PresenterDatabase, } from "./internal/opportunities/opportunity.presentation.js";
46
- export { hasUnsupportedOpportunityClaim, stripUnsupportedOpportunityClaims, stripUnsupportedOpportunityClaims as stripUnsupportedOpportunityClaimsText, } from "./internal/shared/utils/claim-safety.js";
47
46
  export { getPrimaryActionLabel, } from "./internal/opportunities/opportunity.labels.js";
48
47
  export { buildApiChatCardPresentationCacheKey, buildRadarCardPresentationCacheKey, } from "./internal/opportunities/opportunity.presentation.js";
49
- export { presentOpportunity, stripUuids, truncateAtBoundary, } from "./internal/opportunities/opportunity.presentation.js";
50
48
  export type { UserInfo, } from "./internal/opportunities/opportunity.presentation.js";
51
- export { DEFAULT_FALLBACK_HEADLINE, safeFallbackSummary, } from "./internal/opportunities/opportunity.presentation.js";
52
49
  export { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, RADAR_SOFT_TARGETS, selectByComposition, validateOpportunityActors, } from "./internal/opportunities/opportunity.utils.js";
53
50
  export { RadarGraphFactory, } from "./internal/opportunities/radar/radar.graph.js";
54
51
  export { readOpportunities } from './internal/opportunities/opportunity.graph.modes.js';
package/dist/index.js CHANGED
@@ -47,11 +47,8 @@ export { normalizeTelegramHandle } from './internal/shared/utils/telegram-handle
47
47
  */
48
48
  export { pairKeyOf, } from "./internal/opportunities/opportunity.counterparties.js";
49
49
  export { gatherPresenterContext, OpportunityPresenter, } from "./internal/opportunities/opportunity.presentation.js";
50
- export { hasUnsupportedOpportunityClaim, stripUnsupportedOpportunityClaims, stripUnsupportedOpportunityClaims as stripUnsupportedOpportunityClaimsText, } from "./internal/shared/utils/claim-safety.js";
51
50
  export { getPrimaryActionLabel, } from "./internal/opportunities/opportunity.labels.js";
52
51
  export { buildApiChatCardPresentationCacheKey, buildRadarCardPresentationCacheKey, } from "./internal/opportunities/opportunity.presentation.js";
53
- export { presentOpportunity, stripUuids, truncateAtBoundary, } from "./internal/opportunities/opportunity.presentation.js";
54
- export { DEFAULT_FALLBACK_HEADLINE, safeFallbackSummary, } from "./internal/opportunities/opportunity.presentation.js";
55
52
  export { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, RADAR_SOFT_TARGETS, selectByComposition, validateOpportunityActors, } from "./internal/opportunities/opportunity.utils.js";
56
53
  export { RadarGraphFactory, } from "./internal/opportunities/radar/radar.graph.js";
57
54
  export { readOpportunities } from './internal/opportunities/opportunity.graph.modes.js';
@@ -29,7 +29,6 @@ export declare function readOpportunities(deps: {
29
29
  networkName: string;
30
30
  connectedWith: string[];
31
31
  suggestedBy: null;
32
- reasoning: string;
33
32
  status: import("../../platform/database.js").OpportunityStatus;
34
33
  category: string;
35
34
  confidence: number | null;
@@ -10,7 +10,6 @@
10
10
  * `readResult` / `mutationResult` exactly as before.
11
11
  */
12
12
  import { timed } from '../shared/observability/performance.js';
13
- import { safeFallbackSummary } from "./opportunity.presentation.js";
14
13
  import { protocolLogger } from '../shared/observability/protocol.logger.js';
15
14
  const readLog = protocolLogger('Opportunity:Read');
16
15
  /**
@@ -101,10 +100,6 @@ export async function readOpportunities(deps, request) {
101
100
  networkName: networkRecord?.title ?? (actorNetworkId ?? ''),
102
101
  connectedWith,
103
102
  suggestedBy,
104
- reasoning: safeFallbackSummary(opp.interpretation?.reasoning, {
105
- counterpartName: connectedWith.join(' and '),
106
- emptyText: 'Connection opportunity',
107
- }),
108
103
  status: opp.status,
109
104
  category,
110
105
  confidence: confidence != null ? confidence : null,
@@ -1,5 +1,3 @@
1
- /** Max chars for main text in minimal opportunity cards. Full text shown so cards are not truncated. */
2
- export declare const MINIMAL_MAIN_TEXT_MAX_CHARS = 2000;
3
1
  /** Hardcoded button labels for opportunity cards (not LLM-generated). */
4
2
  export declare const PRIMARY_ACTION_LABEL_DEFAULT = "Start Chat";
5
3
  export declare const SECONDARY_ACTION_LABEL = "Skip";
@@ -1,5 +1,3 @@
1
- /** Max chars for main text in minimal opportunity cards. Full text shown so cards are not truncated. */
2
- export const MINIMAL_MAIN_TEXT_MAX_CHARS = 2000;
3
1
  /** Hardcoded button labels for opportunity cards (not LLM-generated). */
4
2
  export const PRIMARY_ACTION_LABEL_DEFAULT = "Start Chat";
5
3
  export const SECONDARY_ACTION_LABEL = "Skip";
@@ -2,186 +2,26 @@
2
2
  * The opportunity presentation cluster.
3
3
  *
4
4
  * One file for the whole path from a persisted opportunity to the copy a user
5
- * reads: the pure text transforms, the cache-key builders, the safe-fallback
6
- * pipeline, and the LLM presenter itself. They were four modules that only ever
7
- * called each other in one direction, and following a card's copy meant hopping
8
- * between them.
5
+ * reads: the cache-key builders and the LLM presenter itself. All user-facing
6
+ * copy comes from the presenter; there is no deterministic fallback.
9
7
  *
10
8
  * Sections, in dependency order:
11
- * 1. Pure presentation transforms
12
- * 2. Presentation cache keys
13
- * 3. Safe-presentation pipeline (fallbacks that never leak raw reasoning)
14
- * 4. OpportunityPresenter (LLM card and chat copy)
9
+ * 1. Presentation cache keys
10
+ * 2. OpportunityPresenter (LLM card and chat copy)
15
11
  */
16
12
  import { z } from "zod";
17
13
  import type { Opportunity } from "../../platform/database.js";
18
14
  import type { CompositeDatabase } from "../../platform/database.js";
19
15
  import type { NegotiationContext } from "./negotiation-context.loader.js";
20
- /**
21
- * Pure presentation layer for opportunities.
22
- * Generates title, description, and CTA based on viewer context — no DB access.
23
- */
24
- export interface OpportunityPresentation {
25
- title: string;
26
- description: string;
27
- callToAction: string;
28
- }
29
16
  export interface UserInfo {
30
17
  id: string;
31
18
  name: string;
32
19
  avatar: string | null;
33
20
  }
34
- /**
35
- * Generate presentation copy for an opportunity based on viewer context.
36
- * Pure function — no side effects, no database access.
37
- */
38
- export declare function presentOpportunity(opp: Opportunity, viewerId: string, otherPartyInfo: UserInfo, format: 'card' | 'email' | 'notification'): OpportunityPresentation;
39
- export declare function stripUuids(text: string): string;
40
- /**
41
- * Truncate user-facing text to at most `maxChars` without cutting mid-word.
42
- *
43
- * Prefers a sentence boundary, then a word boundary, and only falls back to a
44
- * hard slice if no boundary exists within the limit. An ellipsis is appended
45
- * when the text is actually shortened. Used by presenter fallbacks so a degraded
46
- * card never shows a sentence chopped mid-word (e.g. "His focus on 'indiv").
47
- */
48
- export declare function truncateAtBoundary(text: string, maxChars: number): string;
49
- /**
50
- * Returns viewer-centric main text for an opportunity card.
51
- * Prefers the part of the reasoning that describes the counterpart (the person
52
- * on the card), so the viewer sees an introduction to the counterpart rather
53
- * than a description of themselves.
54
- *
55
- * @param reasoning - Raw interpretation.reasoning (may describe both parties).
56
- * @param counterpartName - Display name of the suggested connection (e.g. "Alex Chen").
57
- * @param maxChars - Max length of returned string (default MINIMAL_MAIN_TEXT_MAX_CHARS).
58
- * @param viewerName - Optional display name of the viewer (signed-in user). When provided, sentences or prefixes describing the viewer are skipped so the card introduces the counterpart, not the viewer.
59
- * @returns Viewer-centric snippet mentioning the counterpart when possible; if counterpartName is empty, returns reasoning truncated to maxChars. Never null; may be "A suggested connection." when reasoning is empty.
60
- */
61
- export declare function viewerCentricCardSummary(reasoning: string, counterpartName: string, maxChars?: number, viewerName?: string): string;
62
- /**
63
- * Generates a short narrator remark from opportunity reasoning for the narrator chip.
64
- * Used by the minimal (no-LLM) card path so each card gets a unique remark
65
- * instead of the same static text.
66
- *
67
- * Extracts domain keywords (e.g. "AI", "design", "machine learning") from the
68
- * reasoning and frames them in a short template like "Shared interest in AI and design."
69
- *
70
- * This is a regex-based heuristic — an alternative is OpportunityPresenter.presentCard()
71
- * which generates narratorRemark via LLM with much higher quality.
72
- *
73
- * @param reasoning - Raw interpretation.reasoning text.
74
- * @param counterpartName - Display name of the counterpart (stripped from output).
75
- * @param viewerName - Optional display name of the viewer (stripped from output).
76
- * @returns A short remark (max ~80 chars) suitable for the narrator chip. Never truncated with "...".
77
- */
78
- export declare function narratorRemarkFromReasoning(reasoning: string, counterpartName: string, viewerName?: string): string;
79
21
  /** Cache namespace for opportunity presentation copy. Bump to invalidate copy safety changes. */
80
22
  export declare const OPPORTUNITY_PRESENTATION_CACHE_VERSION = "v2";
81
23
  export declare function buildRadarCardPresentationCacheKey(opportunityId: string, status: string, viewerId: string, focusedViewerIntentId?: string): string;
82
24
  export declare function buildApiChatCardPresentationCacheKey(opportunityId: string, viewerId: string): string;
83
- /**
84
- * Shared safe-presentation primitive for all user-facing opportunity surfaces.
85
- *
86
- * Historically every surface (radar, list/discover cards, minimal chat
87
- * cards, notification emails/Telegram, chat context, delivery cards) invented
88
- * its own fallback chain for the case where genuine LLM presenter output is
89
- * unavailable — some sliced raw `interpretation.reasoning` with no
90
- * sanitization at all. This module is the single standard:
91
- *
92
- * raw reasoning
93
- * → whitespace-normalize
94
- * → viewer-centric rewrite (incl. UUID stripping)
95
- * → boundary-aware truncation
96
- * → per-surface empty-text default
97
- *
98
- * Surfaces choose *policy* (send a sanitized fallback vs skip entirely) via
99
- * `allowFallback`; they no longer choose (or forget) sanitization steps.
100
- *
101
- * See `packages/protocol/s./opportunity/AGENTS.md` for the review checklist this
102
- * module exists to satisfy.
103
- */
104
- /** Default max length for fallback summaries (matches presenter internal fallback). */
105
- export declare const SAFE_FALLBACK_MAX_CHARS = 300;
106
- /** Default copy when no reasoning text is available at all. */
107
- export declare const DEFAULT_EMPTY_FALLBACK_TEXT = "A promising connection.";
108
- /** Default headline for fallback presentations (matches presenter internal fallback). */
109
- export declare const DEFAULT_FALLBACK_HEADLINE = "A promising connection";
110
- /** Default CTA for fallback presentations (matches presenter internal fallback). */
111
- export declare const DEFAULT_FALLBACK_ACTION = "Take a look and decide whether to reach out.";
112
- export interface SafeFallbackOptions {
113
- /** Display name of the counterpart shown on the card (enables viewer-centric rewrite). */
114
- counterpartName?: string;
115
- /** Display name of the viewer; sentences describing the viewer are skipped/rewritten to "you". */
116
- viewerName?: string;
117
- /** Max output length (boundary-aware). Default {@link SAFE_FALLBACK_MAX_CHARS}. */
118
- maxChars?: number;
119
- /** Copy returned when reasoning is empty/blank. Default {@link DEFAULT_EMPTY_FALLBACK_TEXT}. */
120
- emptyText?: string;
121
- }
122
- /**
123
- * Produce safe user-facing fallback copy from raw match reasoning.
124
- *
125
- * This is the ONE sanitization standard: UUID stripping,
126
- * stripping, and viewer-centric rewrite (via {@link viewerCentricCardSummary}),
127
- * followed by whitespace normalization and boundary-aware truncation (via
128
- * {@link truncateAtBoundary}). Never returns raw reasoning verbatim beyond
129
- * these guarantees, and never returns an empty string.
130
- *
131
- * @param rawReasoning - Raw `interpretation.reasoning` / `matchReason` text (may be null/undefined).
132
- * @param opts - Per-surface knobs (names for rewrite, max length, empty-text copy).
133
- */
134
- export declare function safeFallbackSummary(rawReasoning: string | null | undefined, opts?: SafeFallbackOptions): string;
135
- /** Minimal presenter-output shape the primitive inspects (subset of CardPresentationResult). */
136
- export interface SafePresentationCandidate {
137
- headline?: string;
138
- personalizedSummary?: string;
139
- suggestedAction?: string;
140
- /** Set by OpportunityPresenter when its LLM call failed and it returned fallback-shaped copy. */
141
- isFallback?: boolean;
142
- }
143
- /** Opportunity-ish source object accepted by {@link getSafePresentationOrSkip}. */
144
- export interface SafePresentationSource {
145
- /** Presenter output attached to the record, when available. */
146
- homeCardPresentation?: SafePresentationCandidate | null;
147
- /** Pre-truncated raw reasoning carried on discovery/list card data. */
148
- matchReason?: string | null;
149
- /** Full opportunity interpretation, when the caller holds the record. */
150
- interpretation?: {
151
- reasoning?: string | null;
152
- } | null;
153
- }
154
- export interface SafePresentationOptions extends SafeFallbackOptions {
155
- /**
156
- * Policy switch: when false, return null instead of fallback copy so the
157
- * surface can skip rendering entirely (e.g. scheduled digests where sending
158
- * degraded copy is worse than sending nothing). Default true.
159
- */
160
- allowFallback?: boolean;
161
- }
162
- /** Resolved safe presentation for a surface to render. */
163
- export interface SafePresentation {
164
- headline: string;
165
- summary: string;
166
- suggestedAction: string;
167
- /** True when copy was derived from raw reasoning rather than genuine LLM presenter output. */
168
- isFallback: boolean;
169
- }
170
- /**
171
- * Resolve the safe user-facing presentation for an opportunity, or signal skip.
172
- *
173
- * Resolution order:
174
- * 1. Genuine presenter output (`homeCardPresentation` present, non-empty, and
175
- * NOT tagged `isFallback` by the presenter) — claim-validated before return.
176
- * 2. Otherwise, if `allowFallback` (default true): sanitized fallback copy
177
- * built from `matchReason` / `interpretation.reasoning` via
178
- * {@link safeFallbackSummary}.
179
- * 3. Otherwise `null` — the surface must skip this opportunity.
180
- *
181
- * Raw `interpretation.reasoning` / `matchReason` never reaches the caller
182
- * unsanitized through this function.
183
- */
184
- export declare function getSafePresentationOrSkip(source: SafePresentationSource, opts?: SafePresentationOptions): SafePresentation | null;
185
25
  /**
186
26
  * Opportunity Presenter Agent
187
27
  *
@@ -201,22 +41,17 @@ declare const PresentationSchema: z.ZodObject<{
201
41
  suggestedAction: z.ZodString;
202
42
  greeting: z.ZodString;
203
43
  }, "strip", z.ZodTypeAny, {
204
- personalizedSummary: string;
205
44
  headline: string;
45
+ personalizedSummary: string;
206
46
  suggestedAction: string;
207
47
  greeting: string;
208
48
  }, {
209
- personalizedSummary: string;
210
49
  headline: string;
50
+ personalizedSummary: string;
211
51
  suggestedAction: string;
212
52
  greeting: string;
213
53
  }>;
214
- export type OpportunityPresentationResult = z.infer<typeof PresentationSchema> & {
215
- /** True when any output field used resilience fallback copy. */
216
- isFallback?: boolean;
217
- /** Diagnostic category; never changes production fallback policy. */
218
- fallbackReason?: "timeout" | "error" | "sanitization";
219
- };
54
+ export type OpportunityPresentationResult = z.infer<typeof PresentationSchema>;
220
55
  /** Input for card presenter call; extends PresenterInput with optional mutual intent count. */
221
56
  export interface CardPresenterInput extends PresenterInput {
222
57
  /** Number of overlapping intents (for generating mutualIntentsLabel). */
@@ -238,30 +73,22 @@ export declare const CardLLMSchema: z.ZodObject<{
238
73
  mutualIntentsLabel: z.ZodString;
239
74
  greeting: z.ZodString;
240
75
  }, "strip", z.ZodTypeAny, {
241
- personalizedSummary: string;
242
76
  headline: string;
77
+ personalizedSummary: string;
243
78
  suggestedAction: string;
244
79
  greeting: string;
245
80
  narratorRemark: string;
246
81
  mutualIntentsLabel: string;
247
82
  }, {
248
- personalizedSummary: string;
249
83
  headline: string;
84
+ personalizedSummary: string;
250
85
  suggestedAction: string;
251
86
  greeting: string;
252
87
  narratorRemark: string;
253
88
  mutualIntentsLabel: string;
254
89
  }>;
255
90
  /** LLM-generated result from presentCard (callers append button labels from opportunity.constants). */
256
- export type CardLLMResult = z.infer<typeof CardLLMSchema> & {
257
- /**
258
- * True when the LLM call failed and this is fallback-shaped copy built from
259
- * raw match reasoning. Callers with strict quality requirements (digests,
260
- * long-lived caches) should check this before sending/persisting — fallback
261
- * output is otherwise indistinguishable from genuine LLM output.
262
- */
263
- isFallback?: boolean;
264
- };
91
+ export type CardLLMResult = z.infer<typeof CardLLMSchema>;
265
92
  /** Full card display contract including hardcoded button labels (assembled by callers). */
266
93
  export type CardPresentationResult = CardLLMResult & {
267
94
  primaryActionLabel: string;
@@ -286,6 +113,11 @@ export declare class OpportunityPresenter {
286
113
  private invokeWithTimeout;
287
114
  /**
288
115
  * Generate personalized presentation for a single opportunity.
116
+ *
117
+ * @param input - Pre-assembled presenter context.
118
+ * @param options - Optional abort signal.
119
+ * @returns The LLM-generated presentation.
120
+ * @throws When the LLM call fails, times out, or returns invalid output.
289
121
  */
290
122
  present(input: PresenterInput, options?: {
291
123
  signal?: AbortSignal;
@@ -297,6 +129,10 @@ export declare class OpportunityPresenter {
297
129
  * When `negotiationContext.status === 'negotiating'`, returns a templated
298
130
  * chip synchronously without invoking the LLM — the card just reflects
299
131
  * "negotiation in progress" at that point.
132
+ *
133
+ * @param input - Pre-assembled card presenter context.
134
+ * @returns The LLM-generated card copy.
135
+ * @throws When the LLM call fails, times out, or returns invalid output.
300
136
  */
301
137
  presentCard(input: CardPresenterInput): Promise<CardLLMResult>;
302
138
  /**