@indexnetwork/protocol 8.8.0-rc.431.1 → 8.9.0-rc.432.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.
@@ -121,7 +121,6 @@ export function createEnrichmentTools(defineTool, deps) {
121
121
  ...(context.indexName ? { indexName: context.indexName } : {}),
122
122
  ...(context.sessionId ? { sessionId: context.sessionId } : {}),
123
123
  ...(context.agentId ? { agentId: context.agentId } : {}),
124
- ...(context.clientSurface ? { clientSurface: context.clientSurface } : {}),
125
124
  },
126
125
  });
127
126
  try {
@@ -87,5 +87,4 @@ export declare const MCP_INSTRUCTIONS: string;
87
87
  * Extracts a Bearer token from an HTTP Authorization header.
88
88
  */
89
89
  export declare function extractBearerToken(req: Request): string | undefined;
90
- export declare function parseClientSurface(raw: string | null): 'telegram' | 'web';
91
90
  export declare function createMcpServer(deps: ToolRegistryDeps, authResolver: McpAuthResolver, scopedDepsFactory: ScopedDepsFactory, policyOptions?: McpCapabilityPolicyOptions, authorizationObserver?: McpAuthorizationObserver): McpServer;
@@ -317,27 +317,6 @@ export function extractBearerToken(req) {
317
317
  return token;
318
318
  return undefined;
319
319
  }
320
- /**
321
- * Normalizes the x-index-surface header to a typed surface value.
322
- * Unknown or absent values collapse to 'web'.
323
- */
324
- let hasWarnedInvalidSurface = false;
325
- export function parseClientSurface(raw) {
326
- if (raw === null || raw === '')
327
- return 'web';
328
- const trimmed = raw.trim().toLowerCase();
329
- if (trimmed === '')
330
- return 'web';
331
- if (trimmed === 'telegram')
332
- return 'telegram';
333
- if (trimmed === 'web')
334
- return 'web';
335
- if (!hasWarnedInvalidSurface) {
336
- hasWarnedInvalidSurface = true;
337
- logger.warn('Unknown x-index-surface value (collapsing to web; warning once per process)');
338
- }
339
- return 'web';
340
- }
341
320
  export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOptions = {}, authorizationObserver) {
342
321
  const server = new McpServer({ name: 'index-network', version: '1.0.0' }, { instructions: MCP_INSTRUCTIONS });
343
322
  const toolMetadata = getCachedMcpToolMetadata(deps);
@@ -367,7 +346,6 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
367
346
  const extractAuthInput = (httpReq) => ({
368
347
  bearerToken: extractBearerToken(httpReq),
369
348
  apiKey: httpReq.headers.get('x-api-key') ?? undefined,
370
- clientSurface: parseClientSurface(httpReq.headers.get('x-index-surface')),
371
349
  telegramHandle: httpReq.headers.get('x-index-telegram-handle') ?? undefined,
372
350
  telegramUsername: httpReq.headers.get('x-index-telegram-username') ?? undefined,
373
351
  });
@@ -431,9 +409,6 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
431
409
  surface: 'mcp',
432
410
  sessionAuthenticated: authenticated.identity.isSessionAuth === true,
433
411
  });
434
- if (authenticated.identity.clientSurface) {
435
- context.clientSurface = authenticated.identity.clientSurface;
436
- }
437
412
  applyNetworkScopeToContext(context, authenticated.identity.networkScopeId);
