@indexnetwork/protocol 3.6.0 → 3.6.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.
@@ -155,4 +155,60 @@ export declare function deduplicateByPerson<T extends {
155
155
  confidence?: number;
156
156
  } | null;
157
157
  }>(opportunities: T[], viewerId: string): T[];
158
+ /**
159
+ * Days a digest-delivered opportunity stays suppressed before it becomes
160
+ * eligible for a "still open" reminder re-show (when nothing fresh exists).
161
+ */
162
+ export declare const DIGEST_REDELIVERY_COOLDOWN_DAYS = 5;
163
+ /** Committed delivery row shape consumed by {@link selectDigestCandidates}. */
164
+ export interface DigestDeliveredRow {
165
+ opportunityId: string;
166
+ deliveredAtStatus: string;
167
+ deliveredAt: Date;
168
+ }
169
+ /**
170
+ * Cross-day digest suppression for scheduled-brief candidates.
171
+ *
172
+ * Three rules, applied in order:
173
+ * 1. **Accepted-counterpart suppression** — a direct-connection candidate whose
174
+ * counterpart the viewer has already connected with (an `accepted`
175
+ * opportunity exists with that person) is dropped permanently. A new
176
+ * discovery run re-minting the same person must not resurface them.
177
+ * Connector-flow candidates (viewer is the introducer) are exempt: being
178
+ * connected with someone doesn't make an intro ask on their behalf stale.
179
+ * 2. **Delivery-ledger dedup** — candidates with a committed delivery row at
180
+ * the same `(opportunityId, status)` key have already been shown. While any
181
+ * fresh (never-shown) candidate exists, shown ones are dropped entirely.
182
+ * 3. **Cooldown re-show** — when *no* fresh candidate survives, already-shown
183
+ * candidates whose latest delivery is at least `cooldownDays` old are
184
+ * returned instead, least-recently-shown first, flagged via
185
+ * `redeliveryIds` so the digest can frame them as reminders.
186
+ *
187
+ * Pure function — callers fetch accepted counterparts and ledger rows.
188
+ *
189
+ * @param candidates - Deduped, confidence-ordered digest candidates.
190
+ * @param opts.viewerId - The digest recipient.
191
+ * @param opts.acceptedCounterpartIds - userIds the viewer already connected with.
192
+ * @param opts.deliveredRows - Committed ledger rows for the candidate ids.
193
+ * @param opts.now - Clock override for tests.
194
+ * @param opts.cooldownDays - Cooldown override (default {@link DIGEST_REDELIVERY_COOLDOWN_DAYS}).
195
+ * @returns Surviving pool plus the set of candidate ids that are cooldown re-shows.
196
+ */
197
+ export declare function selectDigestCandidates<T extends {
198
+ id: string;
199
+ status: string;
200
+ actors: Array<{
201
+ userId: string;
202
+ role: string;
203
+ }>;
204
+ }>(candidates: T[], opts: {
205
+ viewerId: string;
206
+ acceptedCounterpartIds: ReadonlySet<string>;
207
+ deliveredRows: DigestDeliveredRow[];
208
+ now?: Date;
209
+ cooldownDays?: number;
210
+ }): {
211
+ pool: T[];
212
+ redeliveryIds: Set<string>;
213
+ };
158
214
  //# sourceMappingURL=opportunity.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"opportunity.utils.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAKxE,qEAAqE;AACrE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhE,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,oBAAoB,CAAC;IACjC,aAAa,EAAE,oBAAoB,CAAC;CACrC;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,YAAY,CAe5E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,CA+BhG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC/C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAiBT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACnE,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CA2BT;AAED,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAEvE,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB;;;;CAIpB,CAAC;AAEX;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE;IAAE,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACxE,QAAQ,EAAE,MAAM,GACf,YAAY,CAKd;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC/G,aAAa,EAAE,CAAC,EAAE,EAClB,QAAQ,EAAE,MAAM,GACf,CAAC,EAAE,CAwDL;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAC5C,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,cAAc,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjD,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,CAwC5C"}
1
+ {"version":3,"file":"opportunity.utils.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.utils.ts"],"names":[],"mappings":"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"}
@@ -280,4 +280,84 @@ export function deduplicateByPerson(opportunities, viewerId) {
280
280
  }
281
281
  return result;
282
282
  }
