@indexnetwork/protocol 65.0.0 → 66.0.0-rc.609.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 (26) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/IMPLEMENTATION.md +11 -12
  3. package/STABILITY.md +0 -1
  4. package/dist/index.d.ts +5 -7
  5. package/dist/index.js +3 -6
  6. package/dist/internal/opportunities/opportunity.cards.d.ts +78 -0
  7. package/dist/internal/opportunities/opportunity.cards.js +165 -0
  8. package/dist/internal/opportunities/opportunity.graph.modes.d.ts +0 -1
  9. package/dist/internal/opportunities/opportunity.graph.modes.js +0 -5
  10. package/dist/internal/opportunities/opportunity.labels.d.ts +0 -2
  11. package/dist/internal/opportunities/opportunity.labels.js +0 -2
  12. package/dist/internal/opportunities/opportunity.presentation.d.ts +21 -185
  13. package/dist/internal/opportunities/opportunity.presentation.js +35 -651
  14. package/dist/internal/opportunities/opportunity.utils.d.ts +0 -42
  15. package/dist/internal/opportunities/opportunity.utils.js +0 -102
  16. package/dist/platform/database/capabilities.d.ts +2 -2
  17. package/dist/platform/database/entities.d.ts +1 -1
  18. package/package.json +1 -1
  19. package/dist/internal/opportunities/opportunity.feed-selection.d.ts +0 -24
  20. package/dist/internal/opportunities/opportunity.feed-selection.js +0 -40
  21. package/dist/internal/opportunities/radar/radar.graph.d.ts +0 -230
  22. package/dist/internal/opportunities/radar/radar.graph.js +0 -537
  23. package/dist/internal/opportunities/radar/radar.state.d.ts +0 -88
  24. package/dist/internal/opportunities/radar/radar.state.js +0 -87
  25. package/dist/internal/shared/utils/claim-safety.d.ts +0 -24
  26. package/dist/internal/shared/utils/claim-safety.js +0 -88
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
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`.
10
+ - Remove `RadarGraphFactory` and the radar graph. Use `listOpportunityCards(deps, { viewerId, statuses, networkId?, intentId?, limit, noCache?, skeleton? })` for a presented card list and `presentOpportunityCard(deps, opportunity, viewerId, { intentId?, skeleton? })` for one card; both return `OpportunityCard`. `statuses` is required, and the list is newest first with one card per counterpart. A card whose presenter call fails is dropped from the list; `presentOpportunityCard` throws.
11
+ - Rename `RadarGraphDatabase` to `OpportunityCardsDatabase` and `buildRadarCardPresentationCacheKey` to `buildOpportunityCardCacheKey` (keys now start with `card:`). Remove `RADAR_SOFT_TARGETS` and `selectByComposition`.
12
+
3
13
  ## 65.0.0
4
14
 
5
15
  ### Breaking changes
package/IMPLEMENTATION.md CHANGED
@@ -128,26 +128,25 @@ negotiation state and presentation contracts.
128
128
  Optional capabilities default to a
129
129
  degraded-but-functional mode when omitted.
130
130
 
131
- ## Graphs
131
+ ## Opportunity cards
132
132
 
133
- A `*GraphFactory` class is exported for each workflow:
133
+ Presented opportunity cards are plain async functions over
134
+ `{ database: OpportunityCardsDatabase, cache: OpportunityCache, presenter: OpportunityPresenter }`:
134
135
 
135
136
  ```typescript
136
137
  import {
137
- RadarGraphFactory,
138
+ listOpportunityCards,
139
+ presentOpportunityCard,
138
140
  } from "@indexnetwork/protocol";
