import type { DefineTool } from "../../shared/agent/tool.helpers.js"; import type { OpportunityToolDeps } from "../ports/index.js"; export { buildOpportunityPresentation } from "./opportunity.card-presentation.js"; import type { Opportunity } from "../../shared/interfaces/database.interface.js"; /** * Build the agent-facing profile link for a counterpart — always the Index web * profile URL with `?link_preview=false`. Returns `undefined` only if * `frontendUrl` is not configured. * * The `?link_preview=false` hint is honored by chat-gateway runtimes (e.g. * OpenClaw's Telegram delivery) that strip link previews when present in the * URL; consistent placement matters more than Telegram's own handling. * * Trailing slashes on frontendUrl are stripped before concatenation. */ export declare function buildProfileUrl(counterpartUserId: string, frontendUrl: string | undefined): string | undefined; /** * Build the deep-link to an opportunity's A2A negotiation trace * (`/chat/:conversationId`) so users can see *what negotiation led to* the * surfaced opportunity (EDG-50/EDG-51). Returns `undefined` when `frontendUrl` * is unset or there is no negotiation conversation to link to. * * The `?link_preview=false` hint mirrors `buildProfileUrl` — chat-gateway * runtimes (e.g. Telegram delivery) strip link previews when it is present. * Trailing slashes on `frontendUrl` are stripped before concatenation. */ export declare function buildNegotiationUrl(conversationId: string | undefined, frontendUrl: string | undefined): string | undefined; /** * Build the agent-facing deep link for an opportunity — the canonical * `https://index.network/o/` universal link. Returns `undefined` when * `frontendUrl` is unset or there is no opportunity id. * * This is the *only* place the protocol mints an opportunity deep link. It is * a navigation link, not an authority: opening it raises the opportunity card * in the Index macOS app when installed and a static Index landing page * otherwise. Acceptance stays an authenticated call. * * No `?link_preview=false` hint here (unlike `buildProfileUrl`): the Hermes * plugin mints the identical bare form for payloads the protocol does not * touch, and keeping the two byte-identical is what makes its never-overwrite * rule invisible. */ export declare function buildOpportunityAppUrl(opportunityId: string, frontendUrl: string | undefined): string | undefined; /** * Attach the agent-facing profile link for a counterpart to `card` (mutates * in place). Every counterpart has a profile page worth linking to — without * this, the agent gets a name with no URL attached and tends to fabricate * one. Accept/act guidance is plain text ("accept in the Index app"); no * actionable URLs are minted here. */ export declare function attachProfileLink(card: Record & { opportunityId: string; }, opts: { counterpartUserId: string; frontendUrl: string | undefined; }): void; /** * Attach the opportunity deep link to `card` (mutates in place) so every MCP * client — Claude Desktop, the CLI, the web, Hermes — can hand the user one * clickable link to the card instead of fabricating one from an id. */ export declare function attachOpportunityAppLink(card: Record & { opportunityId: string; }, opts: { frontendUrl: string | undefined; }): void; /** * Build minimal opportunity card data for chat without calling the LLM presenter. * Uses only required fields from the opportunity record and counterpart name/avatar * so list_opportunities and discovery return quickly. * * Note: narratorChip.text is generated via regex heuristics (narratorRemarkFromReasoning) * rather than the OpportunityPresenter LLM. If narrator quality becomes an issue again, * consider making this function async and delegating to OpportunityPresenter.presentCard() * which already produces a high-quality narratorRemark via LLM (used by the home graph * and discovery pipeline). The trade-off is 5-20s latency per card. * * Exported for use in tests (opportunity.tools.spec.ts). */ export declare function buildMinimalOpportunityCard(opp: Opportunity, viewerId: string, counterpartUserId: string, counterpartName: string, counterpartAvatar: string | null, introducerName?: string | null, introducerAvatar?: string | null, viewerName?: string, secondPartyName?: string, secondPartyAvatar?: string | null, secondPartyUserId?: string, isCounterpartGhost?: boolean): { opportunityId: string; userId: string; name: string; avatar: string | null; mainText: string; cta: string; headline: string; primaryActionLabel: string; secondaryActionLabel: string; mutualIntentsLabel: string; narratorChip: { name: string; text: string; avatar?: string | null; userId?: string; }; viewerRole: string; score: number | undefined; status: string; isGhost: boolean; secondParty?: { name: string; avatar?: string | null; userId?: string; }; }; /** * Stable, retry-classified error codes for `confirm_opportunity_delivery`. * * The plain `error()` envelope only carries a human message, which forced * callers (the Hermes digest sweep) to treat every failure — permanent or * transient — as retryable, and made "already delivered but never confirmed" * impossible to distinguish from "opportunity deleted". Each code carries an * explicit `retryable` flag so deterministic callers can retry transient * failures and drop permanent ones instead of re-spamming the ledger. */ export type ConfirmDeliveryErrorCode = "unauthenticated" | "ledger_unavailable" | "invalid_opportunity_id" | "opportunity_not_found" | "not_authorized" | "confirm_failed"; export declare function createOpportunityTools(defineTool: DefineTool, deps: OpportunityToolDeps): readonly [any, any, any];