283
+ /**
284
+ * Days a digest-delivered opportunity stays suppressed before it becomes
285
+ * eligible for a "still open" reminder re-show (when nothing fresh exists).
286
+ */
287
+ export const DIGEST_REDELIVERY_COOLDOWN_DAYS = 5;
288
+ /**
289
+ * Cross-day digest suppression for scheduled-brief candidates.
290
+ *
291
+ * Three rules, applied in order:
292
+ * 1. **Accepted-counterpart suppression** — a direct-connection candidate whose
293
+ * counterpart the viewer has already connected with (an `accepted`
294
+ * opportunity exists with that person) is dropped permanently. A new
295
+ * discovery run re-minting the same person must not resurface them.
296
+ * Connector-flow candidates (viewer is the introducer) are exempt: being
297
+ * connected with someone doesn't make an intro ask on their behalf stale.
298
+ * 2. **Delivery-ledger dedup** — candidates with a committed delivery row at
299
+ * the same `(opportunityId, status)` key have already been shown. While any
300
+ * fresh (never-shown) candidate exists, shown ones are dropped entirely.
301
+ * 3. **Cooldown re-show** — when *no* fresh candidate survives, already-shown
302
+ * candidates whose latest delivery is at least `cooldownDays` old are
303
+ * returned instead, least-recently-shown first, flagged via
304
+ * `redeliveryIds` so the digest can frame them as reminders.
305
+ *
306
+ * Pure function — callers fetch accepted counterparts and ledger rows.
307
+ *
308
+ * @param candidates - Deduped, confidence-ordered digest candidates.
309
+ * @param opts.viewerId - The digest recipient.
310
+ * @param opts.acceptedCounterpartIds - userIds the viewer already connected with.
311
+ * @param opts.deliveredRows - Committed ledger rows for the candidate ids.
312
+ * @param opts.now - Clock override for tests.
313
+ * @param opts.cooldownDays - Cooldown override (default {@link DIGEST_REDELIVERY_COOLDOWN_DAYS}).
314
+ * @returns Surviving pool plus the set of candidate ids that are cooldown re-shows.
315
+ */
316
+ export function selectDigestCandidates(candidates, opts) {
317
+ const { viewerId, acceptedCounterpartIds } = opts;
318
+ // Rule 1: accepted-counterpart suppression (direct connections only).
319
+ const afterAccepted = candidates.filter((opp) => {
320
+ const viewerIsIntroducer = opp.actors.some((a) => a.role === 'introducer' && a.userId === viewerId);
321
+ if (viewerIsIntroducer)
322
+ return true;
323
+ const counterpart = opp.actors.find((a) => a.userId !== viewerId && a.role !== 'introducer');
324
+ return !counterpart || !acceptedCounterpartIds.has(counterpart.userId);
325
+ });
326
+ if (afterAccepted.length < candidates.length) {
327
+ logger.info(`[selectDigestCandidates] accepted-counterpart suppression dropped ${candidates.length - afterAccepted.length} of ${candidates.length} candidates`);
328
+ }
329
+ // Rule 2: delivery-ledger dedup keyed (opportunityId, deliveredAtStatus).
330
+ // Keep the LATEST committed delivery per key — cooldown measures time since
331
+ // the user last saw the card, not since they first saw it.
332
+ const lastDeliveredByKey = new Map();
333
+ for (const row of opts.deliveredRows) {
334
+ if (!(row.deliveredAt instanceof Date) || Number.isNaN(row.deliveredAt.getTime()))
335
+ continue;
336
+ const key = `${row.opportunityId}:${row.deliveredAtStatus}`;
337
+ const existing = lastDeliveredByKey.get(key);
338
+ if (!existing || row.deliveredAt > existing)
339
+ lastDeliveredByKey.set(key, row.deliveredAt);
340
+ }
341
+ const fresh = afterAccepted.filter((opp) => !lastDeliveredByKey.has(`${opp.id}:${opp.status}`));
342
+ if (fresh.length > 0) {
343
+ if (fresh.length < afterAccepted.length) {
344
+ logger.info(`[selectDigestCandidates] ledger dedup dropped ${afterAccepted.length - fresh.length} already-shown candidates`);
345
+ }
346
+ return { pool: fresh, redeliveryIds: new Set() };
347
+ }
348
+ // Rule 3: nothing fresh — re-show the least-recently-shown candidates past cooldown.
349
+ const cooldownMs = (opts.cooldownDays ?? DIGEST_REDELIVERY_COOLDOWN_DAYS) * 86400000;
350
+ const now = opts.now ?? new Date();
351
+ const cooled = afterAccepted
352
+ .map((opp) => ({ opp, at: lastDeliveredByKey.get(`${opp.id}:${opp.status}`) }))
353
+ .filter((entry) => entry.at instanceof Date && now.getTime() - entry.at.getTime() >= cooldownMs)
354
+ .sort((a, b) => a.at.getTime() - b.at.getTime());
355
+ if (cooled.length > 0) {
356
+ logger.info(`[selectDigestCandidates] no fresh candidates — re-showing ${cooled.length} past-cooldown candidate(s)`);
357
+ }
358
+ return {
359
+ pool: cooled.map((entry) => entry.opp),
360
+ redeliveryIds: new Set(cooled.map((entry) => entry.opp.id)),
361
+ };
362
+ }
283
363
  //# sourceMappingURL=opportunity.utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"opportunity.utils.js","sourceRoot":"/","sources":["opportunity/opportunity.utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Opportunity graph utilities: role derivation from corpus type.\n * Used by the opportunity graph to map lens corpus to opportunity actor roles.\n *\n * With lens-based HyDE, strategy selection is handled automatically by the\n * LensInferrer agent. This file provides corpus-to-role mapping for opportunity actors.\n */\n\nimport type { HydeTargetCorpus } from '../shared/hyde/lens.inferrer.js';\nimport { log } from '../shared/observability/log.js';\n\nconst logger = log.graph.from('SelectByComposition');\n\n/** Actor roles in the opportunity model (agent / patient / peer). */\nexport type OpportunityActorRole = 'agent' | 'patient' | 'peer';\n\n/** Result of mapping a corpus to source and candidate roles. */\nexport interface DerivedRoles {\n sourceRole: OpportunityActorRole;\n candidateRole: OpportunityActorRole;\n}\n\n/**\n * Derive actor roles from the corpus type of a lens match.\n *\n * When a candidate is found via:\n * - \"profiles\" corpus → found by who they are → candidate can help → agent\n * - \"intents\" corpus → found by what they need → candidate needs something → patient\n *\n * @param corpus - The target corpus that produced the match ('profiles' | 'intents')\n * @returns Roles for the source (intent owner) and the candidate (matched user/intent)\n */\nexport function deriveRolesFromCorpus(corpus: HydeTargetCorpus): DerivedRoles {\n switch (corpus) {\n case 'profiles':\n // Source seeks someone who can help → source is patient, candidate can help → agent\n return { sourceRole: 'patient', candidateRole: 'agent' };\n case 'intents':\n // Source offers or needs; candidate has complementary goal → source is agent, candidate is patient\n return { sourceRole: 'agent', candidateRole: 'patient' };\n case 'premises':\n // Premise matches are symmetric: two people whose self-descriptions align.\n // Unlike intents (directional roles), premises express stable identity truths.\n return { sourceRole: 'peer', candidateRole: 'peer' };\n default:\n return { sourceRole: 'peer', candidateRole: 'peer' };\n }\n}\n\n/**\n * Validates opportunity actors: if an opportunity has an introducer, it must have\n * one or two non-introducer actors (1 = 1:1 intro e.g. \"I want to connect with X\";\n * 2 = introducer connecting two others).\n *\n * Also rejects self-matches — the same person occupying both sides of a\n * connection. The discovery/persist pipeline trusts the LLM evaluator's actor\n * list, which can collapse onto a single user; downstream readers then garble\n * identity (e.g. a connect link/greeting rendered in one party's voice while the\n * card shows the viewer \"matched with themselves\"). Two degenerate shapes are\n * blocked here, at the single persist chokepoint:\n * - every userId-bearing non-introducer actor collapses to the same user,\n * e.g. `[X(agent), X(patient)]`\n * - an introducer who is also a participant (\"Amina introduced you to Amina\")\n * Only `userId`-bearing actors are checked; role-only actors (legacy/tests) pass.\n * Duplicate rows for one participant are allowed when at least one other distinct\n * participant is present (some callers model multiple intents as multiple actor rows).\n *\n * @param actors - Array of actors with at least a role and optional userId\n * @throws Error when the actor set is invalid\n */\nexport function validateOpportunityActors(actors: Array<{ userId?: string; role: string }>): void {\n const introducerCount = actors.filter((a) => a.role === 'introducer').length;\n const nonIntroducerCount = actors.filter((a) => a.role !== 'introducer').length;\n\n if (introducerCount > 0 && (nonIntroducerCount < 1 || nonIntroducerCount > 2)) {\n throw new Error(\n 'An opportunity with an introducer must have one or two other actors.'\n );\n }\n\n // Self-match guard. Compare only actors that carry a userId so role-only\n // shapes (used by some callers/tests) are unaffected.\n const introducerUserIds = new Set(\n actors.filter((a) => a.role === 'introducer' && a.userId).map((a) => a.userId as string),\n );\n const nonIntroducerUserIds = actors\n .filter((a) => a.role !== 'introducer' && a.userId)\n .map((a) => a.userId as string);\n\n for (const userId of nonIntroducerUserIds) {\n if (introducerUserIds.has(userId)) {\n throw new Error(\n 'An opportunity actor cannot be both the introducer and a participant (self-match).'\n );\n }\n }\n\n const uniqueNonIntroducerUserIds = new Set(nonIntroducerUserIds);\n if (nonIntroducerUserIds.length > 1 && uniqueNonIntroducerUserIds.size === 1) {\n throw new Error('An opportunity cannot match a user with themselves (duplicate participant).');\n }\n}\n\n/**\n * Read-level ACL: whether a user is an actor on the opportunity and may fetch\n * its details. Intentionally broader than `isActionableForViewer` — a user can\n * read an opportunity they are not currently expected to act on (e.g. an agent\n * viewing an accepted opportunity).\n *\n * The feed graph and debug controller chain both predicates: an opportunity only\n * reaches the home feed if it passes `canUserSeeOpportunity` first, then\n * `isActionableForViewer`. For `agent with introducer at pending`,\n * `canUserSeeOpportunity` returns false (read gate blocks it), so the opportunity\n * never surfaces even though `isActionableForViewer` Rule 4 would return true in\n * isolation. This is by design — the agent is not granted read access through the\n * home path until the introducer path completes (negotiation → accepted).\n *\n * Compact Visibility Rule (from lifecycle doc):\n * - Introducer or peer: always see.\n * - Patient or party: see if (status is not latent, or there is no introducer).\n * - Agent: see if (status is accepted/rejected/expired, or (status is not latent and there is no introducer)).\n */\nexport function canUserSeeOpportunity(\n actors: Array<{ userId: string; role: string }>,\n status: string,\n userId: string\n): boolean {\n const hasIntroducer = actors.some((a) => a.role === 'introducer');\n const userRoles = actors.filter((a) => a.userId === userId).map((a) => a.role);\n if (userRoles.length === 0) return false;\n\n return userRoles.some((role) => {\n if (role === 'introducer') return true;\n if (role === 'peer') return true;\n if (role === 'patient' || role === 'party')\n return status !== 'latent' || !hasIntroducer;\n if (role === 'agent')\n return (\n ['accepted', 'rejected', 'expired'].includes(status) ||\n (status !== 'latent' && !hasIntroducer)\n );\n return false;\n });\n}\n\n/**\n * Whether an opportunity should appear on the viewer's home feed (actionable =\n * has a pending action for this user).\n *\n * Rules (see `docs/Latent Opportunity Lifecycle.md` — Role-Visibility Matrix):\n *\n * (1) `latent`, no introducer → all actors actionable\n * (2) `latent`, introducer `approved !== true` → introducer only\n * (3) `latent`, introducer `approved === true` → all non-introducer actors\n * (4) `pending` (any introducer config) → all non-introducer actors\n * (5) `accepted`/`rejected`/`expired`/`stalled`/`draft`/`negotiating`\n * → never actionable\n *\n * The introducer approval signal is stored on the `introducer`-roled actor's\n * `approved: boolean` field within the opportunity's `actors` JSONB. It flips\n * from `false` to `true` when the introducer approves; status stays `latent`\n * across the flip while a background negotiation runs.\n */\nexport function isActionableForViewer(\n actors: Array<{ userId: string; role: string; approved?: boolean }>,\n status: string,\n viewerId: string\n): boolean {\n const viewerActors = actors.filter((a) => a.userId === viewerId);\n if (viewerActors.length === 0) return false;\n\n const introducer = actors.find((a) => a.role === 'introducer');\n const hasIntroducer = !!introducer;\n const introducerApproved = introducer?.approved === true;\n\n return viewerActors.some(({ role }) => {\n if (role === 'introducer') {\n // Rule 2: introducer sees own latent opp only while not yet approved.\n return status === 'latent' && !introducerApproved;\n }\n\n // Non-introducer actors: patient / party / agent / peer.\n if (status === 'latent') {\n // Rule 1: no introducer → visible.\n // Rule 3: introducer approved → visible.\n return !hasIntroducer || introducerApproved;\n }\n if (status === 'pending') {\n // Rule 4: visible to all non-introducer actors.\n return true;\n }\n // Rule 5: never actionable at terminal or internal statuses.\n return false;\n });\n}\n\n/** Feed category for home composition. */\nexport type FeedCategory = 'connection' | 'connector-flow' | 'expired';\n\n/** Soft targets for home feed composition. */\nexport const FEED_SOFT_TARGETS = {\n connection: 3,\n connectorFlow: 2,\n expired: 2,\n} as const;\n\n/**\n * Classify an actionable opportunity into a feed category.\n * Assumes the opportunity already passed isActionableForViewer or is expired.\n *\n * @param opp - Opportunity with actors and status\n * @param viewerId - The viewing user's ID\n * @returns Feed category\n */\nexport function classifyOpportunity(\n opp: { actors: Array<{ userId: string; role: string }>; status: string },\n viewerId: string\n): FeedCategory {\n if (opp.status === 'expired') return 'expired';\n const viewerIsIntroducer = opp.actors.some((a) => a.userId === viewerId && a.role === 'introducer');\n if (viewerIsIntroducer) return 'connector-flow';\n return 'connection';\n}\n\n/**\n * Select opportunities for the home feed using soft composition targets.\n * Fills each category up to its target, then redistributes unused slots\n * to categories that have more items available. Preserves input order.\n *\n * @param opportunities - Pre-sorted opportunities (by confidence/recency)\n * @param viewerId - The viewing user's ID\n * @returns Composition-balanced subset\n */\nexport function selectByComposition<T extends { actors: Array<{ userId: string; role: string }>; status: string }>(\n opportunities: T[],\n viewerId: string\n): T[] {\n const buckets: Record<FeedCategory, T[]> = {\n connection: [],\n 'connector-flow': [],\n expired: [],\n };\n\n for (const opp of opportunities) {\n const category = classifyOpportunity(opp, viewerId);\n buckets[category].push(opp);\n }\n\n const targets: Record<FeedCategory, number> = {\n connection: FEED_SOFT_TARGETS.connection,\n 'connector-flow': FEED_SOFT_TARGETS.connectorFlow,\n expired: FEED_SOFT_TARGETS.expired,\n };\n\n // First pass: fill each category up to its target\n const selected: Record<FeedCategory, T[]> = {\n connection: buckets.connection.slice(0, targets.connection),\n 'connector-flow': buckets['connector-flow'].slice(0, targets['connector-flow']),\n expired: buckets.expired.slice(0, targets.expired),\n };\n\n // Calculate unused slots and remaining items\n const totalTarget = targets.connection + targets['connector-flow'] + targets.expired;\n const usedSlots = selected.connection.length + selected['connector-flow'].length + selected.expired.length;\n let unusedSlots = totalTarget - usedSlots;\n\n // Second pass: redistribute unused slots to categories with remaining items\n // Priority: connection > connector-flow > expired\n const redistOrder: FeedCategory[] = ['connection', 'connector-flow', 'expired'];\n for (const category of redistOrder) {\n if (unusedSlots <= 0) break;\n const remaining = buckets[category].slice(selected[category].length);\n const take = Math.min(remaining.length, unusedSlots);\n selected[category].push(...remaining.slice(0, take));\n unusedSlots -= take;\n }\n\n // Merge in category priority order: connection > connector-flow > expired\n // Within each category, preserve original input order\n const indexMap = new Map(opportunities.map((opp, i) => [opp, i]));\n const sortByOriginal = (a: T, b: T) => (indexMap.get(a) ?? 0) - (indexMap.get(b) ?? 0);\n selected.connection.sort(sortByOriginal);\n selected['connector-flow'].sort(sortByOriginal);\n selected.expired.sort(sortByOriginal);\n\n logger.info(`[selectByComposition] input=${opportunities.length} buckets: connection=${buckets.connection.length} connector-flow=${buckets['connector-flow'].length} expired=${buckets.expired.length} → selected: connection=${selected.connection.length} connector-flow=${selected['connector-flow'].length} expired=${selected.expired.length}`);\n\n return [\n ...selected.connection,\n ...selected['connector-flow'],\n ...selected.expired,\n ];\n}\n\n/**\n * Deduplicate opportunities so each counterpart appears at most once.\n * Keeps the opportunity with the highest interpretation.confidence per\n * counterpart userId. On ties, the first encountered wins (stable).\n *\n * Counterpart = first actor whose userId !== viewerId and role !== 'introducer'.\n * Opportunities without a derivable counterpart pass through undeduped.\n *\n * @param opportunities - Pre-sorted opportunities (e.g. by confidence/recency)\n * @param viewerId - The viewing user's ID\n * @returns Deduped subset preserving original input order among winners\n */\nexport function deduplicateByPerson<T extends {\n actors: Array<{ userId: string; role: string }>;\n interpretation?: { confidence?: number } | null;\n}>(opportunities: T[], viewerId: string): T[] {\n const bestByCounterpart = new Map<string, { opp: T; index: number }>();\n const noCounterpart: Array<{ opp: T; index: number }> = [];\n\n for (let i = 0; i < opportunities.length; i++) {\n const opp = opportunities[i];\n const counterpart = opp.actors.find(\n (a) => a.userId !== viewerId && a.role !== 'introducer',\n );\n\n if (!counterpart) {\n noCounterpart.push({ opp, index: i });\n continue;\n }\n\n const key = counterpart.userId;\n const existing = bestByCounterpart.get(key);\n\n if (!existing) {\n bestByCounterpart.set(key, { opp, index: i });\n continue;\n }\n\n const newConf = opp.interpretation?.confidence ?? -1;\n const oldConf = existing.opp.interpretation?.confidence ?? -1;\n if (newConf > oldConf) {\n bestByCounterpart.set(key, { opp, index: i });\n }\n }\n\n const all = [...bestByCounterpart.values(), ...noCounterpart];\n all.sort((a, b) => a.index - b.index);\n\n const result = all.map((entry) => entry.opp);\n if (result.length < opportunities.length) {\n logger.info(\n `[deduplicateByPerson] deduped ${opportunities.length} → ${result.length} opportunities`,\n );\n }\n return result;\n}\n"]}