438
413
  const subject = resolveMcpCapabilitySubject({
439
414
  identity: authenticated.identity,
@@ -11,8 +11,9 @@ export type OpportunityCardLike = Record<string, unknown> & {
11
11
  digestSummary?: string | undefined;
12
12
  status?: string | undefined;
13
13
  feedCategory?: string | undefined;
14
- acceptUrl?: string | undefined;
15
14
  profileUrl?: string | undefined;
15
+ /** Universal link that opens this opportunity's card (`/o/<id>`). */
16
+ appUrl?: string | undefined;
16
17
  /** Deep-link to the A2A negotiation trace that produced this opportunity. */
17
18
  negotiationUrl?: string | undefined;
18
19
  score?: number | undefined;
@@ -26,14 +27,13 @@ export type OpportunityCardLike = Record<string, unknown> & {
26
27
  * "include EXACTLY as-is" directive so the frontend card renderer can parse
27
28
  * and render interactive cards.
28
29
  *
29
- * MCP (`isMcp=true`): emits prose (name, reason, status, profileUrl when
30
- * present, acceptUrl when present, feedCategory when present) and includes
31
- * `opportunityId` ONLY for cards without an `acceptUrl` exposing the UUID
32
- * alongside an actionable link gave LLMs a foothold to hallucinate bare
33
- * `/api/opportunities/<id>/connect` URLs (see IND-271). The trailing
34
- * instruction reminds the agent to synthesize in natural language and never
35
- * fabricate URLs for cards that don't have them. MCP clients have no card
36
- * renderer, so code fences would surface as raw JSON to end users.
30
+ * MCP (`isMcp=true`): emits prose (name, reason, status, appUrl and profileUrl
31
+ * when present, feedCategory when present) and includes `opportunityId` for
32
+ * every card so the agent can act via the tools. The trailing instruction
33
+ * reminds the agent to synthesize in natural language, to surface the `appUrl`
34
+ * verbatim as the one link that opens the card, and to fabricate no other URL.
35
+ * MCP clients have no card renderer, so code fences would surface as raw JSON
36
+ * to end users.
37
37
  */
38
38
  export declare function buildOpportunityPresentation(inputCards: OpportunityCardLike[], opts: {
39
39
  isMcp: boolean;
@@ -31,14 +31,13 @@ function sanitizeOpportunityCardProse(card) {
31
31
  * "include EXACTLY as-is" directive so the frontend card renderer can parse
32
32
  * and render interactive cards.
33
33
  *
34
- * MCP (`isMcp=true`): emits prose (name, reason, status, profileUrl when
35
- * present, acceptUrl when present, feedCategory when present) and includes
36
- * `opportunityId` ONLY for cards without an `acceptUrl` exposing the UUID
37
- * alongside an actionable link gave LLMs a foothold to hallucinate bare
38
- * `/api/opportunities/<id>/connect` URLs (see IND-271). The trailing
39
- * instruction reminds the agent to synthesize in natural language and never
40
- * fabricate URLs for cards that don't have them. MCP clients have no card
41
- * renderer, so code fences would surface as raw JSON to end users.
34
+ * MCP (`isMcp=true`): emits prose (name, reason, status, appUrl and profileUrl
35
+ * when present, feedCategory when present) and includes `opportunityId` for
36
+ * every card so the agent can act via the tools. The trailing instruction
37
+ * reminds the agent to synthesize in natural language, to surface the `appUrl`
38
+ * verbatim as the one link that opens the card, and to fabricate no other URL.
39
+ * MCP clients have no card renderer, so code fences would surface as raw JSON
40
+ * to end users.
42
41
  */
43
42
  export function buildOpportunityPresentation(inputCards, opts) {
44
43
  const cards = inputCards.map(sanitizeOpportunityCardProse);
@@ -61,10 +60,10 @@ export function buildOpportunityPresentation(inputCards, opts) {
61
60
  }
62
61
  if (card.status)
63
62
  lines.push(` status: ${card.status}`);
63
+ if (card.appUrl)
64
+ lines.push(` appUrl: ${card.appUrl}`);
64
65
  if (card.profileUrl)
65
66
  lines.push(` profileUrl: ${card.profileUrl}`);
66
- if (card.acceptUrl)
67
- lines.push(` acceptUrl: ${card.acceptUrl}`);
68
67
  if (opts.includeDigestMarkers && card.negotiationUrl)
69
68
  lines.push(` negotiationUrl: ${card.negotiationUrl}`);
70
69
  if (card.feedCategory)
@@ -73,26 +72,16 @@ export function buildOpportunityPresentation(inputCards, opts) {
73
72
  lines.push(` confidence: ${Math.round(card.score * 100)}`);
74
73
  if (opts.includeDigestMarkers && card.redelivery)
75
74
  lines.push(` redelivery: true`);
76
- // Only surface opportunityId when there's no acceptUrl. Exposing the
77
- // UUID alongside an actionable link gives the LLM a foothold to
78
- // hallucinate bare `/api/opportunities/<id>/connect` URLs.
79
- if (!card.acceptUrl) {
80
- lines.push(` opportunityId: ${card.opportunityId}`);
81
- }
75
+ lines.push(` opportunityId: ${card.opportunityId}`);
82
76
  return lines.join("\n");
83
77
  })
84
78
  .join("\n\n");
85
- const hasLinks = cards.some((c) => c.acceptUrl);
86
- const hasOpportunityIds = cards.some((c) => !c.acceptUrl);
87
- const linkInstructions = hasLinks
88
- ? `For each card that has an acceptUrl, embed it on a short verb phrase (e.g. "message [Name]" for connection, "make intro" for connector-flow). For each card that has a profileUrl, link the person's name to it. Some cards may have neither — render those as plain text and never fabricate URLs for them. The acceptUrl is opaque and self-contained — embed it verbatim. Do NOT append, encode, or modify any part of any URL. Never render link strips or tables — weave URLs into prose. `
89
- : "";
90
- const idInstructions = hasOpportunityIds
91
- ? `Use opportunityId values only when calling update_opportunity (send/accept/reject) or confirm_opportunity_delivery.`
92
- : "";
79
+ const idInstructions = `Use opportunityId values only when calling update_opportunity (send/accept/reject) or confirm_opportunity_delivery.`;
93
80
  return (`${opts.leadIn}\n\n${prose}\n\n` +
94
81
  `Summarize these for the user in natural prose — mention first names and a brief match reason per connection. ` +
95
- `${linkInstructions}` +
82
+ `For each card that has a profileUrl, link the person's name to it. Some cards may have no URL — render those as plain text and never fabricate URLs for them. ` +
83
+ `For each card that has an appUrl, show that link so the user can open the opportunity: it opens the card in the Index app when installed, and an Index web page otherwise. Show only an appUrl a tool returned — never assemble one from an opportunityId. ` +
84
+ `No link accepts on the user's behalf: accepting happens in the Index app (or via update_opportunity) — never invent an accept URL. ` +
96
85
  `Do NOT print raw JSON, field labels, or opportunityIds. ` +
97
86
  `${idInstructions}`);
98
87
  }
@@ -133,7 +133,7 @@ export declare class OpportunityGraphFactory {
133
133
  targetUserId: Id<"users"> | undefined;
134
134
  onBehalfOfUserId: Id<"users"> | undefined;
135
135
  options: import("../domain/opportunity.state.js").OpportunityGraphOptions;
136
- operationMode: "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing";
136
+ operationMode: "delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction";
137
137
  introductionEntities: EvaluatorEntity[];
138
138
  introductionHint: string | undefined;
139
139
  requiredNetworkId: Id<"networks"> | undefined;
@@ -242,7 +242,7 @@ export declare class OpportunityGraphFactory {
242
242
  targetUserId?: Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined;
243
243
  onBehalfOfUserId?: Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined;
244
244
  options?: import("../domain/opportunity.state.js").OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityGraphOptions> | undefined;
245
- operationMode?: "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing"> | undefined;
245
+ operationMode?: "delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction"> | undefined;
246
246
  introductionEntities?: EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]> | undefined;
247
247
  introductionHint?: string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined;
248
248
  requiredNetworkId?: Id<"networks"> | import("@langchain/langgraph").OverwriteValue<Id<"networks"> | undefined> | undefined;
@@ -406,7 +406,7 @@ export declare class OpportunityGraphFactory {
406
406
  seedAssessmentScore?: number | undefined;
407
407
  }[]> | undefined;
408
408
  discoverySummary?: import("../../index.js").DiscoverySummary | import("@langchain/langgraph").OverwriteValue<import("../../index.js").DiscoverySummary | null> | null | undefined;
409
- }, "scope" | "discovery" | "send" | "update" | "approve_introduction" | "__start__" | "read" | "negotiate_existing" | "prep" | "persist" | "resolve" | "evaluation" | "ranking" | "intro_validation" | "intro_evaluation" | "delete_opp" | "negotiate", {
409
+ }, "scope" | "discovery" | "send" | "update" | "__start__" | "read" | "negotiate_existing" | "approve_introduction" | "prep" | "persist" | "resolve" | "evaluation" | "ranking" | "intro_validation" | "intro_evaluation" | "delete_opp" | "negotiate", {
410
410
  userId: import("@langchain/langgraph").BaseChannel<Id<"users">, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users">>, unknown>;
411
411
  searchQuery: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
412
412
  networkId: import("@langchain/langgraph").BaseChannel<Id<"networks"> | undefined, Id<"networks"> | import("@langchain/langgraph").OverwriteValue<Id<"networks"> | undefined> | undefined, unknown>;
@@ -415,7 +415,7 @@ export declare class OpportunityGraphFactory {
415
415
  targetUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
416
416
  onBehalfOfUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
417
417
  options: import("@langchain/langgraph").BaseChannel<import("../domain/opportunity.state.js").OpportunityGraphOptions, import("../domain/opportunity.state.js").OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityGraphOptions>, unknown>;
418
- operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing", "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing">, unknown>;
418
+ operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction", "delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction">, unknown>;
419
419
  introductionEntities: import("@langchain/langgraph").BaseChannel<EvaluatorEntity[], EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]>, unknown>;
420
420
  introductionHint: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
421
421
  requiredNetworkId: import("@langchain/langgraph").BaseChannel<Id<"networks"> | undefined, Id<"networks"> | import("@langchain/langgraph").OverwriteValue<Id<"networks"> | undefined> | undefined, unknown>;
@@ -652,7 +652,7 @@ export declare class OpportunityGraphFactory {
652
652
  targetUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
653
653
  onBehalfOfUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
654
654
  options: import("@langchain/langgraph").BaseChannel<import("../domain/opportunity.state.js").OpportunityGraphOptions, import("../domain/opportunity.state.js").OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityGraphOptions>, unknown>;
655
- operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing", "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing">, unknown>;
655
+ operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction", "delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction">, unknown>;
656
656
  introductionEntities: import("@langchain/langgraph").BaseChannel<EvaluatorEntity[], EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]>, unknown>;
657
657
  introductionHint: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
658
658
  requiredNetworkId: import("@langchain/langgraph").BaseChannel<Id<"networks"> | undefined, Id<"networks"> | import("@langchain/langgraph").OverwriteValue<Id<"networks"> | undefined> | undefined, unknown>;
@@ -2,37 +2,6 @@ import type { DefineTool } from "../../shared/agent/tool.helpers.js";
2
2
  import type { OpportunityToolDeps } from "../../capabilities/opportunities.tools.port.js";
3
3
  export { buildOpportunityPresentation } from "./opportunity.card-presentation.js";
4
4
  import type { Opportunity } from "../../shared/interfaces/database.interface.js";
5
- import type { ConnectLinkKind } from "../../shared/interfaces/connect-link.interface.js";
6
- /**
7
- * Pure status × role → ConnectLinkKind matrix.
8
- *
9
- * Returns the kind of short-link the viewer can act on directly, or `null` if
10
- * no direct link makes sense for this combination. Non-null kinds map to:
11
- *
12
- * - `connect` — pending opp where viewer is a non-introducer party. Clicking
13
- * flips the opp to accepted and opens the chat with the counterpart.
14
- * - `approve_introduction` — draft or latent opp where viewer is an unapproved
15
- * introducer. Clicking flips approved=true and triggers negotiation. The
16
- * `draft` case represents a pending introduction; the `latent`
17
- * case comes from background-discovered connector-flow cards surfaced in
18
- * `list_opportunities`. In both, status remains pre-send and the `/c/<code>`
19
- * link is the only MCP path to approve.
20
- * - `outreach` — accepted opp where viewer is a non-introducer party.
21
- * Clicking opens the existing chat (no state change).
22
- * - `send_direct` — draft or latent opp where viewer is a non-introducer
23
- * party. Issued for a direct opportunity without an introducer
24
- * mode: the match has already passed evaluation, the row exists in
25
- * draft state, and the sender just needs to release it. Clicking flips
26
- * the opp straight to accepted and opens the chat with a greeting —
27
- * same handler path as `connect`. The counterpart's side sees the new
28
- * accepted opp and can engage or ignore.
29
- */
30
- export declare function resolveActionableLinkKind(input: {
31
- status: string;
32
- viewerRole: string;
33
- viewerApproved?: boolean;
34
- viewerActedAt?: string | null;
35
- }): ConnectLinkKind | null;
36
5
  /**
37
6
  * Build the agent-facing profile link for a counterpart — always the Index web
38
7
  * profile URL with `?link_preview=false`. Returns `undefined` only if
@@ -57,26 +26,44 @@ export declare function buildProfileUrl(counterpartUserId: string, frontendUrl:
57
26
  */
58
27
  export declare function buildNegotiationUrl(conversationId: string | undefined, frontendUrl: string | undefined): string | undefined;
59
28
  /**
60
- * Mint a short-link for `card` if the (status, viewerRole, viewerApproved)
61
- * combination is actionable; mutate the card in place with `acceptUrl`,
62
- * `profileUrl`, and `feedCategory`. No-op (and no DB call) if not actionable.
29
+ * Build the agent-facing deep link for an opportunity the canonical
30
+ * `https://index.network/o/<id>` universal link. Returns `undefined` when
31
+ * `frontendUrl` is unset or there is no opportunity id.
63
32
  *
64
- * Swallows mint errors after logging the card is still returned without an
65
- * `acceptUrl`, matching the prior `list_opportunities` resilience behavior.
33
+ * This is the *only* place the protocol mints an opportunity deep link. It is
34
+ * a navigation link, not an authority: opening it raises the opportunity card
35
+ * in the Index macOS app when installed and a static Index landing page
36
+ * otherwise. Acceptance stays an authenticated call.
37
+ *
38
+ * No `?link_preview=false` hint here (unlike `buildProfileUrl`): the Hermes
39
+ * plugin mints the identical bare form for payloads the protocol does not
40
+ * touch, and keeping the two byte-identical is what makes its never-overwrite
41
+ * rule invisible.
42
+ */
43
+ export declare function buildOpportunityAppUrl(opportunityId: string, frontendUrl: string | undefined): string | undefined;
44
+ /**
45
+ * Attach the agent-facing profile link for a counterpart to `card` (mutates
46
+ * in place). Every counterpart has a profile page worth linking to — without
47
+ * this, the agent gets a name with no URL attached and tends to fabricate
48
+ * one. Accept/act guidance is plain text ("accept in the Index app"); no
49
+ * actionable URLs are minted here.
66
50
  */
67
- export declare function attachActionableLinks(card: Record<string, unknown> & {
51
+ export declare function attachProfileLink(card: Record<string, unknown> & {
68
52
  opportunityId: string;
69
- viewerRole: string;
70
- status: string;
71
53
  }, opts: {
72
- viewerId: string;
73
- viewerApproved?: boolean;
74
- viewerActedAt?: string | null;
75
54
  counterpartUserId: string;
76
- mintConnectLink: NonNullable<OpportunityToolDeps["mintConnectLink"]>;
77
55
  frontendUrl: string | undefined;
78
- preferredSurface?: 'telegram' | 'web';
79
- }): Promise<void>;
56
+ }): void;
57
+ /**
58
+ * Attach the opportunity deep link to `card` (mutates in place) so every MCP
59
+ * client — Claude Desktop, the CLI, the web, Hermes — can hand the user one
60
+ * clickable link to the card instead of fabricating one from an id.
61
+ */
62
+ export declare function attachOpportunityAppLink(card: Record<string, unknown> & {
63
+ opportunityId: string;
64
+ }, opts: {
65
+ frontendUrl: string | undefined;
66
+ }): void;
80
67
  /**
81
68
  * Build minimal opportunity card data for chat without calling the LLM presenter.
82
69
  * Uses only required fields from the opportunity record and counterpart name/avatar
@@ -41,47 +41,6 @@ function stripLeadingNarratorName(remark, narratorName) {
41
41
  import { protocolLogger } from "../../shared/observability/protocol.logger.js";
42
42
  import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
43
43
  const logger = protocolLogger("ChatTools:Opportunity");
44
- /**
45
- * Pure status × role → ConnectLinkKind matrix.
46
- *
47
- * Returns the kind of short-link the viewer can act on directly, or `null` if
48
- * no direct link makes sense for this combination. Non-null kinds map to:
49
- *
50
- * - `connect` — pending opp where viewer is a non-introducer party. Clicking
51
- * flips the opp to accepted and opens the chat with the counterpart.
52
- * - `approve_introduction` — draft or latent opp where viewer is an unapproved
53
- * introducer. Clicking flips approved=true and triggers negotiation. The
54
- * `draft` case represents a pending introduction; the `latent`
55
- * case comes from background-discovered connector-flow cards surfaced in
56
- * `list_opportunities`. In both, status remains pre-send and the `/c/<code>`
57
- * link is the only MCP path to approve.
58
- * - `outreach` — accepted opp where viewer is a non-introducer party.
59
- * Clicking opens the existing chat (no state change).
60
- * - `send_direct` — draft or latent opp where viewer is a non-introducer
61
- * party. Issued for a direct opportunity without an introducer
62
- * mode: the match has already passed evaluation, the row exists in
63
- * draft state, and the sender just needs to release it. Clicking flips
64
- * the opp straight to accepted and opens the chat with a greeting —
65
- * same handler path as `connect`. The counterpart's side sees the new
66
- * accepted opp and can engage or ignore.
67
- */
68
- export function resolveActionableLinkKind(input) {
69
- const { status, viewerRole, viewerApproved, viewerActedAt } = input;
70
- const isIntroducer = viewerRole === "introducer";
71
- const hasViewerActed = !!viewerActedAt;
72
- if (status === "accepted") {
73
- return isIntroducer ? null : "outreach";
74
- }
75
- if (status === "pending") {
76
- return isIntroducer || hasViewerActed ? null : "connect";
77
- }
78
- if (status === "draft" || status === "latent") {
79
- if (!isIntroducer)
80
- return hasViewerActed ? null : "send_direct";
81
- return viewerApproved === true ? null : "approve_introduction";
82
- }
83
- return null;
84
- }
85
44
  /**
86
45
  * Build the agent-facing profile link for a counterpart — always the Index web
87
46
  * profile URL with `?link_preview=false`. Returns `undefined` only if
@@ -95,9 +54,7 @@ export function resolveActionableLinkKind(input) {
95
54
  */
96
55
  export function buildProfileUrl(counterpartUserId, frontendUrl) {
97
56
  // The profile link is always the Index web profile, regardless of the
98
- // counterpart's socials or the viewer's surface. Surface-aware Telegram
99
- // deep-linking lives on the connect URL (`/c/:code`) — the connect-link
100
- // controller resolves that to a pre-messaged t.me link — never here.
57
+ // counterpart's socials.
101
58
  if (!frontendUrl)
102
59
  return undefined;
103
60
  const base = frontendUrl.replace(/\/+$/, "");
@@ -120,58 +77,47 @@ export function buildNegotiationUrl(conversationId, frontendUrl) {
120
77
  return `${base}/chat/${conversationId}?link_preview=false`;
121
78
  }
122
79
  /**
123
- * Mint a short-link for `card` if the (status, viewerRole, viewerApproved)
124
- * combination is actionable; mutate the card in place with `acceptUrl`,
125
- * `profileUrl`, and `feedCategory`. No-op (and no DB call) if not actionable.
80
+ * Build the agent-facing deep link for an opportunity the canonical
81
+ * `https://index.network/o/<id>` universal link. Returns `undefined` when
82
+ * `frontendUrl` is unset or there is no opportunity id.
83
+ *
84
+ * This is the *only* place the protocol mints an opportunity deep link. It is
85
+ * a navigation link, not an authority: opening it raises the opportunity card
86
+ * in the Index macOS app when installed and a static Index landing page
87
+ * otherwise. Acceptance stays an authenticated call.
126
88
  *
127
- * Swallows mint errors after logging the card is still returned without an
128
- * `acceptUrl`, matching the prior `list_opportunities` resilience behavior.
89
+ * No `?link_preview=false` hint here (unlike `buildProfileUrl`): the Hermes
90
+ * plugin mints the identical bare form for payloads the protocol does not
91
+ * touch, and keeping the two byte-identical is what makes its never-overwrite
92
+ * rule invisible.
129
93
  */
130
- export async function attachActionableLinks(card, opts) {
131
- // profileUrl is independent of whether the (status, viewerRole) combination
132
- // is actionable — every counterpart has a profile page worth linking to,
133
- // even on a fresh draft where there is no acceptUrl yet. Setting it before
134
- // the early-return below means cards from non-actionable combinations
135
- // (e.g. a draft party card) still carry
136
- // the profile link the agent needs to render. Without this, the agent gets
137
- // a name with no URL attached and tends to fabricate one.
94
+ export function buildOpportunityAppUrl(opportunityId, frontendUrl) {
95
+ if (!frontendUrl || !opportunityId)
96
+ return undefined;
97
+ const base = frontendUrl.replace(/\/+$/, "");
98
+ return `${base}/o/${opportunityId}`;
99
+ }
100
+ /**
101
+ * Attach the agent-facing profile link for a counterpart to `card` (mutates
102
+ * in place). Every counterpart has a profile page worth linking to — without
103
+ * this, the agent gets a name with no URL attached and tends to fabricate
104
+ * one. Accept/act guidance is plain text ("accept in the Index app"); no
105
+ * actionable URLs are minted here.
106
+ */
107
+ export function attachProfileLink(card, opts) {
138
108
  const profileUrl = buildProfileUrl(opts.counterpartUserId, opts.frontendUrl);
139
109
  if (profileUrl)
140
110
  card.profileUrl = profileUrl;
141
- const kind = resolveActionableLinkKind({
142
- status: card.status,
143
- viewerRole: card.viewerRole,
144
- viewerApproved: opts.viewerApproved,
145
- viewerActedAt: opts.viewerActedAt,
146
- });
147
- logger.info("Opportunity actionability decision", {
148
- opportunityId: card.opportunityId,
149
- status: card.status,
150
- viewerRole: card.viewerRole,
151
- viewerApproved: opts.viewerApproved,
152
- viewerActedAt: opts.viewerActedAt,
153
- kind: kind ?? "none",
154
- });
155
- if (kind === null)
156
- return;
157
- try {
158
- const { url } = await opts.mintConnectLink({
159
- userId: opts.viewerId,
160
- opportunityId: card.opportunityId,
161
- kind,
162
- greeting: null,
163
- preferredSurface: opts.preferredSurface,
164
- });
165
- card.acceptUrl = url;
166
- card.feedCategory = card.viewerRole === "introducer" ? "connector-flow" : "connection";
167
- }
168
- catch (err) {
169
- logger.warn("Failed to mint MCP opportunity link — surfacing card without acceptUrl/feedCategory; profileUrl is still attached", {
170
- opportunityId: card.opportunityId,
171
- kind,
172
- error: err instanceof Error ? err.message : String(err),
173
- });
174
- }
111
+ }
112
+ /**
113
+ * Attach the opportunity deep link to `card` (mutates in place) so every MCP
114
+ * client — Claude Desktop, the CLI, the web, Hermes — can hand the user one
115
+ * clickable link to the card instead of fabricating one from an id.
116
+ */
117
+ export function attachOpportunityAppLink(card, opts) {
118
+ const appUrl = buildOpportunityAppUrl(card.opportunityId, opts.frontendUrl);
119
+ if (appUrl)
120
+ card.appUrl = appUrl;
175
121
  }
176
122
  function publicUptakeQuestion(question) {
177
123
  return {
@@ -563,17 +509,15 @@ export function createOpportunityTools(defineTool, deps) {
563
509
  }
564
510
  : {}),
565
511
  };
566
- // Attach actionable links for MCP callers
567
- if (context.isMcp && deps.mintConnectLink) {
568
- const viewerApproved = viewerActor?.role === "introducer" ? viewerActor.approved === true : undefined;
569
- await attachActionableLinks(card, {
570
- viewerId: context.userId,
571
- viewerApproved,
572
- viewerActedAt: viewerActor?.actedAt ?? null,
512
+ // Attach the agent-facing profile and opportunity links for
513
+ // MCP callers
514
+ if (context.isMcp) {
515
+ attachProfileLink(card, {
573
516
  counterpartUserId,
574
- mintConnectLink: deps.mintConnectLink,
575
517
  frontendUrl: deps.frontendUrl,
576
- preferredSurface: context.clientSurface,
518
+ });
519
+ attachOpportunityAppLink(card, {
520
+ frontendUrl: deps.frontendUrl,
577
521
  });
578
522
  }
579
523
  return card;
@@ -710,22 +654,17 @@ export function createOpportunityTools(defineTool, deps) {
710
654
  }
711
655
  : {}),
712
656
  };
713
- // For MCP callers (e.g. Edge Claw), mint a connect token and attach
714
- // acceptUrl + profileUrl when the (status, viewerRole) is actionable
715
- // for the viewer. Non-actionable combos (sender-on-draft,
716
- // pending-on-introducer-waiting, rejected, etc.) deliberately get
717
- // no link — the LLM would otherwise hallucinate `/api/.../connect`
718
- // URLs from the exposed opportunityId.
719
- if (context.isMcp && deps.mintConnectLink) {
720
- const viewerApproved = viewerActor?.role === "introducer" ? viewerActor.approved === true : undefined;
721
- await attachActionableLinks(cardData, {
722
- viewerId: context.userId,
723
- viewerApproved,
724
- viewerActedAt: viewerActor?.actedAt ?? null,
657
+ // For MCP callers, attach the agent-facing profile link and the
658
+ // opportunity deep link so the agent never has to fabricate
659
+ // either. Accepting still happens in the Index app behind an
660
+ // authenticated call the deep link only opens the card.
661
+ if (context.isMcp) {
662
+ attachProfileLink(cardData, {
725
663
  counterpartUserId,
726
- mintConnectLink: deps.mintConnectLink,
727
664
  frontendUrl: deps.frontendUrl,
728
- preferredSurface: context.clientSurface,
665
+ });
666
+ attachOpportunityAppLink(cardData, {
667
+ frontendUrl: deps.frontendUrl,
729
668
  });
730
669
  }
731
670
  return cardData;
@@ -178,7 +178,7 @@ export declare const OpportunityGraphState: import("@langchain/langgraph").Annot
178
178
  *
179
179
  * Defaults to 'create' for backward compatibility.
180
180
  */
181
- operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing", "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing">, unknown>;
181
+ operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction", "delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | "approve_introduction">, unknown>;
182
182
  /** Introduction mode: pre-gathered entities (profiles + intents per party). */
183
183
  introductionEntities: import("@langchain/langgraph").BaseChannel<EvaluatorEntity[], EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]>, unknown>;
184
184
  /** Introduction mode: optional hint from the introducer. */
@@ -11,6 +11,6 @@ export interface OpportunityOwnerApprovalDeps {
11
11
  opportunityOwnerApproval?: OpportunityOwnerApprovalAuthority;
12
12
  }
13
13
  /** Host capabilities consumed by opportunity discovery, delivery, and presentation tools. */
14
- export type OpportunityToolDeps = Pick<ToolRegistryCompositionDeps, "database" | "userDb" | "systemDb" | "cache" | "chatSummary" | "opportunityPresentation" | "questionGenerator" | "questionerEnqueue" | "findPendingQuestions" | "negotiationSummary" | "negotiationDatabase" | "deliveryLedger" | "mintConnectLink" | "frontendUrl" | "stampNewbornOpportunities" | "reportToolError"> & OpportunityOwnerApprovalDeps & {
14
+ export type OpportunityToolDeps = Pick<ToolRegistryCompositionDeps, "database" | "userDb" | "systemDb" | "cache" | "chatSummary" | "opportunityPresentation" | "questionGenerator" | "questionerEnqueue" | "findPendingQuestions" | "negotiationSummary" | "negotiationDatabase" | "deliveryLedger" | "frontendUrl" | "stampNewbornOpportunities" | "reportToolError"> & OpportunityOwnerApprovalDeps & {
15
15
  graphs: Pick<ToolRegistryCompositionDeps["graphs"], "index" | "networkMembership" | "opportunity">;
16
16
  };
@@ -166,8 +166,6 @@ export async function createChatTools(deps, preResolvedContext) {
166
166
  ...(deps.opportunityOwnerApproval && { opportunityOwnerApproval: deps.opportunityOwnerApproval }),
167
167
  enrichmentRuns: deps.enrichmentRuns,
168
168
  enrichmentRunQueue: deps.enrichmentRunQueue,
169
- mintConnectToken: deps.mintConnectToken,
170
- mintConnectLink: deps.mintConnectLink,
171
169
  frontendUrl: deps.frontendUrl,
172
170
  apiBaseUrl: deps.apiBaseUrl,
173
171
  ...(deps.chatSummary && { chatSummary: deps.chatSummary }),
@@ -19,7 +19,6 @@ import type { AgentDatabase } from "../interfaces/agent.interface.js";
19
19
  import type { NegotiationTimeoutQueue } from "../interfaces/negotiation-events.interface.js";
20
20
  import type { AgentDispatcher } from "../interfaces/agent-dispatcher.interface.js";
21
21
  import type { DeliveryLedger } from "../interfaces/delivery-ledger.interface.js";
22
- import type { MintConnectLink } from "../interfaces/connect-link.interface.js";
23
22
  import type { ChatQuestionsHost, QuestionerDatabase } from "../interfaces/questioner.interface.js";
24
23
  import type { NegotiatorMemoryToolsHost } from "../interfaces/negotiator-memory.interface.js";
25
24
  import type { QuestionerEnqueueFn } from "../../questioner/questioner.types.js";
@@ -100,14 +99,6 @@ export interface ResolvedToolContext {
100
99
  * which are owner-trusted and receive the full owner view.
101
100
  */
102
101
  mcpCaller?: McpActivityCaller;
103
- /**
104
- * Receiver's rendering surface declared by the MCP client via the
105
- * `x-index-surface` request header. `'telegram'` means the MCP response is
106
- * being rendered inside a Telegram chat; anything
107
- * else (including `undefined`) is treated as web. Forwarded into
108
- * `mintConnectLink` so the click-time redirect can branch.
109
- */
110
- clientSurface?: 'telegram' | 'web';
111
102
  /**
112
103
  * True when the CONTACTS_ENABLED feature flag is on. Carried from the
113
104
  * composition root so prompt modules can gate contact-import guidance —
@@ -268,13 +259,6 @@ interface ToolContextBindings {
268
259
  enrichmentRuns?: EnrichmentRunStore;
269
260
  /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */
270
261
  enrichmentRunQueue?: EnrichmentRunQueue;
271
- /**
272
- * Legacy direct-token minting for opportunity accept redirects.
273
- * Prefer `mintConnectLink` for user-facing links.
274
- */
275
- mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;
276
- /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */
277
- mintConnectLink?: MintConnectLink;
278
262
  /** Frontend base URL for building profile links (e.g. https://index.network, optional). */
279
263
  frontendUrl?: string;
280
264
  /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */
@@ -495,13 +479,6 @@ interface ToolDepsBindings {
495
479
  enrichmentRuns?: EnrichmentRunStore;
496
480
  /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */
497
481
  enrichmentRunQueue?: EnrichmentRunQueue;
498
- /**
499
- * Legacy direct-token minting for opportunity accept redirects.
500
- * Prefer `mintConnectLink` for user-facing links.
501
- */
502
- mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;
503
- /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */
504
- mintConnectLink?: MintConnectLink;
505
482
  /** Frontend base URL for building profile links (e.g. https://index.network, optional). */
506
483
  frontendUrl?: string;
507
484
  /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */
@@ -557,9 +534,9 @@ interface ToolDepsBindings {
557
534
  * Shared backing shape for the registry composition boundary. Capability-local
558
535
  * ports may Pick from this type, but it is intentionally not a root export.
559
536
  */
560
- export type ToolRegistryCompositionDeps = Omit<ToolDepsBindings, 'embedder' | 'chatMessageWriter' | 'mintConnectToken' | 'apiBaseUrl' | 'mcpRateLimiter'>;
537
+ export type ToolRegistryCompositionDeps = Omit<ToolDepsBindings, 'embedder' | 'chatMessageWriter' | 'apiBaseUrl' | 'mcpRateLimiter'>;
561
538
  /** Runtime-only hooks retained for MCP and existing host composition. */
562
- type ToolRuntimeCompatibilityDeps = Pick<ToolDepsBindings, 'embedder' | 'chatMessageWriter' | 'mintConnectToken' | 'apiBaseUrl' | 'mcpRateLimiter'>;
539
+ type ToolRuntimeCompatibilityDeps = Pick<ToolDepsBindings, 'embedder' | 'chatMessageWriter' | 'apiBaseUrl' | 'mcpRateLimiter'>;
563
540
  /**
564
541
  * Legacy complete tool composition contract.
565
542
  *
@@ -13,9 +13,8 @@ export interface McpAuthResolver {
13
13
  * @param input - Transport-neutral auth input DTO with credential fields
14
14
  * extracted at the MCP transport edge.
15
15
  * @returns The authenticated user's UUID, optional agent UUID, auth method,
16
- * `networkScopeId` if the caller's API key is bound to a network-scoped
17
- * agent, and `clientSurface` declaring which kind of UI is rendering the
18
- * MCP response (drives connect-link redirect choice at click time).
16
+ * and `networkScopeId` if the caller's API key is bound to a network-scoped
17
+ * agent.
19
18
  *
20
19
  * When `networkScopeId` is set, the MCP server promotes it into the
21
20
  * canonical `{ scopeType: 'network', scopeId }` envelope. Downstream tools
@@ -24,9 +23,6 @@ export interface McpAuthResolver {
24
23
  * `isSessionAuth` is true for OAuth/JWT bearer sessions — the agent-
25
24
  * registration gate in the MCP server is skipped for these callers.
26
25
  *
27
- * `clientSurface` is passed through from the DTO. Only `'telegram'`
28
- * activates the t.me redirect path on `/c/{code}` clicks.
29
- *
30
26
  * @throws Error if authentication fails (no token, invalid token, etc.)
31
27
  */
32
28
  resolveIdentity(input: McpAuthInput): Promise<McpResolvedIdentity>;
@@ -26,7 +26,7 @@ export interface EnrichmentRunRecord {
26
26
  operation: EnrichmentRunOperation;
27
27
  status: EnrichmentRunStatus;
28
28
  input: EnrichmentRunInput;
29
- context: Pick<ToolRequestContext, "userId" | "userName" | "userEmail" | "scopeType" | "scopeId" | "indexName" | "sessionId" | "agentId" | "clientSurface">;
29
+ context: Pick<ToolRequestContext, "userId" | "userName" | "userEmail" | "scopeType" | "scopeId" | "indexName" | "sessionId" | "agentId">;
30
30
  progress?: Record<string, unknown> | null;
31
31
  result?: unknown;
32
32
  error?: string | null;
@@ -8,5 +8,4 @@ export interface ToolRequestContext {
8
8
  indexName?: string;
9
9
  sessionId?: string;
10
10
  agentId?: string;
11
- clientSurface?: "telegram" | "web";
12
11
  }
@@ -5,18 +5,15 @@ import { z } from 'zod';
5
5
  export declare const McpAuthInputSchema: z.ZodObject<{
6
6
  bearerToken: z.ZodOptional<z.ZodString>;
7
7
  apiKey: z.ZodOptional<z.ZodString>;
8
- clientSurface: z.ZodOptional<z.ZodEnum<["telegram", "web"]>>;
9
8
  telegramHandle: z.ZodOptional<z.ZodString>;
10
9
  telegramUsername: z.ZodOptional<z.ZodString>;
11
10
  }, "strict", z.ZodTypeAny, {
12
11
  apiKey?: string | undefined;
13
- clientSurface?: "telegram" | "web" | undefined;
14
12
  bearerToken?: string | undefined;
15
13
  telegramHandle?: string | undefined;
16
14
  telegramUsername?: string | undefined;
17
15
  }, {
18
16
  apiKey?: string | undefined;
19
- clientSurface?: "telegram" | "web" | undefined;
20
17
  bearerToken?: string | undefined;
21
18
  telegramHandle?: string | undefined;
22
19
  telegramUsername?: string | undefined;
@@ -52,11 +49,9 @@ export declare const McpResolvedIdentitySchema: z.ZodEffects<z.ZodObject<{
52
49
  enrollmentCapable: z.ZodOptional<z.ZodBoolean>;
53
50
  isDeliveryAgent: z.ZodOptional<z.ZodBoolean>;
54
51
  networkScopeId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
- clientSurface: z.ZodOptional<z.ZodEnum<["telegram", "web"]>>;
56
52
  }, "strict", z.ZodTypeAny, {
57
53
  userId: string;
58
54
  agentId?: string | undefined;
59
- clientSurface?: "telegram" | "web" | undefined;
60
55
  networkScopeId?: string | null | undefined;
61
56
  enrollmentCapable?: boolean | undefined;
62
57
  isDeliveryAgent?: boolean | undefined;
@@ -64,7 +59,6 @@ export declare const McpResolvedIdentitySchema: z.ZodEffects<z.ZodObject<{
64
59
  }, {
65
60
  userId: string;
66
61
  agentId?: string | undefined;
67
- clientSurface?: "telegram" | "web" | undefined;
68
62
  networkScopeId?: string | null | undefined;
69
63
  enrollmentCapable?: boolean | undefined;
70
64
  isDeliveryAgent?: boolean | undefined;
@@ -72,7 +66,6 @@ export declare const McpResolvedIdentitySchema: z.ZodEffects<z.ZodObject<{
72
66
  }>, {
73
67
  userId: string;
74
68
  agentId?: string | undefined;
75
- clientSurface?: "telegram" | "web" | undefined;
76
69
  networkScopeId?: string | null | undefined;
77
70
  enrollmentCapable?: boolean | undefined;
78
71
  isDeliveryAgent?: boolean | undefined;
@@ -80,7 +73,6 @@ export declare const McpResolvedIdentitySchema: z.ZodEffects<z.ZodObject<{
80
73
  }, {
81
74
  userId: string;
82
75
  agentId?: string | undefined;
83
- clientSurface?: "telegram" | "web" | undefined;
84
76
  networkScopeId?: string | null | undefined;
85
77
  enrollmentCapable?: boolean | undefined;
86
78
  isDeliveryAgent?: boolean | undefined;
@@ -5,7 +5,6 @@ import { z } from 'zod';
5
5
  export const McpAuthInputSchema = z.object({
6
6
  bearerToken: z.string().min(1).optional(),
7
7
  apiKey: z.string().min(1).optional(),
8
- clientSurface: z.enum(['telegram', 'web']).optional(),
9
8
  telegramHandle: z.string().min(1).optional(),
10
9
  telegramUsername: z.string().min(1).optional(),
11
10
  }).strict();
@@ -30,7 +29,6 @@ export const McpResolvedIdentitySchema = z.object({
30
29
  enrollmentCapable: z.boolean().optional(),
31
30
  isDeliveryAgent: z.boolean().optional(),
32
31
  networkScopeId: z.string().min(1).nullable().optional(),
33
- clientSurface: z.enum(['telegram', 'web']).optional(),
34
32
  }).strict().superRefine((identity, ctx) => {
35
33
  if (identity.isSessionAuth === true && identity.agentId) {
36
34
  ctx.addIssue({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indexnetwork/protocol",
3
- "version": "8.8.0-rc.431.1",
3
+ "version": "8.9.0-rc.432.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1,39 +0,0 @@
1
- /**
2
- * Kind of connect link being minted. Determines the action endpoint the short
3
- * URL eventually redirects to:
4
- *
5
- * - `connect` — receiver of a `pending` opp clicks to flip it to `accepted`
6
- * and open the chat with a pre-filled greeting.
7
- * - `approve_introduction` — unapproved introducer on a `draft`/`latent` opp
8
- * clicks to flip `approved=true` and kick off negotiation.
9
- * - `outreach` — non-introducer party on an `accepted` opp clicks to open
10
- * the existing chat (no state change).
11
- * - `send_direct` — sender (non-introducer party) of a `draft`/`latent`
12
- * direct-mode opp clicks to flip it straight to `accepted` and open
13
- * the chat with a pre-filled greeting. Mirrors the frontend's "Start
14
- * Chat" button on draft cards: both posted public intents that
15
- * matched, so opening the chat counts as the consent; the counterpart
16
- * sees the new accepted opp on their side and can engage or not.
17
- */
18
- export type ConnectLinkKind = 'connect' | 'approve_introduction' | 'outreach' | 'send_direct';
19
- /**
20
- * Mints (or reuses) a short link for the given recipient and kind, snapshotting
21
- * the greeting and the caller's preferred surface onto the link record. Returns
22
- * the full public URL.
23
- *
24
- * `preferredSurface` is stamped onto the row at insert time and drives the
25
- * click-time redirect on `/c/{code}/go`: only `'telegram'` activates the t.me
26
- * deep-link path; everything else (including `undefined`, persisted as NULL)
27
- * routes to the web frontend chat URL.
28
- */
29
- export interface MintConnectLink {
30
- (args: {
31
- userId: string;
32
- opportunityId: string;
33
- kind: ConnectLinkKind;
34
- greeting?: string | null;
35
- preferredSurface?: 'telegram' | 'web';
36
- }): Promise<{
37
- url: string;
38
- }>;
39
- }
@@ -1 +0,0 @@
1
- export {};