@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 +8 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -3
- package/dist/internal/opportunities/opportunity.graph.modes.d.ts +0 -1
- package/dist/internal/opportunities/opportunity.graph.modes.js +0 -5
- package/dist/internal/opportunities/opportunity.labels.d.ts +0 -2
- package/dist/internal/opportunities/opportunity.labels.js +0 -2
- package/dist/internal/opportunities/opportunity.presentation.d.ts +19 -183
- package/dist/internal/opportunities/opportunity.presentation.js +32 -648
- package/dist/internal/opportunities/radar/radar.graph.d.ts +2 -2
- package/dist/internal/opportunities/radar/radar.graph.js +4 -35
- package/dist/internal/opportunities/radar/radar.state.d.ts +1 -3
- package/package.json +1 -1
- package/dist/internal/shared/utils/claim-safety.d.ts +0 -24
- package/dist/internal/shared/utils/claim-safety.js +0 -88
|
@@ -50,8 +50,8 @@ export type RadarGraphInvokeResult = {
|
|
|
50
50
|
export declare const DEFAULT_RADAR_STATUSES: OpportunityStatus[];
|
|
51
51
|
/** Full status enumeration. Pass this to `RadarGraphInvokeInput.statuses` to restore pre-Issue-3 (unfiltered) behavior. */
|
|
52
52
|
export declare const ALL_OPPORTUNITY_STATUSES: OpportunityStatus[];
|
|
53
|
-
/** Pure cache policy for presenter cards
|
|
54
|
-
export declare function isRadarPresentationCacheable(card: Pick<RadarCardItem, 'presentationPending' | '
|
|
53
|
+
/** Pure cache policy for presenter cards. */
|
|
54
|
+
export declare function isRadarPresentationCacheable(card: Pick<RadarCardItem, 'presentationPending' | 'name'>, status: OpportunityStatus | undefined): boolean;
|
|
55
55
|
/**
|
|
56
56
|
* Strip leading narrator name from remark when the UI already prepends "Name: " to the chip.
|
|
57
57
|
* Avoids duplication like "Yankı Ekin Yüksel: Yankı Ekin Yüksel introduced you two..."
|
|
@@ -16,7 +16,6 @@ import { OpportunityPresenter, gatherPresenterContext } from '../opportunity.pre
|
|
|
16
16
|
import { loadNegotiationContext } from '../negotiation-context.loader.js';
|
|
17
17
|
import { canUserSeeOpportunity, isActionableForViewer, selectByComposition } from '../opportunity.utils.js';
|
|
18
18
|
import { getPrimaryActionLabel, SECONDARY_ACTION_LABEL } from '../opportunity.labels.js';
|
|
19
|
-
import { safeFallbackSummary } from '../opportunity.presentation.js';
|
|
20
19
|
import { buildRadarCardPresentationCacheKey } from '../opportunity.presentation.js';
|
|
21
20
|
import { protocolLogger } from '../../shared/observability/protocol.logger.js';
|
|
22
21
|
import { timed } from '../../shared/observability/performance.js';
|
|
@@ -41,14 +40,12 @@ const OPPORTUNITY_STATUS_REGISTRY = {
|
|
|
41
40
|
/** Full status enumeration. Pass this to `RadarGraphInvokeInput.statuses` to restore pre-Issue-3 (unfiltered) behavior. */
|
|
42
41
|
export const ALL_OPPORTUNITY_STATUSES = Object.keys(OPPORTUNITY_STATUS_REGISTRY);
|
|
43
42
|
const PRESENTATION_CONCURRENCY = 50;
|
|
44
|
-
const MAX_REASONING_SNIPPET_LENGTH = 240;
|
|
45
43
|
const RADAR_CACHE_TTL = 24 * 60 * 60; // 24 hours in seconds
|
|
46
|
-
/** Pure cache policy for presenter cards
|
|
44
|
+
/** Pure cache policy for presenter cards. */
|
|
47
45
|
export function isRadarPresentationCacheable(card, status) {
|
|
48
46
|
return Boolean(status &&
|
|
49
47
|
status !== 'negotiating' &&
|
|
50
48
|
!card.presentationPending &&
|
|
51
|
-
!card._presentationFallback &&
|
|
52
49
|
card.name &&
|
|
53
50
|
card.name !== 'Unknown');
|
|
54
51
|
}
|
|
@@ -383,15 +380,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
383
380
|
return null;
|
|
384
381
|
}
|
|
385
382
|
const userAvatar = otherUser?.avatar ?? null;
|
|
386
|
-
// Shared sanitization standard (UUID strip, viewer-centric rewrite,
|
|
387
|
-
// boundary truncation) — raw reasoning must never render verbatim.
|
|
388
|
-
const reasoningSnippet = safeFallbackSummary(typeof opportunity.interpretation?.reasoning === 'string'
|
|
389
|
-
? opportunity.interpretation.reasoning
|
|
390
|
-
: '', {
|
|
391
|
-
counterpartName: userName !== 'Unknown' ? userName : undefined,
|
|
392
|
-
maxChars: MAX_REASONING_SNIPPET_LENGTH,
|
|
393
|
-
emptyText: 'A promising connection.',
|
|
394
|
-
});
|
|
395
383
|
// Skeleton presentation: return an identity-only card without the
|
|
396
384
|
// deps.presenter LLM or negotiation-context load. Name resolution and
|
|
397
385
|
// the unresolvable-counterpart drop above still apply, so the card
|
|
@@ -413,22 +401,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
413
401
|
_cardIndex: cardIndex,
|
|
414
402
|
};
|
|
415
403
|
}
|
|
416
|
-
const fallbackCard = (outcomeReasoning) => ({
|
|
417
|
-
opportunityId: opportunity.id,
|
|
418
|
-
status: opportunity.status,
|
|
419
|
-
userId: otherActor?.userId ?? '',
|
|
420
|
-
name: userName,
|
|
421
|
-
avatar: userAvatar,
|
|
422
|
-
mainText: outcomeReasoning ?? reasoningSnippet,
|
|
423
|
-
cta: 'Take a look and decide whether to reach out.',
|
|
424
|
-
primaryActionLabel: getPrimaryActionLabel(viewerRole),
|
|
425
|
-
secondaryActionLabel: SECONDARY_ACTION_LABEL,
|
|
426
|
-
mutualIntentsLabel: 'Shared interests',
|
|
427
|
-
narratorChip: { name: 'Index', text: 'Worth a look.' },
|
|
428
|
-
viewerRole,
|
|
429
|
-
_presentationFallback: true,
|
|
430
|
-
_cardIndex: cardIndex,
|
|
431
|
-
});
|
|
432
404
|
try {
|
|
433
405
|
const [ctx, negotiationContext] = await Promise.all([
|
|
434
406
|
gatherPresenterContext(db, opportunity, state.userId, otherActor?.userId, state.scopeId),
|
|
@@ -447,9 +419,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
447
419
|
const _presenterDuration = Date.now() - presenterStart;
|
|
448
420
|
agentTimingsAccum.push({ name: 'opportunity.presenter', durationMs: _presenterDuration });
|
|
449
421
|
_traceEmitterPresenter?.({ type: "agent_end", name: "opportunity-presenter", durationMs: _presenterDuration, summary: `Presented: ${userName}` });
|
|
450
|
-
if (presentation.isFallback) {
|
|
451
|
-
return fallbackCard();
|
|
452
|
-
}
|
|
453
422
|
// Every card is system-discovered now: one narrator.
|
|
454
423
|
const narratorChip = { name: 'Index', text: presentation.narratorRemark };
|
|
455
424
|
return {
|
|
@@ -471,7 +440,7 @@ export async function generateCardTextNode(state, deps) {
|
|
|
471
440
|
}
|
|
472
441
|
catch (e) {
|
|
473
442
|
logger.warn('RadarGraph presenter failed for opportunity', { opportunityId: opportunity.id, error: e });
|
|
474
|
-
return
|
|
443
|
+
return null;
|
|
475
444
|
}
|
|
476
445
|
}));
|
|
477
446
|
cards.push(...chunkCards.filter((c) => c !== null));
|
|
@@ -493,7 +462,7 @@ export async function cachePresenterResultsNode(state, deps) {
|
|
|
493
462
|
try {
|
|
494
463
|
await Promise.all(newCards.map((card) => {
|
|
495
464
|
const status = statusById.get(card.opportunityId);
|
|
496
|
-
// Negotiating, skeleton,
|
|
465
|
+
// Negotiating, skeleton, and unresolved-name cards are
|
|
497
466
|
// safe for the current response but must not become 24h entries.
|
|
498
467
|
if (!status || !isRadarPresentationCacheable(card, status))
|
|
499
468
|
return Promise.resolve();
|
|
@@ -527,7 +496,7 @@ export async function normalizeItemsNode(state) {
|
|
|
527
496
|
normalizeItemsLog.verbose('entry', { cardsLength: state.cards.length });
|
|
528
497
|
const createdAtById = new Map(state.opportunities.map((opportunity) => [opportunity.id, opportunity.createdAt.toISOString()]));
|
|
529
498
|
const items = state.cards.map((card) => {
|
|
530
|
-
const { _cardIndex,
|
|
499
|
+
const { _cardIndex, ...rest } = card;
|
|
531
500
|
return { ...rest, createdAt: createdAtById.get(card.opportunityId) };
|
|
532
501
|
});
|
|
533
502
|
const meta = { totalOpportunities: state.opportunities.length };
|
|
@@ -35,13 +35,11 @@ export interface RadarCardItem {
|
|
|
35
35
|
* full view. Skeleton cards are never written to the presenter cache.
|
|
36
36
|
*/
|
|
37
37
|
presentationPending?: boolean;
|
|
38
|
-
/** Internal marker: safe deterministic fallback rendered, but must not be cached. */
|
|
39
|
-
_presentationFallback?: boolean;
|
|
40
38
|
/** Internal: original position in the loaded opportunity list, for stable ordering. */
|
|
41
39
|
_cardIndex: number;
|
|
42
40
|
}
|
|
43
41
|
/** Card item as returned in API responses (no internal fields). */
|
|
44
|
-
export type RadarResponseItem = Omit<RadarCardItem, '_cardIndex'
|
|
42
|
+
export type RadarResponseItem = Omit<RadarCardItem, '_cardIndex'>;
|
|
45
43
|
/**
|
|
46
44
|
* Radar Graph State (Annotation-based).
|
|
47
45
|
* Flow: loadOpportunities → checkPresenterCache → [generateCardText if misses]
|
package/package.json
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic guard for unsupported person-affiliation and presence claims.
|
|
3
|
-
*
|
|
4
|
-
* Opportunity presentation contracts currently do not carry typed provenance
|
|
5
|
-
* proving attendance, membership, residence, or shared presence. The guard
|
|
6
|
-
* therefore fails closed: a sentence making one of those claims is rejected
|
|
7
|
-
* rather than loosely "grounded" against network/context text.
|
|
8
|
-
*
|
|
9
|
-
* The patterns are deliberately phrase-based. They target factual claims such
|
|
10
|
-
* as "Alice is a member of..." while leaving generic domain phrases such as
|
|
11
|
-
* "membership model" and "team members" alone. This is not a general factuality
|
|
12
|
-
* checker and does not attempt semantic inference beyond the listed claim
|
|
13
|
-
* families.
|
|
14
|
-
*/
|
|
15
|
-
/** Returns true when text contains an unsupported affiliation/presence claim. */
|
|
16
|
-
export declare function hasUnsupportedOpportunityClaim(text: string | null | undefined): boolean;
|
|
17
|
-
/** Returns true when one sentence contains an unsupported claim family. */
|
|
18
|
-
export declare function isUnsupportedOpportunityClaimSentence(sentence: string): boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Removes complete sentences containing unsupported claims and preserves the
|
|
21
|
-
* remaining sentence order. An all-unsafe input becomes an empty string so the
|
|
22
|
-
* caller can apply a field-specific deterministic default.
|
|
23
|
-
*/
|
|
24
|
-
export declare function stripUnsupportedOpportunityClaims(text: string | null | undefined): string;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic guard for unsupported person-affiliation and presence claims.
|
|
3
|
-
*
|
|
4
|
-
* Opportunity presentation contracts currently do not carry typed provenance
|
|
5
|
-
* proving attendance, membership, residence, or shared presence. The guard
|
|
6
|
-
* therefore fails closed: a sentence making one of those claims is rejected
|
|
7
|
-
* rather than loosely "grounded" against network/context text.
|
|
8
|
-
*
|
|
9
|
-
* The patterns are deliberately phrase-based. They target factual claims such
|
|
10
|
-
* as "Alice is a member of..." while leaving generic domain phrases such as
|
|
11
|
-
* "membership model" and "team members" alone. This is not a general factuality
|
|
12
|
-
* checker and does not attempt semantic inference beyond the listed claim
|
|
13
|
-
* families.
|
|
14
|
-
*/
|
|
15
|
-
const UNSUPPORTED_CLAIM_PATTERNS = [
|
|
16
|
-
// Attendance and co-attendance claims. Relational grammar avoids product
|
|
17
|
-
// phrases such as "attendee management".
|
|
18
|
-
/\b(?:co[-\s]?attendance|co[-\s]?attendee(?:s)?|co[-\s]?attend(?:ed|ing)?|(?:another|fellow)\s+(?:(?:event|session|conference|summit|gathering|meetup)\s+)?attendees?)\b/i,
|
|
19
|
-
/\b(?:is|are|was|were|will be|has been|have been|as)\s+(?:an?\s+)?attendees?\b/i,
|
|
20
|
-
/\b(?:a|an|this|that)\s+attendee\s+(?:of|at)\b/i,
|
|
21
|
-
/\b(?:will\s+|plans?\s+to\s+)?attend(?:ed|ing)?\s+(?!to\b)(?:(?:at|in)\s+)?(?:the\s+)?[\p{L}\p{N}&'’.-]+\b/iu,
|
|
22
|
-
/\b(?:is|are|was|were|will be)\s+going\s+to\s+(?:the\s+)?(?:event|session|conference|summit|gathering|meetup|[A-Z][\p{L}\p{N}&'’.-]*)\b/u,
|
|
23
|
-
/\b(?:[Pp]articipat(?:ed|ing)|[Pp]articipants?)\b(?=.{0,40}\b(?:in|at)\s+(?:the\s+)?(?:event|session|conference|summit|gathering|meetup|[A-Z][\p{L}\p{N}&'’.-]*))/u,
|
|
24
|
-
/\b(?:[Ww]ent\s+to|[Tt]ook\s+part\s+in|(?:[Ww]as|[Ww]ere|[Ii]s|[Aa]re)\s+present\s+at)\s+(?:the\s+)?(?:event|session|conference|summit|gathering|meetup|[A-Z][\p{L}\p{N}&'’.-]*)\b/u,
|
|
25
|
-
/\b(?:session\s+attendance|attendance\s+(?:at|in)\s+(?:the\s+)?(?:event|session|conference|summit|gathering|meetup))\b/i,
|
|
26
|
-
// Shared event/session/place/time claims, including "both were at ...".
|
|
27
|
-
/\b(?:same|the same)\s+(?:event|session|place|location|venue|time)\b/i,
|
|
28
|
-
/\b(?:share|shared|sharing)\s+(?:an?\s+|the\s+)?(?:event|session|workshop|conference|summit|gathering|meetup|place|location|venue|time)\b/i,
|
|
29
|
-
/\bboth\s+(?:were|are|will be|have been)\s+(?:at|in|during)\b/i,
|
|
30
|
-
/\b(?:were|are|will be|have been)\s+both\s+(?:at|in|during)\b/i,
|
|
31
|
-
/\b(?:will|would|are|were)\s+both\s+be?\s*(?:at|in|during)\b/i,
|
|
32
|
-
/\bboth\b.{0,80}\b(?:attend(?:ed|ing)?|participat(?:e|ed|ing)|met|meet|meeting|stayed|stay)\b.{0,60}\b(?:event|session|place|location|venue|time)\b/i,
|
|
33
|
-
// Factual membership/affiliation claims. Generic descriptions of product
|
|
34
|
-
// users ("members of cooperatives") and "team members" stay untouched.
|
|
35
|
-
/\b(?:is|are|was|were|be|being|been|became|become|as)\s+(?:an?\s+)?(?:fellow\s+)?members?\s+(?:of|in)\b/i,
|
|
36
|
-
/\b(?:another|fellow)\s+members?\b/i,
|
|
37
|
-
/\b(?:is|are|was|were|be|being|been|as)\s+(?:an?\s+)?(?:community|network|event)\s+members?\b/i,
|
|
38
|
-
/\b(?:a|an|this|that)\s+(?:fellow\s+)?member\s+(?:of|in)\b/i,
|
|
39
|
-
/\b(?:is|are|was|were|be|being|been)\s+(?:an?\s+)?part\s+of\s+(?:the\s+)?[^.!?]{0,60}\b(?:community|network|event)\b/i,
|
|
40
|
-
/\bfellow\s+members?\s+(?:of|in)\b/i,
|
|
41
|
-
/\b(?:a|an)\s+(?:[A-Z][\p{L}\p{N}&'’.-]*\s+){1,6}(?:community\s+|network\s+)?member\b/u,
|
|
42
|
-
/\b(?:[Bb]elongs?|[Bb]elonged)\s+to\s+(?:the\s+)?(?:[Nn]etwork|[Cc]ommunity|[Ee]vent|(?:[A-Z][\p{L}\p{N}&'’.-]*\s+){1,5}[A-Z][\p{L}\p{N}&'’.-]*)\b/u,
|
|
43
|
-
/\b[Jj]oined\s+(?:the\s+)?(?:[Nn]etwork|[Cc]ommunity|[Ee]vent|(?:[A-Z][\p{L}\p{N}&'’.-]*\s+){1,5}[A-Z][\p{L}\p{N}&'’.-]*)\b/u,
|
|
44
|
-
/\b[Aa]ffiliated\s+with\s+(?:the\s+)?(?:[Nn]etwork|[Cc]ommunity|[Ee]vent|[A-Z][\p{L}\p{N}&'’.-]*)\b/u,
|
|
45
|
-
// Residence/co-residence claims. Require person-claim grammar so generic
|
|
46
|
-
// audience descriptions such as "tools for residents of Berlin" survive.
|
|
47
|
-
/\b(?:is|are|was|were|be|being|been|as)\s+(?:an?\s+)?(?:local\s+|long[-\s]?time\s+|co[-\s]?)?residents?\b/i,
|
|
48
|
-
/\bco[-\s]?(?:reside|resides|resided|residing)\b/i,
|
|
49
|
-
/\b(?:another|fellow)\s+residents?\b/i,
|
|
50
|
-
/\b(?:a|an|this|that)\s+(?:co[-\s]?)?resident\s+(?:of|in)\b/i,
|
|
51
|
-
/\b(?:a|an)\s+(?:[A-Z][\p{L}\p{N}&'’.-]*\s+){1,6}(?:co[-\s]?)?resident\b/u,
|
|
52
|
-
/\b(?:[Hh]e|[Ss]he|[Tt]hey|[A-Z][\p{L}'’.-]+)\s+(?:reside|resides|resided|residing|live|lives|lived|living|(?:is|are|was|were)\s+based)\s+in\b/u,
|
|
53
|
-
/\b(?:[Hh]e|[Ss]he|[Tt]hey|[A-Z][\p{L}'’.-]+)\s+calls?\s+[A-Z][\p{L}\p{N}&'’.-]*(?:\s+[A-Z][\p{L}\p{N}&'’.-]*){0,5}\s+home\b/u,
|
|
54
|
-
// Relationship claims inferred from shared network/event placement.
|
|
55
|
-
/\b(?:know|knows|knew|met|meet)\b.{0,60}\b(?:through|from|at)\s+(?:the\s+)?(?:network|community|event|session)\b/i,
|
|
56
|
-
/\bcrossed\s+paths\s+(?:at|during)\b/i,
|
|
57
|
-
];
|
|
58
|
-
/** Returns true when text contains an unsupported affiliation/presence claim. */
|
|
59
|
-
export function hasUnsupportedOpportunityClaim(text) {
|
|
60
|
-
if (!text?.trim())
|
|
61
|
-
return false;
|
|
62
|
-
return splitClaimSentences(text).some(isUnsupportedOpportunityClaimSentence);
|
|
63
|
-
}
|
|
64
|
-
/** Returns true when one sentence contains an unsupported claim family. */
|
|
65
|
-
export function isUnsupportedOpportunityClaimSentence(sentence) {
|
|
66
|
-
return UNSUPPORTED_CLAIM_PATTERNS.some((pattern) => pattern.test(sentence));
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Removes complete sentences containing unsupported claims and preserves the
|
|
70
|
-
* remaining sentence order. An all-unsafe input becomes an empty string so the
|
|
71
|
-
* caller can apply a field-specific deterministic default.
|
|
72
|
-
*/
|
|
73
|
-
export function stripUnsupportedOpportunityClaims(text) {
|
|
74
|
-
if (!text?.trim())
|
|
75
|
-
return "";
|
|
76
|
-
return splitClaimSentences(text)
|
|
77
|
-
.filter((sentence) => !isUnsupportedOpportunityClaimSentence(sentence))
|
|
78
|
-
.join(" ")
|
|
79
|
-
.replace(/\s+/g, " ")
|
|
80
|
-
.trim();
|
|
81
|
-
}
|
|
82
|
-
function splitClaimSentences(text) {
|
|
83
|
-
return text
|
|
84
|
-
.trim()
|
|
85
|
-
.split(/(?<=[.!?])\s+|\n+/)
|
|
86
|
-
.map((sentence) => sentence.trim())
|
|
87
|
-
.filter(Boolean);
|
|
88
|
-
}
|