1
+ {"version":3,"file":"opportunity.utils.js","sourceRoot":"/","sources":["opportunity/opportunity.utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Opportunity graph utilities: role derivation from corpus type.\n * Used by the opportunity graph to map lens corpus to opportunity actor roles.\n *\n * With lens-based HyDE, strategy selection is handled automatically by the\n * LensInferrer agent. This file provides corpus-to-role mapping for opportunity actors.\n */\n\nimport type { HydeTargetCorpus } from '../shared/hyde/lens.inferrer.js';\nimport { log } from '../shared/observability/log.js';\n\nconst logger = log.graph.from('SelectByComposition');\n\n/** Actor roles in the opportunity model (agent / patient / peer). */\nexport type OpportunityActorRole = 'agent' | 'patient' | 'peer';\n\n/** Result of mapping a corpus to source and candidate roles. */\nexport interface DerivedRoles {\n sourceRole: OpportunityActorRole;\n candidateRole: OpportunityActorRole;\n}\n\n/**\n * Derive actor roles from the corpus type of a lens match.\n *\n * When a candidate is found via:\n * - \"profiles\" corpus → found by who they are → candidate can help → agent\n * - \"intents\" corpus → found by what they need → candidate needs something → patient\n *\n * @param corpus - The target corpus that produced the match ('profiles' | 'intents')\n * @returns Roles for the source (intent owner) and the candidate (matched user/intent)\n */\nexport function deriveRolesFromCorpus(corpus: HydeTargetCorpus): DerivedRoles {\n switch (corpus) {\n case 'profiles':\n // Source seeks someone who can help → source is patient, candidate can help → agent\n return { sourceRole: 'patient', candidateRole: 'agent' };\n case 'intents':\n // Source offers or needs; candidate has complementary goal → source is agent, candidate is patient\n return { sourceRole: 'agent', candidateRole: 'patient' };\n case 'premises':\n // Premise matches are symmetric: two people whose self-descriptions align.\n // Unlike intents (directional roles), premises express stable identity truths.\n return { sourceRole: 'peer', candidateRole: 'peer' };\n default:\n return { sourceRole: 'peer', candidateRole: 'peer' };\n }\n}\n\n/**\n * Validates opportunity actors: if an opportunity has an introducer, it must have\n * one or two non-introducer actors (1 = 1:1 intro e.g. \"I want to connect with X\";\n * 2 = introducer connecting two others).\n *\n * Also rejects self-matches — the same person occupying both sides of a\n * connection. The discovery/persist pipeline trusts the LLM evaluator's actor\n * list, which can collapse onto a single user; downstream readers then garble\n * identity (e.g. a connect link/greeting rendered in one party's voice while the\n * card shows the viewer \"matched with themselves\"). Two degenerate shapes are\n * blocked here, at the single persist chokepoint:\n * - every userId-bearing non-introducer actor collapses to the same user,\n * e.g. `[X(agent), X(patient)]`\n * - an introducer who is also a participant (\"Amina introduced you to Amina\")\n * Only `userId`-bearing actors are checked; role-only actors (legacy/tests) pass.\n * Duplicate rows for one participant are allowed when at least one other distinct\n * participant is present (some callers model multiple intents as multiple actor rows).\n *\n * @param actors - Array of actors with at least a role and optional userId\n * @throws Error when the actor set is invalid\n */\nexport function validateOpportunityActors(actors: Array<{ userId?: string; role: string }>): void {\n const introducerCount = actors.filter((a) => a.role === 'introducer').length;\n const nonIntroducerCount = actors.filter((a) => a.role !== 'introducer').length;\n\n if (introducerCount > 0 && (nonIntroducerCount < 1 || nonIntroducerCount > 2)) {\n throw new Error(\n 'An opportunity with an introducer must have one or two other actors.'\n );\n }\n\n // Self-match guard. Compare only actors that carry a userId so role-only\n // shapes (used by some callers/tests) are unaffected.\n const introducerUserIds = new Set(\n actors.filter((a) => a.role === 'introducer' && a.userId).map((a) => a.userId as string),\n );\n const nonIntroducerUserIds = actors\n .filter((a) => a.role !== 'introducer' && a.userId)\n .map((a) => a.userId as string);\n\n for (const userId of nonIntroducerUserIds) {\n if (introducerUserIds.has(userId)) {\n throw new Error(\n 'An opportunity actor cannot be both the introducer and a participant (self-match).'\n );\n }\n }\n\n const uniqueNonIntroducerUserIds = new Set(nonIntroducerUserIds);\n if (nonIntroducerUserIds.length > 1 && uniqueNonIntroducerUserIds.size === 1) {\n throw new Error('An opportunity cannot match a user with themselves (duplicate participant).');\n }\n}\n\n/**\n * Read-level ACL: whether a user is an actor on the opportunity and may fetch\n * its details. Intentionally broader than `isActionableForViewer` — a user can\n * read an opportunity they are not currently expected to act on (e.g. an agent\n * viewing an accepted opportunity).\n *\n * The feed graph and debug controller chain both predicates: an opportunity only\n * reaches the home feed if it passes `canUserSeeOpportunity` first, then\n * `isActionableForViewer`. For `agent with introducer at pending`,\n * `canUserSeeOpportunity` returns false (read gate blocks it), so the opportunity\n * never surfaces even though `isActionableForViewer` Rule 4 would return true in\n * isolation. This is by design — the agent is not granted read access through the\n * home path until the introducer path completes (negotiation → accepted).\n *\n * Compact Visibility Rule (from lifecycle doc):\n * - Introducer or peer: always see.\n * - Patient or party: see if (status is not latent, or there is no introducer).\n * - Agent: see if (status is accepted/rejected/expired, or (status is not latent and there is no introducer)).\n */\nexport function canUserSeeOpportunity(\n actors: Array<{ userId: string; role: string }>,\n status: string,\n userId: string\n): boolean {\n const hasIntroducer = actors.some((a) => a.role === 'introducer');\n const userRoles = actors.filter((a) => a.userId === userId).map((a) => a.role);\n if (userRoles.length === 0) return false;\n\n return userRoles.some((role) => {\n if (role === 'introducer') return true;\n if (role === 'peer') return true;\n if (role === 'patient' || role === 'party')\n return status !== 'latent' || !hasIntroducer;\n if (role === 'agent')\n return (\n ['accepted', 'rejected', 'expired'].includes(status) ||\n (status !== 'latent' && !hasIntroducer)\n );\n return false;\n });\n}\n\n/**\n * Whether an opportunity should appear on the viewer's home feed (actionable =\n * has a pending action for this user).\n *\n * Rules (see `docs/Latent Opportunity Lifecycle.md` — Role-Visibility Matrix):\n *\n * (1) `latent`, no introducer → all actors actionable\n * (2) `latent`, introducer `approved !== true` → introducer only\n * (3) `latent`, introducer `approved === true` → all non-introducer actors\n * (4) `pending` (any introducer config) → all non-introducer actors\n * (5) `accepted`/`rejected`/`expired`/`stalled`/`draft`/`negotiating`\n * → never actionable\n *\n * The introducer approval signal is stored on the `introducer`-roled actor's\n * `approved: boolean` field within the opportunity's `actors` JSONB. It flips\n * from `false` to `true` when the introducer approves; status stays `latent`\n * across the flip while a background negotiation runs.\n */\nexport function isActionableForViewer(\n actors: Array<{ userId: string; role: string; approved?: boolean }>,\n status: string,\n viewerId: string\n): boolean {\n const viewerActors = actors.filter((a) => a.userId === viewerId);\n if (viewerActors.length === 0) return false;\n\n const introducer = actors.find((a) => a.role === 'introducer');\n const hasIntroducer = !!introducer;\n const introducerApproved = introducer?.approved === true;\n\n return viewerActors.some(({ role }) => {\n if (role === 'introducer') {\n // Rule 2: introducer sees own latent opp only while not yet approved.\n return status === 'latent' && !introducerApproved;\n }\n\n // Non-introducer actors: patient / party / agent / peer.\n if (status === 'latent') {\n // Rule 1: no introducer → visible.\n // Rule 3: introducer approved → visible.\n return !hasIntroducer || introducerApproved;\n }\n if (status === 'pending') {\n // Rule 4: visible to all non-introducer actors.\n return true;\n }\n // Rule 5: never actionable at terminal or internal statuses.\n return false;\n });\n}\n\n/** Feed category for home composition. */\nexport type FeedCategory = 'connection' | 'connector-flow' | 'expired';\n\n/** Soft targets for home feed composition. */\nexport const FEED_SOFT_TARGETS = {\n connection: 3,\n connectorFlow: 2,\n expired: 2,\n} as const;\n\n/**\n * Classify an actionable opportunity into a feed category.\n * Assumes the opportunity already passed isActionableForViewer or is expired.\n *\n * @param opp - Opportunity with actors and status\n * @param viewerId - The viewing user's ID\n * @returns Feed category\n */\nexport function classifyOpportunity(\n opp: { actors: Array<{ userId: string; role: string }>; status: string },\n viewerId: string\n): FeedCategory {\n if (opp.status === 'expired') return 'expired';\n const viewerIsIntroducer = opp.actors.some((a) => a.userId === viewerId && a.role === 'introducer');\n if (viewerIsIntroducer) return 'connector-flow';\n return 'connection';\n}\n\n/**\n * Select opportunities for the home feed using soft composition targets.\n * Fills each category up to its target, then redistributes unused slots\n * to categories that have more items available. Preserves input order.\n *\n * @param opportunities - Pre-sorted opportunities (by confidence/recency)\n * @param viewerId - The viewing user's ID\n * @returns Composition-balanced subset\n */\nexport function selectByComposition<T extends { actors: Array<{ userId: string; role: string }>; status: string }>(\n opportunities: T[],\n viewerId: string\n): T[] {\n const buckets: Record<FeedCategory, T[]> = {\n connection: [],\n 'connector-flow': [],\n expired: [],\n };\n\n for (const opp of opportunities) {\n const category = classifyOpportunity(opp, viewerId);\n buckets[category].push(opp);\n }\n\n const targets: Record<FeedCategory, number> = {\n connection: FEED_SOFT_TARGETS.connection,\n 'connector-flow': FEED_SOFT_TARGETS.connectorFlow,\n expired: FEED_SOFT_TARGETS.expired,\n };\n\n // First pass: fill each category up to its target\n const selected: Record<FeedCategory, T[]> = {\n connection: buckets.connection.slice(0, targets.connection),\n 'connector-flow': buckets['connector-flow'].slice(0, targets['connector-flow']),\n expired: buckets.expired.slice(0, targets.expired),\n };\n\n // Calculate unused slots and remaining items\n const totalTarget = targets.connection + targets['connector-flow'] + targets.expired;\n const usedSlots = selected.connection.length + selected['connector-flow'].length + selected.expired.length;\n let unusedSlots = totalTarget - usedSlots;\n\n // Second pass: redistribute unused slots to categories with remaining items\n // Priority: connection > connector-flow > expired\n const redistOrder: FeedCategory[] = ['connection', 'connector-flow', 'expired'];\n for (const category of redistOrder) {\n if (unusedSlots <= 0) break;\n const remaining = buckets[category].slice(selected[category].length);\n const take = Math.min(remaining.length, unusedSlots);\n selected[category].push(...remaining.slice(0, take));\n unusedSlots -= take;\n }\n\n // Merge in category priority order: connection > connector-flow > expired\n // Within each category, preserve original input order\n const indexMap = new Map(opportunities.map((opp, i) => [opp, i]));\n const sortByOriginal = (a: T, b: T) => (indexMap.get(a) ?? 0) - (indexMap.get(b) ?? 0);\n selected.connection.sort(sortByOriginal);\n selected['connector-flow'].sort(sortByOriginal);\n selected.expired.sort(sortByOriginal);\n\n logger.info(`[selectByComposition] input=${opportunities.length} buckets: connection=${buckets.connection.length} connector-flow=${buckets['connector-flow'].length} expired=${buckets.expired.length} → selected: connection=${selected.connection.length} connector-flow=${selected['connector-flow'].length} expired=${selected.expired.length}`);\n\n return [\n ...selected.connection,\n ...selected['connector-flow'],\n ...selected.expired,\n ];\n}\n\n/**\n * Deduplicate opportunities so each counterpart appears at most once.\n * Keeps the opportunity with the highest interpretation.confidence per\n * counterpart userId. On ties, the first encountered wins (stable).\n *\n * Counterpart = first actor whose userId !== viewerId and role !== 'introducer'.\n * Opportunities without a derivable counterpart pass through undeduped.\n *\n * @param opportunities - Pre-sorted opportunities (e.g. by confidence/recency)\n * @param viewerId - The viewing user's ID\n * @returns Deduped subset preserving original input order among winners\n */\nexport function deduplicateByPerson<T extends {\n actors: Array<{ userId: string; role: string }>;\n interpretation?: { confidence?: number } | null;\n}>(opportunities: T[], viewerId: string): T[] {\n const bestByCounterpart = new Map<string, { opp: T; index: number }>();\n const noCounterpart: Array<{ opp: T; index: number }> = [];\n\n for (let i = 0; i < opportunities.length; i++) {\n const opp = opportunities[i];\n const counterpart = opp.actors.find(\n (a) => a.userId !== viewerId && a.role !== 'introducer',\n );\n\n if (!counterpart) {\n noCounterpart.push({ opp, index: i });\n continue;\n }\n\n const key = counterpart.userId;\n const existing = bestByCounterpart.get(key);\n\n if (!existing) {\n bestByCounterpart.set(key, { opp, index: i });\n continue;\n }\n\n const newConf = opp.interpretation?.confidence ?? -1;\n const oldConf = existing.opp.interpretation?.confidence ?? -1;\n if (newConf > oldConf) {\n bestByCounterpart.set(key, { opp, index: i });\n }\n }\n\n const all = [...bestByCounterpart.values(), ...noCounterpart];\n all.sort((a, b) => a.index - b.index);\n\n const result = all.map((entry) => entry.opp);\n if (result.length < opportunities.length) {\n logger.info(\n `[deduplicateByPerson] deduped ${opportunities.length} → ${result.length} opportunities`,\n );\n }\n return result;\n}\n\n/**\n * Days a digest-delivered opportunity stays suppressed before it becomes\n * eligible for a \"still open\" reminder re-show (when nothing fresh exists).\n */\nexport const DIGEST_REDELIVERY_COOLDOWN_DAYS = 5;\n\n/** Committed delivery row shape consumed by {@link selectDigestCandidates}. */\nexport interface DigestDeliveredRow {\n opportunityId: string;\n deliveredAtStatus: string;\n deliveredAt: Date;\n}\n\n/**\n * Cross-day digest suppression for scheduled-brief candidates.\n *\n * Three rules, applied in order:\n * 1. **Accepted-counterpart suppression** — a direct-connection candidate whose\n * counterpart the viewer has already connected with (an `accepted`\n * opportunity exists with that person) is dropped permanently. A new\n * discovery run re-minting the same person must not resurface them.\n * Connector-flow candidates (viewer is the introducer) are exempt: being\n * connected with someone doesn't make an intro ask on their behalf stale.\n * 2. **Delivery-ledger dedup** — candidates with a committed delivery row at\n * the same `(opportunityId, status)` key have already been shown. While any\n * fresh (never-shown) candidate exists, shown ones are dropped entirely.\n * 3. **Cooldown re-show** — when *no* fresh candidate survives, already-shown\n * candidates whose latest delivery is at least `cooldownDays` old are\n * returned instead, least-recently-shown first, flagged via\n * `redeliveryIds` so the digest can frame them as reminders.\n *\n * Pure function — callers fetch accepted counterparts and ledger rows.\n *\n * @param candidates - Deduped, confidence-ordered digest candidates.\n * @param opts.viewerId - The digest recipient.\n * @param opts.acceptedCounterpartIds - userIds the viewer already connected with.\n * @param opts.deliveredRows - Committed ledger rows for the candidate ids.\n * @param opts.now - Clock override for tests.\n * @param opts.cooldownDays - Cooldown override (default {@link DIGEST_REDELIVERY_COOLDOWN_DAYS}).\n * @returns Surviving pool plus the set of candidate ids that are cooldown re-shows.\n */\nexport function selectDigestCandidates<T extends {\n id: string;\n status: string;\n actors: Array<{ userId: string; role: string }>;\n}>(\n candidates: T[],\n opts: {\n viewerId: string;\n acceptedCounterpartIds: ReadonlySet<string>;\n deliveredRows: DigestDeliveredRow[];\n now?: Date;\n cooldownDays?: number;\n },\n): { pool: T[]; redeliveryIds: Set<string> } {\n const { viewerId, acceptedCounterpartIds } = opts;\n\n // Rule 1: accepted-counterpart suppression (direct connections only).\n const afterAccepted = candidates.filter((opp) => {\n const viewerIsIntroducer = opp.actors.some(\n (a) => a.role === 'introducer' && a.userId === viewerId,\n );\n if (viewerIsIntroducer) return true;\n const counterpart = opp.actors.find(\n (a) => a.userId !== viewerId && a.role !== 'introducer',\n );\n return !counterpart || !acceptedCounterpartIds.has(counterpart.userId);\n });\n if (afterAccepted.length < candidates.length) {\n logger.info(\n `[selectDigestCandidates] accepted-counterpart suppression dropped ${candidates.length - afterAccepted.length} of ${candidates.length} candidates`,\n );\n }\n\n // Rule 2: delivery-ledger dedup keyed (opportunityId, deliveredAtStatus).\n // Keep the LATEST committed delivery per key — cooldown measures time since\n // the user last saw the card, not since they first saw it.\n const lastDeliveredByKey = new Map<string, Date>();\n for (const row of opts.deliveredRows) {\n if (!(row.deliveredAt instanceof Date) || Number.isNaN(row.deliveredAt.getTime())) continue;\n const key = `${row.opportunityId}:${row.deliveredAtStatus}`;\n const existing = lastDeliveredByKey.get(key);\n if (!existing || row.deliveredAt > existing) lastDeliveredByKey.set(key, row.deliveredAt);\n }\n\n const fresh = afterAccepted.filter((opp) => !lastDeliveredByKey.has(`${opp.id}:${opp.status}`));\n if (fresh.length > 0) {\n if (fresh.length < afterAccepted.length) {\n logger.info(\n `[selectDigestCandidates] ledger dedup dropped ${afterAccepted.length - fresh.length} already-shown candidates`,\n );\n }\n return { pool: fresh, redeliveryIds: new Set<string>() };\n }\n\n // Rule 3: nothing fresh — re-show the least-recently-shown candidates past cooldown.\n const cooldownMs = (opts.cooldownDays ?? DIGEST_REDELIVERY_COOLDOWN_DAYS) * 86_400_000;\n const now = opts.now ?? new Date();\n const cooled = afterAccepted\n .map((opp) => ({ opp, at: lastDeliveredByKey.get(`${opp.id}:${opp.status}`) }))\n .filter((entry): entry is { opp: T; at: Date } =>\n entry.at instanceof Date && now.getTime() - entry.at.getTime() >= cooldownMs,\n )\n .sort((a, b) => a.at.getTime() - b.at.getTime());\n\n if (cooled.length > 0) {\n logger.info(\n `[selectDigestCandidates] no fresh candidates — re-showing ${cooled.length} past-cooldown candidate(s)`,\n );\n }\n return {\n pool: cooled.map((entry) => entry.opp),\n redeliveryIds: new Set(cooled.map((entry) => entry.opp.id)),\n };\n}\n"]}
@@ -312,6 +312,24 @@ export interface ToolDeps {
312
312
  chatSession?: ChatSessionReader;
313
313
  /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */
314
314
  chatSummary?: ChatSummaryReader;
315
+ /**
316
+ * Test seam for opportunity discovery helpers. Production compositions leave
317
+ * this unset so tools call the real discovery module directly.
318
+ */
319
+ opportunityDiscovery?: {
320
+ runDiscoverFromQuery?: (input: unknown) => Promise<unknown>;
321
+ continueDiscovery?: (input: unknown) => Promise<unknown>;
322
+ };
323
+ /**
324
+ * Test seam for opportunity card presentation helpers. Production
325
+ * compositions leave this unset so tools construct the real presenter.
326
+ */
327
+ opportunityPresentation?: {
328
+ createPresenter?: () => {
329
+ presentHomeCard(input: unknown): Promise<unknown>;
330
+ };
331
+ gatherPresenterContext?: (...args: unknown[]) => Promise<unknown>;
332
+ };
315
333
  /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */
316
334
  chatMessageWriter?: ChatMessageWriter;
317
335
  /** Decision-question generator. Optional; consumers fall back to no `questions`. */
@@ -1 +1 @@
1
- {"version":3,"file":"tool.helpers.d.ts","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,wBAAwB,EACzB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAC/F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE/F,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,IAAI,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAMD,6DAA6D;AAE7D,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,CAAC;AAMrE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAElC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,cAAc,CAAC;IAC5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,CAAC;IACF,oBAAoB,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1C,oFAAoF;IACpF,YAAY,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,4GAA4G;IAC5G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;CACpC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,QAAQ,EAAE,0BAA0B,CAAC;IACrC,uHAAuH;IACvH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,0IAA0I;IAC1I,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+GAA+G;IAC/G,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,qDAAqD;IACrD,KAAK,EAAE,KAAK,CAAC;IACb,sEAAsE;IACtE,SAAS,EAAE,SAAS,CAAC;IACrB,mEAAmE;IACnE,WAAW,EAAE,kBAAkB,CAAC;IAChC,kFAAkF;IAClF,WAAW,EAAE,gBAAgB,CAAC;IAC9B,qCAAqC;IACrC,cAAc,EAAE,qBAAqB,CAAC;IACtC,4DAA4D;IAC5D,WAAW,EAAE,iBAAiB,CAAC;IAC/B,kGAAkG;IAClG,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,6FAA6F;IAC7F,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,qDAAqD;IACrD,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,mBAAmB,EAAE,wBAAwB,CAAC;IAC9C,kEAAkE;IAClE,mBAAmB,EAAE;QACnB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;YACvD,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;YACpB,gBAAgB,EAAE,MAAM,CAAC;SAC1B,CAAC,CAAC;KACJ,CAAC;IACF,+CAA+C;IAC/C,kBAAkB,EAAE,CAAC,EAAE,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,KAAK,YAAY,CAAC;IACrF,iDAAiD;IACjD,oBAAoB,EAAE,CAAC,EAAE,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,cAAc,CAAC;IACpI,sFAAsF;IACtF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,mGAAmG;IACnG,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,6FAA6F;IAC7F,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,+EAA+E;IAC/E,6BAA6B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,wGAAwG;IACxG,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,oGAAoG;IACpG,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6FAA6F;IAC7F,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,8GAA8G;IAC9G,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,iHAAiH;IACjH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAC1F,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;KAC9B,CAAC,CAAC;IACH,sGAAsG;IACtG,aAAa,CAAC,EAAE;QACd,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;KAC3D,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC;AAEzG;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAG7B,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,2BAA2B;gBAFxF,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,2BAA2B;CAK3F;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,QAAQ,EAAE,IAAI,CACZ,0BAA0B,EAC1B,SAAS,GAAG,YAAY,GAAG,uBAAuB,GAAG,sBAAsB,GAAG,YAAY,GAAG,cAAc,GAAG,iBAAiB,CAChI,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgG/B;AAMD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC;IACf,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,mBAAmB,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAE1F,KAAK,GAAG,CAAC;AAEV;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,mBAAmB,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACvF;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAM1D;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,oFAAoF;IACpF,QAAQ,EAAE,0BAA0B,CAAC;IACrC,mFAAmF;IACnF,MAAM,EAAE,YAAY,CAAC;IACrB,sGAAsG;IACtG,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,qCAAqC,EAAE,QAAQ,CAAC;IACjE,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,qBAAqB,CAAC;IACtC,mBAAmB,EAAE;QACnB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;YACvD,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;YACpB,gBAAgB,EAAE,MAAM,CAAC;SAC1B,CAAC,CAAC;KACJ,CAAC;IACF,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,mBAAmB,EAAE,wBAAwB,CAAC;IAC9C,qFAAqF;IACrF,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,kGAAkG;IAClG,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iEAAiE;QACjE,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,oEAAoE;QACpE,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,KACE,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;IACvC,6FAA6F;IAC7F,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,mGAAmG;IACnG,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,6FAA6F;IAC7F,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,+EAA+E;IAC/E,6BAA6B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,wGAAwG;IACxG,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oGAAoG;IACpG,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6FAA6F;IAC7F,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,8GAA8G;IAC9G,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,iHAAiH;IACjH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAC1F,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;KAC9B,CAAC,CAAC;IACH,sGAAsG;IACtG,aAAa,CAAC,EAAE;QACd,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;KAC3D,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE,aAAa,CAAC;QACvB,MAAM,EAAE,aAAa,CAAC;QACtB,KAAK,EAAE,aAAa,CAAC;QACrB,iBAAiB,EAAE,aAAa,CAAC;QACjC,WAAW,EAAE,aAAa,CAAC;QAC3B,WAAW,EAAE,wBAAwB,CAAC;QACtC,OAAO,EAAE,aAAa,CAAC;KACxB,CAAC;IACF;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACtB,WAAW,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACtF,QAAQ,EAAE,KAAK,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,eAAe,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACjE,KAAK,OAAO,CAAC;QAAE,gBAAgB,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACtD;AAMD,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAE1C;AAED,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GACpF,MAAM,CAMR;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAMT;AAgBD,uFAAuF;AACvF,eAAO,MAAM,UAAU,QAAoE,CAAC;AAE5F;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE;IAAE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;CAAE,EACnF,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAMnB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA2BlD;AAgBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAe7D"}
1
+ {"version":3,"file":"tool.helpers.d.ts","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,wBAAwB,EACzB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAC/F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE/F,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,IAAI,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAMD,6DAA6D;AAE7D,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,CAAC;AAMrE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAElC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,cAAc,CAAC;IAC5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,CAAC;IACF,oBAAoB,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1C,oFAAoF;IACpF,YAAY,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,4GAA4G;IAC5G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;CACpC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,QAAQ,EAAE,0BAA0B,CAAC;IACrC,uHAAuH;IACvH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,0IAA0I;IAC1I,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+GAA+G;IAC/G,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,qDAAqD;IACrD,KAAK,EAAE,KAAK,CAAC;IACb,sEAAsE;IACtE,SAAS,EAAE,SAAS,CAAC;IACrB,mEAAmE;IACnE,WAAW,EAAE,kBAAkB,CAAC;IAChC,kFAAkF;IAClF,WAAW,EAAE,gBAAgB,CAAC;IAC9B,qCAAqC;IACrC,cAAc,EAAE,qBAAqB,CAAC;IACtC,4DAA4D;IAC5D,WAAW,EAAE,iBAAiB,CAAC;IAC/B,kGAAkG;IAClG,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,6FAA6F;IAC7F,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,qDAAqD;IACrD,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,mBAAmB,EAAE,wBAAwB,CAAC;IAC9C,kEAAkE;IAClE,mBAAmB,EAAE;QACnB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;YACvD,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;YACpB,gBAAgB,EAAE,MAAM,CAAC;SAC1B,CAAC,CAAC;KACJ,CAAC;IACF,+CAA+C;IAC/C,kBAAkB,EAAE,CAAC,EAAE,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,KAAK,YAAY,CAAC;IACrF,iDAAiD;IACjD,oBAAoB,EAAE,CAAC,EAAE,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,cAAc,CAAC;IACpI,sFAAsF;IACtF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,mGAAmG;IACnG,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,6FAA6F;IAC7F,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,+EAA+E;IAC/E,6BAA6B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,wGAAwG;IACxG,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,oGAAoG;IACpG,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6FAA6F;IAC7F,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,8GAA8G;IAC9G,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,iHAAiH;IACjH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAC1F,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;KAC9B,CAAC,CAAC;IACH,sGAAsG;IACtG,aAAa,CAAC,EAAE;QACd,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;KAC3D,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC;AAEzG;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAG7B,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,2BAA2B;gBAFxF,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,2BAA2B;CAK3F;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,QAAQ,EAAE,IAAI,CACZ,0BAA0B,EAC1B,SAAS,GAAG,YAAY,GAAG,uBAAuB,GAAG,sBAAsB,GAAG,YAAY,GAAG,cAAc,GAAG,iBAAiB,CAChI,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgG/B;AAMD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC;IACf,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,mBAAmB,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAE1F,KAAK,GAAG,CAAC;AAEV;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,mBAAmB,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACvF;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAM1D;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,oFAAoF;IACpF,QAAQ,EAAE,0BAA0B,CAAC;IACrC,mFAAmF;IACnF,MAAM,EAAE,YAAY,CAAC;IACrB,sGAAsG;IACtG,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,qCAAqC,EAAE,QAAQ,CAAC;IACjE,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,qBAAqB,CAAC;IACtC,mBAAmB,EAAE;QACnB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;YACvD,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;YACpB,gBAAgB,EAAE,MAAM,CAAC;SAC1B,CAAC,CAAC;KACJ,CAAC;IACF,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,mBAAmB,EAAE,wBAAwB,CAAC;IAC9C,qFAAqF;IACrF,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,kGAAkG;IAClG,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;OAGG;IACH,oBAAoB,CAAC,EAAE;QACrB,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5D,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1D,CAAC;IACF;;;OAGG;IACH,uBAAuB,CAAC,EAAE;QACxB,eAAe,CAAC,EAAE,MAAM;YAAE,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAC;QAC9E,sBAAsB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KACnE,CAAC;IACF,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iEAAiE;QACjE,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,oEAAoE;QACpE,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,KACE,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;IACvC,6FAA6F;IAC7F,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,mGAAmG;IACnG,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,6FAA6F;IAC7F,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,+EAA+E;IAC/E,6BAA6B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,wGAAwG;IACxG,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oGAAoG;IACpG,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6FAA6F;IAC7F,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,8GAA8G;IAC9G,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,iHAAiH;IACjH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAC1F,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;KAC9B,CAAC,CAAC;IACH,sGAAsG;IACtG,aAAa,CAAC,EAAE;QACd,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxD,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;KAC3D,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE,aAAa,CAAC;QACvB,MAAM,EAAE,aAAa,CAAC;QACtB,KAAK,EAAE,aAAa,CAAC;QACrB,iBAAiB,EAAE,aAAa,CAAC;QACjC,WAAW,EAAE,aAAa,CAAC;QAC3B,WAAW,EAAE,wBAAwB,CAAC;QACtC,OAAO,EAAE,aAAa,CAAC;KACxB,CAAC;IACF;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACtB,WAAW,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACtF,QAAQ,EAAE,KAAK,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,eAAe,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACjE,KAAK,OAAO,CAAC;QAAE,gBAAgB,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACtD;AAMD,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAE1C;AAED,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GACpF,MAAM,CAMR;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAMT;AAgBD,uFAAuF;AACvF,eAAO,MAAM,UAAU,QAAoE,CAAC;AAE5F;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE;IAAE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;CAAE,EACnF,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAMnB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA2BlD;AAgBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAe7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool.helpers.js","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { ModelConfig } from \"./model.config.js\";\nimport type { ProfileDocument } from \"../schemas/profile.schema.js\";\nimport type {\n ChatGraphCompositeDatabase,\n NetworkMembership,\n UserRecord,\n UserDatabase,\n SystemDatabase,\n NegotiationGraphDatabase,\n} from \"../interfaces/database.interface.js\";\nimport type { Scraper } from \"../interfaces/scraper.interface.js\";\nimport type { Cache, HydeCache } from \"../interfaces/cache.interface.js\";\nimport type { CompiledOpportunityGraph } from \"../../opportunity/opportunity.discover.js\";\nimport type { IntegrationAdapter } from \"../interfaces/integration.interface.js\";\nimport type { ContactServiceAdapter } from \"../interfaces/contact.interface.js\";\nimport type { ProfileEnricher } from \"../interfaces/enrichment.interface.js\";\nimport type { IntentGraphQueue } from \"../interfaces/queue.interface.js\";\nimport type { ChatSessionReader } from \"../interfaces/chat-session.interface.js\";\nimport type { ChatSummaryReader } from \"../interfaces/chat-summary.interface.js\";\nimport type { ChatMessageWriter } from \"../interfaces/chat-message-writer.interface.js\";\nimport type { QuestionGeneratorReader } from \"../interfaces/question-generator.interface.js\";\nimport type { NegotiationSummaryReader } from \"../interfaces/negotiation-summary.interface.js\";\nimport type { Embedder } from \"../interfaces/embedder.interface.js\";\nimport type { AgentDatabase } from \"../interfaces/agent.interface.js\";\nimport type { NegotiationTimeoutQueue } from \"../interfaces/negotiation-events.interface.js\";\nimport type { AgentDispatcher } from \"../interfaces/agent-dispatcher.interface.js\";\nimport type { DeliveryLedger } from \"../interfaces/delivery-ledger.interface.js\";\nimport type { MintConnectLink } from \"../interfaces/connect-link.interface.js\";\nimport type { QuestionerDatabase } from \"../interfaces/questioner.interface.js\";\nimport type { QuestionerEnqueueFn } from \"../../questioner/questioner.types.js\";\nimport type { PendingQuestionSummary } from \"../schemas/pending-question.schema.js\";\nimport type { QuestionMode } from \"../schemas/question.schema.js\";\nimport type { DiscoveryRunQueue, DiscoveryRunStore } from \"../interfaces/discovery-run.interface.js\";\nimport type { ProfileRunQueue, ProfileRunStore } from \"../interfaces/profile-run.interface.js\";\n\nexport type ProfileContext = ProfileDocument | null;\n\nexport interface ToolErrorReport {\n operation: string;\n subsystem?: string;\n toolName?: string;\n userId?: string;\n tags?: Record<string, string | number | boolean | null | undefined>;\n context?: Record<string, unknown>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// COMPILED GRAPH TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Minimal interface for an invokable compiled LangGraph. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type CompiledGraph = { invoke: (input: any) => Promise<any> };\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL CONTEXT TYPES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolved context available to every tool handler.\n * Contains the current user and optional index identity, resolved from DB at init.\n * The LLM can see this context (via system prompt) but cannot change it.\n */\nexport interface ResolvedToolContext {\n // Legacy flat fields (kept for backwards compatibility in tools/prompts).\n userId: string;\n userName: string;\n userEmail: string;\n networkId?: string;\n indexName?: string;\n /** True when chat is index-scoped and the user owns the index. */\n isOwner?: boolean;\n // Rich identity context for prompt/tool orchestration.\n user: UserRecord;\n userProfile: ProfileContext;\n userNetworks: NetworkMembership[];\n /**\n * The set of index IDs this caller can reach in the current request.\n * For unscoped chats: every index the user is a member of.\n * For network-scoped agents: `[boundNetwork, personalIndex]`.\n * This is the same set used to clamp the DB-level systemDb.\n * Tools that filter intents/profiles default to this set; `networkId` is\n * the \"primary focus\" hint, not a read filter.\n */\n indexScope: string[];\n scopedIndex?: {\n id: string;\n title: string;\n prompt: string | null;\n type?: string;\n metadata?: Record<string, unknown>;\n permissions?: Record<string, unknown>;\n };\n scopedMembershipRole?: \"owner\" | \"member\";\n /** True when user has not completed onboarding (onboarding.completedAt is null). */\n isOnboarding: boolean;\n /** True when the user has a non-empty name. */\n hasName: boolean;\n /** Chat session ID when tools are used in a chat; used for draft opportunities (context.conversationId). */\n sessionId?: string;\n /** True when the request originates from an MCP transport (no interactive UI available). */\n isMcp?: boolean;\n /** Agent ID when the request originates from an API key linked to an agent. */\n agentId?: string;\n /**\n * Receiver's rendering surface declared by the MCP client via the\n * `x-index-surface` request header. `'telegram'` means the MCP response is\n * being rendered inside a Telegram chat; anything\n * else (including `undefined`) is treated as web. Forwarded into\n * `mintConnectLink` so the click-time redirect can branch.\n */\n clientSurface?: 'telegram' | 'web';\n}\n\n/**\n * Dependencies passed when creating tools for a user session.\n * Includes DB adapters, embedder, and scraper.\n *\n * Note: userDb and systemDb are optional inputs - if not provided, createChatTools\n * will create them internally from the chatDatabaseAdapter singleton.\n */\nexport interface ToolContext {\n userId: string;\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. Created internally if not provided. */\n userDb?: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. Created internally if not provided. */\n systemDb?: SystemDatabase;\n embedder: Embedder;\n scraper: Scraper;\n /** When set, chat is scoped to this index; tools use it as default for read_intents and create_intent. */\n networkId?: string;\n /**\n * Optional override of the resolved `indexScope`. `resolveChatContext` always\n * computes `indexScope` from the user's memberships (clamped to [bound,\n * personal] when `networkId` is set). When the caller has already computed\n * a clamped scope — notably the MCP server, which clamps via\n * `applyNetworkScopeToContext` for network-scoped agents — passing it on\n * `ToolContext.indexScope` causes `createChatTools` (in tool.factory.ts) to\n * override `resolvedContext.indexScope` with this value rather than the\n * freshly computed one. See ResolvedToolContext.indexScope for the\n * resolved-side semantics.\n */\n indexScope?: string[];\n /** Chat session ID when creating tools for a chat; enables draft opportunities with context.conversationId. */\n sessionId?: string;\n\n // ─── Protocol-level dependencies (injected by composition root) ──────────\n /** General-purpose cache (e.g. for tool results). */\n cache: Cache;\n /** Dedicated cache for HyDE graph (may be same instance as cache). */\n hydeCache: HydeCache;\n /** External integration platform adapter (OAuth, tool actions). */\n integration: IntegrationAdapter;\n /** Queue for enqueuing follow-up intent processing (HyDE generation/deletion). */\n intentQueue: IntentGraphQueue;\n /** Contact management operations. */\n contactService: ContactServiceAdapter;\n /** Chat session reader for loading conversation history. */\n chatSession: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline.\n * Injected by the composition root when QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Profile enrichment from external data sources. */\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Integration importer for bulk contact import from toolkits. */\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n /** Factory for user-scoped database access. */\n createUserDatabase: (db: ChatGraphCompositeDatabase, userId: string) => UserDatabase;\n /** Factory for system-scoped database access. */\n createSystemDatabase: (db: ChatGraphCompositeDatabase, userId: string, indexScope: string[], embedder?: Embedder) => SystemDatabase;\n /** Optional runtime LLM config. Pass to override env vars for API key, model, etc. */\n modelConfig?: ModelConfig;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Enqueue a negotiate_existing job after introducer approval (optional). */\n queueNegotiateExisting?: (opportunityId: string, userId: string) => Promise<void>;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Persistence for structured questions generated by the QuestionerAgent (optional). */\n questionerDatabase?: QuestionerDatabase;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n}\n\n/**\n * All external dependencies needed to initialize the protocol tool engine.\n * The host application (composition root) must provide concrete implementations.\n * This is the subset of ToolContext that is NOT per-request (no userId, indexId, sessionId).\n */\nexport type ProtocolDeps = Omit<ToolContext, 'userId' | 'indexId' | 'sessionId' | 'userDb' | 'systemDb'>;\n\n/**\n * Thrown when a requested chat scope is invalid for the authenticated user.\n * Controllers can map this to an HTTP status code.\n */\nexport class ChatContextAccessError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: \"USER_NOT_FOUND\" | \"INDEX_NOT_FOUND\" | \"INDEX_MEMBERSHIP_REQUIRED\"\n ) {\n super(message);\n this.name = \"ChatContextAccessError\";\n }\n}\n\n/**\n * Resolve the canonical context used by chat tools and system prompt.\n * This preloads user identity, profile, index memberships, and scoped index role.\n */\nexport async function resolveChatContext(params: {\n database: Pick<\n ChatGraphCompositeDatabase,\n \"getUser\" | \"getProfile\" | \"getNetworkMemberships\" | \"getNetworkMembership\" | \"getNetwork\" | \"isIndexOwner\" | \"isNetworkMember\"\n >;\n userId: string;\n networkId?: string;\n /** Chat session ID for draft opportunities (stored as context.conversationId). */\n sessionId?: string;\n}): Promise<ResolvedToolContext> {\n const { database, userId, networkId, sessionId } = params;\n\n const [user, rawProfile, userNetworks] = await Promise.all([\n database.getUser(userId),\n database.getProfile(userId),\n database.getNetworkMemberships(userId),\n ]);\n\n const userProfile: ProfileContext = rawProfile ?? null;\n\n if (!user) {\n throw new ChatContextAccessError(\n \"User not found\",\n 404,\n \"USER_NOT_FOUND\"\n );\n }\n\n let scopedIndex: ResolvedToolContext[\"scopedIndex\"] = undefined;\n let scopedMembershipRole: ResolvedToolContext[\"scopedMembershipRole\"] = undefined;\n let isOwner = false;\n let indexName: string | undefined;\n\n if (networkId) {\n const [index, isMember, owner] = await Promise.all([\n database.getNetwork(networkId),\n database.isNetworkMember(networkId, userId),\n database.isIndexOwner(networkId, userId),\n ]);\n\n if (!index) {\n throw new ChatContextAccessError(\n \"Index not found\",\n 404,\n \"INDEX_NOT_FOUND\"\n );\n }\n\n if (!isMember) {\n throw new ChatContextAccessError(\n \"You are not a member of this index\",\n 403,\n \"INDEX_MEMBERSHIP_REQUIRED\"\n );\n }\n\n let membership = userNetworks.find((m) => m.networkId === index.id);\n if (membership === undefined) {\n membership = (await database.getNetworkMembership(index.id, userId)) ?? undefined;\n }\n scopedIndex = {\n id: index.id,\n title: index.title,\n prompt: membership?.indexPrompt ?? null,\n type: index.type ?? 'community',\n metadata: index.metadata ?? {},\n permissions: index.permissions ?? {},\n };\n isOwner = owner;\n indexName = index.title;\n scopedMembershipRole = owner ? \"owner\" : \"member\";\n }\n\n const userName = user.name ?? \"Unknown\";\n const userEmail = user.email ?? \"\";\n const hasName = !!user.name?.trim();\n\n // When scoped to an index, clamp the caller's reach to [scopedIndex, personalIndex]\n // so the chat's data model matches its \"focus\" semantic: a chat scoped to a\n // community sees that community plus the user's personal index, not their\n // other unrelated memberships. Mirrors the MCP path's clamp for network-scoped\n // agents (see applyNetworkScopeToContext / computeAgentIndexScope).\n const indexScope = networkId\n ? userNetworks\n .filter((m) => m.networkId === networkId || m.isPersonal === true)\n .map((m) => m.networkId)\n : userNetworks.map((m) => m.networkId);\n\n return {\n userId,\n userName,\n userEmail,\n networkId,\n indexName,\n isOwner,\n user,\n userProfile,\n userNetworks,\n indexScope,\n scopedIndex,\n scopedMembershipRole,\n isOnboarding: !(user.onboarding?.completedAt),\n hasName,\n ...(sessionId !== undefined ? { sessionId } : {}),\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// DEFINE TOOL TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Type for the `defineTool` closure created in `createChatTools`.\n * Auto-injects resolved context and provides uniform logging / error handling.\n */\nexport type DefineTool = <T extends z.ZodType>(opts: {\n name: string;\n description: string;\n querySchema: T;\n handler: (input: { context: ResolvedToolContext; query: z.infer<T> }) => Promise<string>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n}) => any;\n\n/**\n * A raw tool definition before LangChain wrapping.\n * Used by the tool registry for direct HTTP invocation.\n */\nexport interface RawToolDefinition {\n name: string;\n description: string;\n schema: z.ZodType;\n handler: (input: { context: ResolvedToolContext; query: unknown }) => Promise<string>;\n}\n\n/**\n * Registry mapping tool names to their raw definitions.\n */\nexport type ToolRegistry = Map<string, RawToolDefinition>;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL DEPENDENCIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Shared dependencies available to all tool domain factories.\n * Passed by `createChatTools` after compiling all subgraphs.\n */\nexport interface ToolDeps {\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. */\n userDb: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. */\n systemDb: SystemDatabase;\n scraper: Scraper;\n embedder: import('../interfaces/embedder.interface.js').Embedder;\n cache: Cache;\n integration: IntegrationAdapter;\n contactService: ContactServiceAdapter;\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Chat session reader for exposing the caller's past conversations as MCP tools. */\n chatSession?: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline\n * via the `questionGenerator`. Injected by the composition root when\n * QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /**\n * Lookup pending questions for a user, optionally filtered by source,\n * detection mode, or capped by count (hosts apply `limit` SQL-side).\n * Used by tools to attach contextually relevant questions to their results.\n * Injected by the composition root — absent when question delivery is disabled.\n */\n findPendingQuestions?: (\n userId: string,\n filters?: {\n sourceType?: string;\n sourceId?: string;\n /** Restrict to questions whose detection mode is in this set. */\n modes?: QuestionMode[];\n /** Maximum rows to return; hosts should apply this in the query. */\n limit?: number;\n },\n ) => Promise<PendingQuestionSummary[]>;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n graphs: {\n profile: CompiledGraph;\n intent: CompiledGraph;\n index: CompiledGraph;\n networkMembership: CompiledGraph;\n intentIndex: CompiledGraph;\n opportunity: CompiledOpportunityGraph;\n premise: CompiledGraph;\n };\n /**\n * Optional network ranking override for `read_networks`. Injected by tests or custom compositions.\n * When absent, defaults to `NetworkRecommender.invoke()` with a lazy module-level singleton.\n */\n networkRanker?: (input: {\n userProfile: { bio: string; location: string; interests: string[]; skills: string[] };\n networks: Array<{ networkId: string; renderedContext: string }>;\n }) => Promise<{ rankedNetworkIds: string[] } | null>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL RESULT HELPERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\nexport function success<T>(data: T): string {\n return JSON.stringify({ success: true, data });\n}\n\nexport function error(\n message: string,\n debugSteps?: Array<{ step: string; detail?: string; data?: Record<string, unknown> }>\n): string {\n return JSON.stringify({\n success: false,\n error: message,\n ...(debugSteps?.length ? { debugSteps } : {}),\n });\n}\n\n/** Return needsClarification for missing required fields. */\nexport function needsClarification(params: {\n missingFields: string[];\n message: string;\n}): string {\n return JSON.stringify({\n success: false,\n needsClarification: true,\n ...params,\n });\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// CONSTANTS & UTILITIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Matches http/https URLs in text; captures full URL. */\nconst URL_IN_TEXT_REGEX = /https?:\\/\\/[^\\s\"'<>)\\]]+/gi;\n\n/**\n * Matches bare domain URLs without protocol (e.g. github.com/foo, www.example.com).\n * Requires at least a SLD.TLD pattern followed by optional path.\n * Negative lookbehind ensures we don't double-match URLs already caught by URL_IN_TEXT_REGEX.\n */\nconst BARE_URL_REGEX = /(?<!\\w:\\/\\/)(?<![/\\w])(?:www\\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.(?:com|org|net|io|dev|co|ai|app|xyz|me|info|gg|so|sh|cc|ly|fm|tv|to|tech|design|network|world|edu|gov|mil|int|us|uk|eu|de|fr|ca|au|jp|cn|in|br|nl|se|no|fi|dk|ch|at|be|it|es|pt|pl|cz|ru|kr|tw|hk|sg|nz|za|mx|ar|cl|id|ph|th|vn|my|ie)(?:\\/[^\\s\"'<>)\\]]*)?/gi;\n\n/** UUID v4 format: 8-4-4-4-12 hex chars (e.g. c2505011-2e45-426e-81dd-b9abb9b72023) */\nexport const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Resolves an array of network IDs to their display titles.\n * Skips any IDs that don't resolve (deleted or invalid networks).\n */\nexport async function resolveIndexNames(\n database: { getNetwork(id: string): Promise<{ id: string; title: string } | null> },\n networkIds: string[]\n): Promise<string[]> {\n if (networkIds.length === 0) return [];\n const results = await Promise.all(\n networkIds.map(id => database.getNetwork(id))\n );\n return results.filter(Boolean).map(idx => idx!.title);\n}\n\n/**\n * Normalize a URL string: if it lacks a protocol, prepend \"https://\".\n * Returns the normalized URL or null if the result is not a valid URL.\n */\nexport function normalizeUrl(raw: string): string | null {\n let url = raw.replace(/[.,;:!?)]+$/, \"\").trim();\n if (!/^https?:\\/\\//i.test(url)) {\n url = `https://${url}`;\n }\n try {\n new URL(url);\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract unique, valid URLs from a string (e.g. user message or details).\n * Handles both full URLs (https://...) and bare domains (github.com/...).\n */\nexport function extractUrls(text: string): string[] {\n if (!text || typeof text !== \"string\") return [];\n\n const seen = new Set<string>();\n const out: string[] = [];\n\n // Pass 1: full protocol URLs\n const fullMatches = text.match(URL_IN_TEXT_REGEX) ?? [];\n for (const raw of fullMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n // Pass 2: bare domain URLs (e.g. github.com/foo)\n const bareMatches = text.match(BARE_URL_REGEX) ?? [];\n for (const raw of bareMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n return out;\n}\n\nconst SENSITIVE_FIELD_KEYS = new Set([\n \"secret\",\n \"webhooksecret\",\n \"password\",\n \"apikey\",\n \"token\",\n \"accesstoken\",\n \"refreshtoken\",\n \"privatekey\",\n \"authtoken\",\n \"bearertoken\",\n \"clientsecret\",\n]);\n\n/**\n * Recursively redacts sensitive field values from an arbitrary payload before\n * it is passed to a structured logger. Matches field names case-insensitively\n * and ignoring underscores, so `api_key`, `apiKey`, and `API_KEY` all match.\n * Non-sensitive fields are passed through unchanged. Never mutates the input —\n * returns a new value.\n *\n * Intended for structured-log redaction only. Do NOT use as a security\n * boundary for data in motion.\n *\n * @param value - Arbitrary JSON-like payload (query object, config blob, etc.)\n * @returns A new value with sensitive fields replaced by `\"[redacted]\"`.\n */\nexport function redactSensitiveFields(value: unknown): unknown {\n if (value === null || typeof value !== \"object\") return value;\n if (Array.isArray(value)) {\n return value.map((item) => redactSensitiveFields(item));\n }\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value as Record<string, unknown>)) {\n const normalized = key.toLowerCase().replace(/_/g, \"\");\n if (SENSITIVE_FIELD_KEYS.has(normalized)) {\n out[key] = \"[redacted]\";\n } else {\n out[key] = redactSensitiveFields(inner);\n }\n }\n return out;\n}\n"]}
1
+ {"version":3,"file":"tool.helpers.js","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { ModelConfig } from \"./model.config.js\";\nimport type { ProfileDocument } from \"../schemas/profile.schema.js\";\nimport type {\n ChatGraphCompositeDatabase,\n NetworkMembership,\n UserRecord,\n UserDatabase,\n SystemDatabase,\n NegotiationGraphDatabase,\n} from \"../interfaces/database.interface.js\";\nimport type { Scraper } from \"../interfaces/scraper.interface.js\";\nimport type { Cache, HydeCache } from \"../interfaces/cache.interface.js\";\nimport type { CompiledOpportunityGraph } from \"../../opportunity/opportunity.discover.js\";\nimport type { IntegrationAdapter } from \"../interfaces/integration.interface.js\";\nimport type { ContactServiceAdapter } from \"../interfaces/contact.interface.js\";\nimport type { ProfileEnricher } from \"../interfaces/enrichment.interface.js\";\nimport type { IntentGraphQueue } from \"../interfaces/queue.interface.js\";\nimport type { ChatSessionReader } from \"../interfaces/chat-session.interface.js\";\nimport type { ChatSummaryReader } from \"../interfaces/chat-summary.interface.js\";\nimport type { ChatMessageWriter } from \"../interfaces/chat-message-writer.interface.js\";\nimport type { QuestionGeneratorReader } from \"../interfaces/question-generator.interface.js\";\nimport type { NegotiationSummaryReader } from \"../interfaces/negotiation-summary.interface.js\";\nimport type { Embedder } from \"../interfaces/embedder.interface.js\";\nimport type { AgentDatabase } from \"../interfaces/agent.interface.js\";\nimport type { NegotiationTimeoutQueue } from \"../interfaces/negotiation-events.interface.js\";\nimport type { AgentDispatcher } from \"../interfaces/agent-dispatcher.interface.js\";\nimport type { DeliveryLedger } from \"../interfaces/delivery-ledger.interface.js\";\nimport type { MintConnectLink } from \"../interfaces/connect-link.interface.js\";\nimport type { QuestionerDatabase } from \"../interfaces/questioner.interface.js\";\nimport type { QuestionerEnqueueFn } from \"../../questioner/questioner.types.js\";\nimport type { PendingQuestionSummary } from \"../schemas/pending-question.schema.js\";\nimport type { QuestionMode } from \"../schemas/question.schema.js\";\nimport type { DiscoveryRunQueue, DiscoveryRunStore } from \"../interfaces/discovery-run.interface.js\";\nimport type { ProfileRunQueue, ProfileRunStore } from \"../interfaces/profile-run.interface.js\";\n\nexport type ProfileContext = ProfileDocument | null;\n\nexport interface ToolErrorReport {\n operation: string;\n subsystem?: string;\n toolName?: string;\n userId?: string;\n tags?: Record<string, string | number | boolean | null | undefined>;\n context?: Record<string, unknown>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// COMPILED GRAPH TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Minimal interface for an invokable compiled LangGraph. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type CompiledGraph = { invoke: (input: any) => Promise<any> };\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL CONTEXT TYPES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolved context available to every tool handler.\n * Contains the current user and optional index identity, resolved from DB at init.\n * The LLM can see this context (via system prompt) but cannot change it.\n */\nexport interface ResolvedToolContext {\n // Legacy flat fields (kept for backwards compatibility in tools/prompts).\n userId: string;\n userName: string;\n userEmail: string;\n networkId?: string;\n indexName?: string;\n /** True when chat is index-scoped and the user owns the index. */\n isOwner?: boolean;\n // Rich identity context for prompt/tool orchestration.\n user: UserRecord;\n userProfile: ProfileContext;\n userNetworks: NetworkMembership[];\n /**\n * The set of index IDs this caller can reach in the current request.\n * For unscoped chats: every index the user is a member of.\n * For network-scoped agents: `[boundNetwork, personalIndex]`.\n * This is the same set used to clamp the DB-level systemDb.\n * Tools that filter intents/profiles default to this set; `networkId` is\n * the \"primary focus\" hint, not a read filter.\n */\n indexScope: string[];\n scopedIndex?: {\n id: string;\n title: string;\n prompt: string | null;\n type?: string;\n metadata?: Record<string, unknown>;\n permissions?: Record<string, unknown>;\n };\n scopedMembershipRole?: \"owner\" | \"member\";\n /** True when user has not completed onboarding (onboarding.completedAt is null). */\n isOnboarding: boolean;\n /** True when the user has a non-empty name. */\n hasName: boolean;\n /** Chat session ID when tools are used in a chat; used for draft opportunities (context.conversationId). */\n sessionId?: string;\n /** True when the request originates from an MCP transport (no interactive UI available). */\n isMcp?: boolean;\n /** Agent ID when the request originates from an API key linked to an agent. */\n agentId?: string;\n /**\n * Receiver's rendering surface declared by the MCP client via the\n * `x-index-surface` request header. `'telegram'` means the MCP response is\n * being rendered inside a Telegram chat; anything\n * else (including `undefined`) is treated as web. Forwarded into\n * `mintConnectLink` so the click-time redirect can branch.\n */\n clientSurface?: 'telegram' | 'web';\n}\n\n/**\n * Dependencies passed when creating tools for a user session.\n * Includes DB adapters, embedder, and scraper.\n *\n * Note: userDb and systemDb are optional inputs - if not provided, createChatTools\n * will create them internally from the chatDatabaseAdapter singleton.\n */\nexport interface ToolContext {\n userId: string;\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. Created internally if not provided. */\n userDb?: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. Created internally if not provided. */\n systemDb?: SystemDatabase;\n embedder: Embedder;\n scraper: Scraper;\n /** When set, chat is scoped to this index; tools use it as default for read_intents and create_intent. */\n networkId?: string;\n /**\n * Optional override of the resolved `indexScope`. `resolveChatContext` always\n * computes `indexScope` from the user's memberships (clamped to [bound,\n * personal] when `networkId` is set). When the caller has already computed\n * a clamped scope — notably the MCP server, which clamps via\n * `applyNetworkScopeToContext` for network-scoped agents — passing it on\n * `ToolContext.indexScope` causes `createChatTools` (in tool.factory.ts) to\n * override `resolvedContext.indexScope` with this value rather than the\n * freshly computed one. See ResolvedToolContext.indexScope for the\n * resolved-side semantics.\n */\n indexScope?: string[];\n /** Chat session ID when creating tools for a chat; enables draft opportunities with context.conversationId. */\n sessionId?: string;\n\n // ─── Protocol-level dependencies (injected by composition root) ──────────\n /** General-purpose cache (e.g. for tool results). */\n cache: Cache;\n /** Dedicated cache for HyDE graph (may be same instance as cache). */\n hydeCache: HydeCache;\n /** External integration platform adapter (OAuth, tool actions). */\n integration: IntegrationAdapter;\n /** Queue for enqueuing follow-up intent processing (HyDE generation/deletion). */\n intentQueue: IntentGraphQueue;\n /** Contact management operations. */\n contactService: ContactServiceAdapter;\n /** Chat session reader for loading conversation history. */\n chatSession: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline.\n * Injected by the composition root when QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Profile enrichment from external data sources. */\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Integration importer for bulk contact import from toolkits. */\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n /** Factory for user-scoped database access. */\n createUserDatabase: (db: ChatGraphCompositeDatabase, userId: string) => UserDatabase;\n /** Factory for system-scoped database access. */\n createSystemDatabase: (db: ChatGraphCompositeDatabase, userId: string, indexScope: string[], embedder?: Embedder) => SystemDatabase;\n /** Optional runtime LLM config. Pass to override env vars for API key, model, etc. */\n modelConfig?: ModelConfig;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Enqueue a negotiate_existing job after introducer approval (optional). */\n queueNegotiateExisting?: (opportunityId: string, userId: string) => Promise<void>;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Persistence for structured questions generated by the QuestionerAgent (optional). */\n questionerDatabase?: QuestionerDatabase;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n}\n\n/**\n * All external dependencies needed to initialize the protocol tool engine.\n * The host application (composition root) must provide concrete implementations.\n * This is the subset of ToolContext that is NOT per-request (no userId, indexId, sessionId).\n */\nexport type ProtocolDeps = Omit<ToolContext, 'userId' | 'indexId' | 'sessionId' | 'userDb' | 'systemDb'>;\n\n/**\n * Thrown when a requested chat scope is invalid for the authenticated user.\n * Controllers can map this to an HTTP status code.\n */\nexport class ChatContextAccessError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: \"USER_NOT_FOUND\" | \"INDEX_NOT_FOUND\" | \"INDEX_MEMBERSHIP_REQUIRED\"\n ) {\n super(message);\n this.name = \"ChatContextAccessError\";\n }\n}\n\n/**\n * Resolve the canonical context used by chat tools and system prompt.\n * This preloads user identity, profile, index memberships, and scoped index role.\n */\nexport async function resolveChatContext(params: {\n database: Pick<\n ChatGraphCompositeDatabase,\n \"getUser\" | \"getProfile\" | \"getNetworkMemberships\" | \"getNetworkMembership\" | \"getNetwork\" | \"isIndexOwner\" | \"isNetworkMember\"\n >;\n userId: string;\n networkId?: string;\n /** Chat session ID for draft opportunities (stored as context.conversationId). */\n sessionId?: string;\n}): Promise<ResolvedToolContext> {\n const { database, userId, networkId, sessionId } = params;\n\n const [user, rawProfile, userNetworks] = await Promise.all([\n database.getUser(userId),\n database.getProfile(userId),\n database.getNetworkMemberships(userId),\n ]);\n\n const userProfile: ProfileContext = rawProfile ?? null;\n\n if (!user) {\n throw new ChatContextAccessError(\n \"User not found\",\n 404,\n \"USER_NOT_FOUND\"\n );\n }\n\n let scopedIndex: ResolvedToolContext[\"scopedIndex\"] = undefined;\n let scopedMembershipRole: ResolvedToolContext[\"scopedMembershipRole\"] = undefined;\n let isOwner = false;\n let indexName: string | undefined;\n\n if (networkId) {\n const [index, isMember, owner] = await Promise.all([\n database.getNetwork(networkId),\n database.isNetworkMember(networkId, userId),\n database.isIndexOwner(networkId, userId),\n ]);\n\n if (!index) {\n throw new ChatContextAccessError(\n \"Index not found\",\n 404,\n \"INDEX_NOT_FOUND\"\n );\n }\n\n if (!isMember) {\n throw new ChatContextAccessError(\n \"You are not a member of this index\",\n 403,\n \"INDEX_MEMBERSHIP_REQUIRED\"\n );\n }\n\n let membership = userNetworks.find((m) => m.networkId === index.id);\n if (membership === undefined) {\n membership = (await database.getNetworkMembership(index.id, userId)) ?? undefined;\n }\n scopedIndex = {\n id: index.id,\n title: index.title,\n prompt: membership?.indexPrompt ?? null,\n type: index.type ?? 'community',\n metadata: index.metadata ?? {},\n permissions: index.permissions ?? {},\n };\n isOwner = owner;\n indexName = index.title;\n scopedMembershipRole = owner ? \"owner\" : \"member\";\n }\n\n const userName = user.name ?? \"Unknown\";\n const userEmail = user.email ?? \"\";\n const hasName = !!user.name?.trim();\n\n // When scoped to an index, clamp the caller's reach to [scopedIndex, personalIndex]\n // so the chat's data model matches its \"focus\" semantic: a chat scoped to a\n // community sees that community plus the user's personal index, not their\n // other unrelated memberships. Mirrors the MCP path's clamp for network-scoped\n // agents (see applyNetworkScopeToContext / computeAgentIndexScope).\n const indexScope = networkId\n ? userNetworks\n .filter((m) => m.networkId === networkId || m.isPersonal === true)\n .map((m) => m.networkId)\n : userNetworks.map((m) => m.networkId);\n\n return {\n userId,\n userName,\n userEmail,\n networkId,\n indexName,\n isOwner,\n user,\n userProfile,\n userNetworks,\n indexScope,\n scopedIndex,\n scopedMembershipRole,\n isOnboarding: !(user.onboarding?.completedAt),\n hasName,\n ...(sessionId !== undefined ? { sessionId } : {}),\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// DEFINE TOOL TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Type for the `defineTool` closure created in `createChatTools`.\n * Auto-injects resolved context and provides uniform logging / error handling.\n */\nexport type DefineTool = <T extends z.ZodType>(opts: {\n name: string;\n description: string;\n querySchema: T;\n handler: (input: { context: ResolvedToolContext; query: z.infer<T> }) => Promise<string>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n}) => any;\n\n/**\n * A raw tool definition before LangChain wrapping.\n * Used by the tool registry for direct HTTP invocation.\n */\nexport interface RawToolDefinition {\n name: string;\n description: string;\n schema: z.ZodType;\n handler: (input: { context: ResolvedToolContext; query: unknown }) => Promise<string>;\n}\n\n/**\n * Registry mapping tool names to their raw definitions.\n */\nexport type ToolRegistry = Map<string, RawToolDefinition>;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL DEPENDENCIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Shared dependencies available to all tool domain factories.\n * Passed by `createChatTools` after compiling all subgraphs.\n */\nexport interface ToolDeps {\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. */\n userDb: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. */\n systemDb: SystemDatabase;\n scraper: Scraper;\n embedder: import('../interfaces/embedder.interface.js').Embedder;\n cache: Cache;\n integration: IntegrationAdapter;\n contactService: ContactServiceAdapter;\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Chat session reader for exposing the caller's past conversations as MCP tools. */\n chatSession?: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /**\n * Test seam for opportunity discovery helpers. Production compositions leave\n * this unset so tools call the real discovery module directly.\n */\n opportunityDiscovery?: {\n runDiscoverFromQuery?: (input: unknown) => Promise<unknown>;\n continueDiscovery?: (input: unknown) => Promise<unknown>;\n };\n /**\n * Test seam for opportunity card presentation helpers. Production\n * compositions leave this unset so tools construct the real presenter.\n */\n opportunityPresentation?: {\n createPresenter?: () => { presentHomeCard(input: unknown): Promise<unknown> };\n gatherPresenterContext?: (...args: unknown[]) => Promise<unknown>;\n };\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline\n * via the `questionGenerator`. Injected by the composition root when\n * QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /**\n * Lookup pending questions for a user, optionally filtered by source,\n * detection mode, or capped by count (hosts apply `limit` SQL-side).\n * Used by tools to attach contextually relevant questions to their results.\n * Injected by the composition root — absent when question delivery is disabled.\n */\n findPendingQuestions?: (\n userId: string,\n filters?: {\n sourceType?: string;\n sourceId?: string;\n /** Restrict to questions whose detection mode is in this set. */\n modes?: QuestionMode[];\n /** Maximum rows to return; hosts should apply this in the query. */\n limit?: number;\n },\n ) => Promise<PendingQuestionSummary[]>;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n graphs: {\n profile: CompiledGraph;\n intent: CompiledGraph;\n index: CompiledGraph;\n networkMembership: CompiledGraph;\n intentIndex: CompiledGraph;\n opportunity: CompiledOpportunityGraph;\n premise: CompiledGraph;\n };\n /**\n * Optional network ranking override for `read_networks`. Injected by tests or custom compositions.\n * When absent, defaults to `NetworkRecommender.invoke()` with a lazy module-level singleton.\n */\n networkRanker?: (input: {\n userProfile: { bio: string; location: string; interests: string[]; skills: string[] };\n networks: Array<{ networkId: string; renderedContext: string }>;\n }) => Promise<{ rankedNetworkIds: string[] } | null>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL RESULT HELPERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\nexport function success<T>(data: T): string {\n return JSON.stringify({ success: true, data });\n}\n\nexport function error(\n message: string,\n debugSteps?: Array<{ step: string; detail?: string; data?: Record<string, unknown> }>\n): string {\n return JSON.stringify({\n success: false,\n error: message,\n ...(debugSteps?.length ? { debugSteps } : {}),\n });\n}\n\n/** Return needsClarification for missing required fields. */\nexport function needsClarification(params: {\n missingFields: string[];\n message: string;\n}): string {\n return JSON.stringify({\n success: false,\n needsClarification: true,\n ...params,\n });\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// CONSTANTS & UTILITIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Matches http/https URLs in text; captures full URL. */\nconst URL_IN_TEXT_REGEX = /https?:\\/\\/[^\\s\"'<>)\\]]+/gi;\n\n/**\n * Matches bare domain URLs without protocol (e.g. github.com/foo, www.example.com).\n * Requires at least a SLD.TLD pattern followed by optional path.\n * Negative lookbehind ensures we don't double-match URLs already caught by URL_IN_TEXT_REGEX.\n */\nconst BARE_URL_REGEX = /(?<!\\w:\\/\\/)(?<![/\\w])(?:www\\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.(?:com|org|net|io|dev|co|ai|app|xyz|me|info|gg|so|sh|cc|ly|fm|tv|to|tech|design|network|world|edu|gov|mil|int|us|uk|eu|de|fr|ca|au|jp|cn|in|br|nl|se|no|fi|dk|ch|at|be|it|es|pt|pl|cz|ru|kr|tw|hk|sg|nz|za|mx|ar|cl|id|ph|th|vn|my|ie)(?:\\/[^\\s\"'<>)\\]]*)?/gi;\n\n/** UUID v4 format: 8-4-4-4-12 hex chars (e.g. c2505011-2e45-426e-81dd-b9abb9b72023) */\nexport const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Resolves an array of network IDs to their display titles.\n * Skips any IDs that don't resolve (deleted or invalid networks).\n */\nexport async function resolveIndexNames(\n database: { getNetwork(id: string): Promise<{ id: string; title: string } | null> },\n networkIds: string[]\n): Promise<string[]> {\n if (networkIds.length === 0) return [];\n const results = await Promise.all(\n networkIds.map(id => database.getNetwork(id))\n );\n return results.filter(Boolean).map(idx => idx!.title);\n}\n\n/**\n * Normalize a URL string: if it lacks a protocol, prepend \"https://\".\n * Returns the normalized URL or null if the result is not a valid URL.\n */\nexport function normalizeUrl(raw: string): string | null {\n let url = raw.replace(/[.,;:!?)]+$/, \"\").trim();\n if (!/^https?:\\/\\//i.test(url)) {\n url = `https://${url}`;\n }\n try {\n new URL(url);\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract unique, valid URLs from a string (e.g. user message or details).\n * Handles both full URLs (https://...) and bare domains (github.com/...).\n */\nexport function extractUrls(text: string): string[] {\n if (!text || typeof text !== \"string\") return [];\n\n const seen = new Set<string>();\n const out: string[] = [];\n\n // Pass 1: full protocol URLs\n const fullMatches = text.match(URL_IN_TEXT_REGEX) ?? [];\n for (const raw of fullMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n // Pass 2: bare domain URLs (e.g. github.com/foo)\n const bareMatches = text.match(BARE_URL_REGEX) ?? [];\n for (const raw of bareMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n return out;\n}\n\nconst SENSITIVE_FIELD_KEYS = new Set([\n \"secret\",\n \"webhooksecret\",\n \"password\",\n \"apikey\",\n \"token\",\n \"accesstoken\",\n \"refreshtoken\",\n \"privatekey\",\n \"authtoken\",\n \"bearertoken\",\n \"clientsecret\",\n]);\n\n/**\n * Recursively redacts sensitive field values from an arbitrary payload before\n * it is passed to a structured logger. Matches field names case-insensitively\n * and ignoring underscores, so `api_key`, `apiKey`, and `API_KEY` all match.\n * Non-sensitive fields are passed through unchanged. Never mutates the input —\n * returns a new value.\n *\n * Intended for structured-log redaction only. Do NOT use as a security\n * boundary for data in motion.\n *\n * @param value - Arbitrary JSON-like payload (query object, config blob, etc.)\n * @returns A new value with sensitive fields replaced by `\"[redacted]\"`.\n */\nexport function redactSensitiveFields(value: unknown): unknown {\n if (value === null || typeof value !== \"object\") return value;\n if (Array.isArray(value)) {\n return value.map((item) => redactSensitiveFields(item));\n }\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value as Record<string, unknown>)) {\n const normalized = key.toLowerCase().replace(/_/g, \"\");\n if (SENSITIVE_FIELD_KEYS.has(normalized)) {\n out[key] = \"[redacted]\";\n } else {\n out[key] = redactSensitiveFields(inner);\n }\n }\n return out;\n}\n"]}
@@ -2,6 +2,13 @@
2
2
  * Delivery ledger interface for committing opportunity delivery rows.