139
141
  ```
140
142
 
141
- Each factory takes its typed dependencies in the constructor and exposes a
142
- `.createGraph()` method that returns a compiled LangGraph ready for `.invoke()`.
143
+ `listOpportunityCards` returns the viewer's opportunities in the requested
144
+ statuses, newest first with one card per counterpart, serving cached cards and
145
+ dropping any whose presenter call fails. `presentOpportunityCard` presents one
146
+ opportunity and throws when the presenter fails.
143
147
 
144
- The intent and community graphs are the exceptions: they are reached through the
145
- `Intents` and `Networks` module classes rather than factories of their own (see
146
- [Intents](#intents) and [Networks](#networks) below).
147
-
148
- | Factory | Workflow |
149
- |---|---|
150
- | `RadarGraphFactory` | Build the radar view: flat presenter-card list, optionally intent-scoped |
148
+ The intent and community graphs are reached through the `Intents` and `Networks`
149
+ module classes (see [Intents](#intents) and [Networks](#networks) below).
151
150
 
152
151
  ## On-demand discovery
153
152
 
package/STABILITY.md CHANGED
@@ -46,7 +46,6 @@ Covered by SemVer below. Breaking changes require a **major** bump.
46
46
  | **Public API** | Model config helpers, request scope helpers (`deriveAllowedNetworkIds`, `deriveDiscoveryNetworkIds`), `requestContext`. |
47
47
  | **Interfaces** | Every port you implement to inject infrastructure (databases, embedder, cache, scraper, integration, …). |
48
48
  | **Shared schemas** | Zod schemas + inferred types that cross the boundary (underspecification, identity, network-assignment, chat-context, …). |
49
- | **Graph factories** | `*GraphFactory` classes (for example, `RadarGraphFactory`). |
50
49
  | **Intents** | `Intents` — the whole signal capability as one class (lifecycle graph, verification, preparation) plus `IntentsDeps` and the `Prepare*` / `RecoveryField*` types. Replaced the six separate intent exports in 18.0.0. |
51
50
  | **Agents** | Structured LLM agents (`OpportunityEvaluator`, …). |
52
51
 
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { requestContext, setRequestContextStore } from "./internal/shared/observ
6
6
  export { setLoggerFactory } from "./internal/shared/observability/log.js";
7
7
  export { setTimingWrapper } from "./internal/shared/observability/performance.js";
8
8
  export type { Cache, CacheOptions, OpportunityCache } from "./platform/discovery/cache.js";
9
- export type { CompositeDatabase, UserDatabase, SystemDatabase, OpportunityDatabase, OpportunityControllerDatabase, RadarGraphDatabase, IntentGraphDatabase, Opportunity, OpportunityActor, OpportunityStatus, AssignmentNetworkMembership, IntentNetworkFinalAssignmentResult, CreateOpportunityData, IntentLifecycleStatus, TransitionLifecycleResult, NegotiationContextDatabase, NegotiationContextRecord, NegotiationContextOutcome, NegotiationContextTurn, } from "./platform/database.js";
9
+ export type { CompositeDatabase, UserDatabase, SystemDatabase, OpportunityDatabase, OpportunityControllerDatabase, OpportunityCardsDatabase, IntentGraphDatabase, Opportunity, OpportunityActor, OpportunityStatus, AssignmentNetworkMembership, IntentNetworkFinalAssignmentResult, CreateOpportunityData, IntentLifecycleStatus, TransitionLifecycleResult, NegotiationContextDatabase, NegotiationContextRecord, NegotiationContextOutcome, NegotiationContextTurn, } from "./platform/database.js";
10
10
  export type { Embedder, VectorStoreOption, VectorSearchResult } from "./platform/discovery/embedder.js";
11
11
  export type { IntentFollowUp } from "./platform/runtime/follow-up.js";
12
12
  export type { Scraper } from "./platform/discovery/scraper.js";
@@ -43,14 +43,12 @@ 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
- export { buildApiChatCardPresentationCacheKey, buildRadarCardPresentationCacheKey, } from "./internal/opportunities/opportunity.presentation.js";
49
- export { presentOpportunity, stripUuids, truncateAtBoundary, } from "./internal/opportunities/opportunity.presentation.js";
47
+ export { buildApiChatCardPresentationCacheKey, buildOpportunityCardCacheKey, } 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
- export { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, RADAR_SOFT_TARGETS, selectByComposition, validateOpportunityActors, } from "./internal/opportunities/opportunity.utils.js";
53
- export { RadarGraphFactory, } from "./internal/opportunities/radar/radar.graph.js";
49
+ export { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, validateOpportunityActors, } from "./internal/opportunities/opportunity.utils.js";
50
+ export { listOpportunityCards, presentOpportunityCard, } from "./internal/opportunities/opportunity.cards.js";
51
+ export type { OpportunityCard, } from "./internal/opportunities/opportunity.cards.js";
54
52
  export { readOpportunities } from './internal/opportunities/opportunity.graph.modes.js';
55
53
  export { admitOpportunityEvent, projectOpportunity } from './internal/opportunities/opportunity.events.js';
56
54
  export type { OpportunityAdmission, OpportunityEventType, OpportunityLogEvent, OpportunityProjection, OpportunityProjectionStatus } from './internal/opportunities/opportunity.events.js';
package/dist/index.js CHANGED
@@ -47,13 +47,10 @@ 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
- 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
- export { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, RADAR_SOFT_TARGETS, selectByComposition, validateOpportunityActors, } from "./internal/opportunities/opportunity.utils.js";
56
- export { RadarGraphFactory, } from "./internal/opportunities/radar/radar.graph.js";
51
+ export { buildApiChatCardPresentationCacheKey, buildOpportunityCardCacheKey, } from "./internal/opportunities/opportunity.presentation.js";
52
+ export { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, validateOpportunityActors, } from "./internal/opportunities/opportunity.utils.js";
53
+ export { listOpportunityCards, presentOpportunityCard, } from "./internal/opportunities/opportunity.cards.js";
57
54
  export { readOpportunities } from './internal/opportunities/opportunity.graph.modes.js';
58
55
  export { admitOpportunityEvent, projectOpportunity } from './internal/opportunities/opportunity.events.js';
59
56
  export { resolveDiscoveryNetworkScope, renderDiscoveryNetworkContext } from './protocol/discovery.rules.js';
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Opportunity cards: presenter-texted cards for a viewer, as a list or one at
3
+ * a time. The list loads, filters, and dedupes the viewer's opportunities,
4
+ * serves cached cards, and presents the misses through OpportunityPresenter.
5
+ */
6
+ import type { Opportunity, OpportunityCardsDatabase, OpportunityStatus } from '../../platform/database.js';
7
+ import type { OpportunityCache } from '../../platform/discovery/cache.js';
8
+ import { OpportunityPresenter } from './opportunity.presentation.js';
9
+ /** One opportunity with its presenter-driven display contract. */
10
+ export interface OpportunityCard {
11
+ opportunityId: string;
12
+ createdAt?: string;
13
+ /** Lifecycle status of the underlying opportunity at render time. */
14
+ status?: OpportunityStatus;
15
+ userId: string;
16
+ name: string;
17
+ avatar: string | null;
18
+ mainText: string;
19
+ cta: string;
20
+ headline?: string;
21
+ primaryActionLabel: string;
22
+ secondaryActionLabel: string;
23
+ mutualIntentsLabel: string;
24
+ narratorChip?: {
25
+ name: string;
26
+ text: string;
27
+ avatar?: string | null;
28
+ userId?: string;
29
+ };
30
+ /** Viewer's role in this opportunity (e.g. 'party', 'agent', 'patient', 'peer'). */
31
+ viewerRole?: string;
32
+ /**
33
+ * True for a skeleton card: identity fields are real but mainText/cta are
34
+ * empty because the presenter was skipped. Never cached.
35
+ */
36
+ presentationPending?: boolean;
37
+ }
38
+ /** Everything card presentation reaches for. */
39
+ export interface OpportunityCardsDeps {
40
+ database: OpportunityCardsDatabase;
41
+ cache: OpportunityCache;
42
+ presenter: OpportunityPresenter;
43
+ }
44
+ /**
45
+ * Present one opportunity as a card for the viewer.
46
+ *
47
+ * @param deps - Database, cache, and presenter.
48
+ * @param opportunity - The opportunity to present.
49
+ * @param viewerId - The viewing user.
50
+ * @param options - `intentId` focuses viewer context on one intent; `skeleton` skips the presenter.
51
+ * @returns The card, or null when the counterpart's name cannot be resolved.
52
+ * @throws When the presenter fails.
53
+ */
54
+ export declare function presentOpportunityCard(deps: OpportunityCardsDeps, opportunity: Opportunity, viewerId: string, options?: {
55
+ intentId?: string;
56
+ skeleton?: boolean;
57
+ }): Promise<OpportunityCard | null>;
58
+ /**
59
+ * List the viewer's opportunities in the given statuses as presented cards,
60
+ * newest first, one card per counterpart. Cards whose presentation fails are
61
+ * dropped.
62
+ *
63
+ * @param deps - Database, cache, and presenter.
64
+ * @param input - Viewer, statuses, optional network/intent scope, limit, cache bypass, skeleton mode.
65
+ * @returns The cards and the number of opportunities selected.
66
+ */
67
+ export declare function listOpportunityCards(deps: OpportunityCardsDeps, input: {
68
+ viewerId: string;
69
+ statuses: OpportunityStatus[];
70
+ networkId?: string;
71
+ intentId?: string;
72
+ limit: number;
73
+ noCache?: boolean;
74
+ skeleton?: boolean;
75
+ }): Promise<{
76
+ cards: OpportunityCard[];
77
+ totalOpportunities: number;
78
+ }>;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Opportunity cards: presenter-texted cards for a viewer, as a list or one at
3
+ * a time. The list loads, filters, and dedupes the viewer's opportunities,
4
+ * serves cached cards, and presents the misses through OpportunityPresenter.
5
+ */
6
+ import { protocolLogger } from '../shared/observability/protocol.logger.js';
7
+ import { buildOpportunityCardCacheKey, gatherPresenterContext } from './opportunity.presentation.js';
8
+ import { loadNegotiationContext } from './negotiation-context.loader.js';
9
+ import { canUserSeeOpportunity, isActionableForViewer } from './opportunity.utils.js';
10
+ import { getPrimaryActionLabel, SECONDARY_ACTION_LABEL } from './opportunity.labels.js';
11
+ const logger = protocolLogger('OpportunityCards');
12
+ const PRESENTATION_CONCURRENCY = 50;
13
+ const CARD_CACHE_TTL = 24 * 60 * 60;
14
+ const ROLE_PRIORITY = new Map([
15
+ ['patient', 0],
16
+ ['party', 1],
17
+ ['agent', 2],
18
+ ['peer', 3],
19
+ ]);
20
+ /** Prefer direct counterpart roles, then a stable sort by user id. */
21
+ function pickDisplayCounterpartActor(opportunity, viewerId) {
22
+ const candidates = opportunity.actors.filter((actor) => actor.userId !== viewerId);
23
+ return [...candidates].sort((a, b) => {
24
+ const aPriority = ROLE_PRIORITY.get(a.role) ?? 99;
25
+ const bPriority = ROLE_PRIORITY.get(b.role) ?? 99;
26
+ if (aPriority !== bPriority)
27
+ return aPriority - bPriority;
28
+ return a.userId.localeCompare(b.userId);
29
+ })[0] ?? null;
30
+ }
31
+ function counterpartUserIds(opportunity, viewerId) {
32
+ return new Set(opportunity.actors.map((a) => a.userId).filter((id) => id && id !== viewerId));
33
+ }
34
+ /**
35
+ * Present one opportunity as a card for the viewer.
36
+ *
37
+ * @param deps - Database, cache, and presenter.
38
+ * @param opportunity - The opportunity to present.
39
+ * @param viewerId - The viewing user.
40
+ * @param options - `intentId` focuses viewer context on one intent; `skeleton` skips the presenter.
41
+ * @returns The card, or null when the counterpart's name cannot be resolved.
42
+ * @throws When the presenter fails.
43
+ */
44
+ export async function presentOpportunityCard(deps, opportunity, viewerId, options = {}) {
45
+ const { database } = deps;
46
+ const viewerRole = opportunity.actors.find((a) => a.userId === viewerId)?.role ?? 'party';
47
+ const counterpart = pickDisplayCounterpartActor(opportunity, viewerId);
48
+ if (!counterpart)
49
+ return null;
50
+ const user = await database.getUser(counterpart.userId).catch(() => null);
51
+ let name = user?.name?.trim();
52
+ if (!name) {
53
+ const profile = await database.getProfile(counterpart.userId).catch(() => null);
54
+ name = profile?.identity?.name?.trim();
55
+ }
56
+ if (!name)
57
+ return null;
58
+ const identity = {
59
+ opportunityId: opportunity.id,
60
+ createdAt: opportunity.createdAt.toISOString(),
61
+ status: opportunity.status,
62
+ userId: counterpart.userId,
63
+ name,
64
+ avatar: user?.avatar ?? null,
65
+ primaryActionLabel: getPrimaryActionLabel(viewerRole),
66
+ secondaryActionLabel: SECONDARY_ACTION_LABEL,
67
+ viewerRole,
68
+ };
69
+ if (options.skeleton) {
70
+ return { ...identity, mainText: '', cta: '', mutualIntentsLabel: 'Shared interests', presentationPending: true };
71
+ }
72
+ const [context, negotiationContext] = await Promise.all([
73
+ gatherPresenterContext(database, opportunity, viewerId, counterpart.userId, options.intentId),
74
+ loadNegotiationContext(database, opportunity.id, opportunity.status, viewerId),
75
+ ]);
76
+ const presentation = await deps.presenter.presentCard({
77
+ ...context,
78
+ opportunityStatus: opportunity.status,
79
+ ...(negotiationContext ? { negotiationContext } : {}),
80
+ });
81
+ return {
82
+ ...identity,
83
+ mainText: presentation.personalizedSummary,
84
+ cta: presentation.suggestedAction,
85
+ headline: presentation.headline,
86
+ mutualIntentsLabel: presentation.mutualIntentsLabel,
87
+ narratorChip: { name: 'Index', text: presentation.narratorRemark },
88
+ };
89
+ }
90
+ /**
91
+ * List the viewer's opportunities in the given statuses as presented cards,
92
+ * newest first, one card per counterpart. Cards whose presentation fails are
93
+ * dropped.
94
+ *
95
+ * @param deps - Database, cache, and presenter.
96
+ * @param input - Viewer, statuses, optional network/intent scope, limit, cache bypass, skeleton mode.
97
+ * @returns The cards and the number of opportunities selected.
98
+ */
99
+ export async function listOpportunityCards(deps, input) {
100
+ const { viewerId, intentId } = input;
101
+ const raw = await deps.database.getOpportunitiesForUser(viewerId, {
102
+ limit: Math.min(150, Math.max(50, input.limit * 3)),
103
+ statuses: input.statuses,
104
+ ...(input.networkId ? { networkId: input.networkId } : {}),
105
+ ...(intentId ? { scopeType: 'intent', scopeId: intentId } : {}),
106
+ });
107
+ const requested = new Set(input.statuses);
108
+ const visible = raw
109
+ .filter((opp) => requested.has(opp.status) && canUserSeeOpportunity(opp.actors, opp.status, viewerId))
110
+ .filter((opp) => opp.status !== 'pending'
111
+ || isActionableForViewer(opp.actors, opp.status, viewerId, opp.committedActorIds ?? []))
112
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
113
+ const seen = new Set();
114
+ const opportunities = visible.filter((opp) => {
115
+ const ids = counterpartUserIds(opp, viewerId);
116
+ if ([...ids].some((id) => seen.has(id)))
117
+ return false;
118
+ for (const id of ids)
119
+ seen.add(id);
120
+ return true;
121
+ }).slice(0, input.limit);
122
+ const cacheable = (opp) => opp.status !== 'negotiating';
123
+ const keyFor = (opp) => buildOpportunityCardCacheKey(opp.id, opp.status, viewerId, intentId);
124
+ const cacheableOpps = input.noCache ? [] : opportunities.filter(cacheable);
125
+ const cached = new Map();
126
+ if (cacheableOpps.length > 0) {
127
+ try {
128
+ const hits = await deps.cache.mget(cacheableOpps.map(keyFor));
129
+ cacheableOpps.forEach((opp, i) => {
130
+ const hit = hits[i];
131
+ if (hit)
132
+ cached.set(opp.id, { ...hit, status: opp.status });
133
+ });
134
+ }
135
+ catch (error) {
136
+ logger.warn('card cache read failed, presenting all', { error });
137
+ }
138
+ }
139
+ const cards = [];
140
+ for (let i = 0; i < opportunities.length; i += PRESENTATION_CONCURRENCY) {
141
+ const chunk = opportunities.slice(i, i + PRESENTATION_CONCURRENCY);
142
+ cards.push(...await Promise.all(chunk.map(async (opp) => {
143
+ const hit = cached.get(opp.id);
144
+ if (hit)
145
+ return hit;
146
+ try {
147
+ const card = await presentOpportunityCard(deps, opp, viewerId, { intentId, skeleton: input.skeleton });
148
+ if (card && !card.presentationPending && cacheable(opp)) {
149
+ await deps.cache.set(keyFor(opp), card, { ttl: CARD_CACHE_TTL }).catch((error) => {
150
+ logger.warn('card cache write failed', { opportunityId: opp.id, error });
151
+ });
152
+ }
153
+ return card;
154
+ }
155
+ catch (error) {
156
+ logger.warn('presenter failed, dropping card', { opportunityId: opp.id, error });
157
+ return null;
158
+ }
159
+ })));
160
+ }
161
+ return {
162
+ cards: cards.filter((card) => card !== null),
163
+ totalOpportunities: opportunities.length,
164
+ };
165
+ }
@@ -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";