3
3
  * Implementations live in src/adapters (e.g. database adapter).
4
4
  */
5
+ /** A committed delivery-ledger row, as read back for digest dedup. */
6
+ export interface DeliveredOpportunityRow {
7
+ opportunityId: string;
8
+ /** Opportunity status at the time the delivery was committed. */
9
+ deliveredAtStatus: string;
10
+ deliveredAt: Date;
11
+ }
5
12
  export interface DeliveryLedger {
6
13
  /**
7
14
  * Write a committed delivery row for an opportunity.
@@ -18,5 +25,17 @@ export interface DeliveryLedger {
18
25
  agentId: string | null;
19
26
  trigger: 'ambient' | 'digest' | 'accepted';
20
27
  }): Promise<'confirmed' | 'already_delivered'>;
28
+ /**
29
+ * Read committed delivery rows for the given opportunities delivered to the user.
30
+ * Used by digest mode of `list_opportunities` to suppress opportunities the user
31
+ * has already been shown across days, and to select cooldown re-show candidates.
32
+ *
33
+ * Optional: hosts that predate digest dedup may not implement it; callers must
34
+ * degrade gracefully (no suppression) when absent.
35
+ */
36
+ getDeliveredOpportunities?(params: {
37
+ userId: string;
38
+ opportunityIds: string[];
39
+ }): Promise<DeliveredOpportunityRow[]>;
21
40
  }
22
41
  //# sourceMappingURL=delivery-ledger.interface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delivery-ledger.interface.d.ts","sourceRoot":"/","sources":["shared/interfaces/delivery-ledger.interface.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,0BAA0B,CAAC,MAAM,EAAE;QACjC,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;KAC5C,GAAG,OAAO,CAAC,WAAW,GAAG,mBAAmB,CAAC,CAAC;CAChD"}
1
+ {"version":3,"file":"delivery-ledger.interface.d.ts","sourceRoot":"/","sources":["shared/interfaces/delivery-ledger.interface.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,0BAA0B,CAAC,MAAM,EAAE;QACjC,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;KAC5C,GAAG,OAAO,CAAC,WAAW,GAAG,mBAAmB,CAAC,CAAC;IAE/C;;;;;;;OAOG;IACH,yBAAyB,CAAC,CAAC,MAAM,EAAE;QACjC,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,MAAM,EAAE,CAAC;KAC1B,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC;CACxC"}