@mukulaggarwal/pacman 0.1.4 → 0.1.6

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.
@@ -11,8 +11,8 @@
11
11
  href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Orbitron:wght@400;500;600;700&family=Lexend:wght@300;400;500;600&family=Fira+Code:wght@400;500&display=swap"
12
12
  rel="stylesheet"
13
13
  />
14
- <script type="module" crossorigin src="/assets/index-roRRNUhE.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-BOAOMlJT.css">
14
+ <script type="module" crossorigin src="/assets/index-C0NQ8pNs.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-BwF9HPxA.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="root"></div>
@@ -19,6 +19,9 @@ type ProjectResolutionCandidate = {
19
19
  matchedNameTokens: string[];
20
20
  matchedContextTokens: string[];
21
21
  matchedAliasTokens: string[];
22
+ matchedFuzzyNameTokens: string[];
23
+ matchedFuzzyContextTokens: string[];
24
+ matchedFuzzyAliasTokens: string[];
22
25
  cosineSimilarity: number;
23
26
  coverageScore: number;
24
27
  aliasScore: number;
@@ -13,14 +13,17 @@ import {
13
13
  import {
14
14
  createSlackConnector,
15
15
  openSocketModeConnection
16
- } from "./chunk-YJ32S56Q.js";
16
+ } from "./chunk-X6CHTBN2.js";
17
17
 
18
18
  // src/slack-listener.ts
19
19
  import { google } from "googleapis";
20
20
  var RECONNECT_DELAY_MS = 5e3;
21
21
  var RECENT_EVENT_TTL_MS = 5 * 60 * 1e3;
22
22
  var PROJECT_RESOLUTION_MIN_SCORE = 0.45;
23
+ var PROJECT_RESOLUTION_CONTEXT_ANCHORED_MIN_SCORE = 0.25;
23
24
  var PROJECT_RESOLUTION_MIN_MARGIN = 0.08;
25
+ var PROJECT_RESOLUTION_FUZZY_MATCH_WEIGHT = 0.85;
26
+ var PROJECT_RESOLUTION_FUZZY_SCORE_BOOST = 0.35;
24
27
  var recentEventKeys = /* @__PURE__ */ new Map();
25
28
  var RESOLUTION_STOPWORDS = /* @__PURE__ */ new Set([
26
29
  "a",
@@ -559,6 +562,9 @@ function inspectProjectResolution(sources, text) {
559
562
  matchedNameTokens: [],
560
563
  matchedContextTokens: [],
561
564
  matchedAliasTokens: [],
565
+ matchedFuzzyNameTokens: [],
566
+ matchedFuzzyContextTokens: [],
567
+ matchedFuzzyAliasTokens: [],
562
568
  cosineSimilarity: 0,
563
569
  coverageScore: 0,
564
570
  aliasScore: 0,
@@ -582,7 +588,8 @@ function inspectProjectResolution(sources, text) {
582
588
  };
583
589
  }
584
590
  const [best, second] = candidates;
585
- const selectedProject = best && best.score >= PROJECT_RESOLUTION_MIN_SCORE && best.score - (second?.score ?? 0) >= PROJECT_RESOLUTION_MIN_MARGIN ? best.projectName : void 0;
591
+ const margin = best ? best.score - (second?.score ?? 0) : 0;
592
+ const selectedProject = best && margin >= PROJECT_RESOLUTION_MIN_MARGIN && (best.score >= PROJECT_RESOLUTION_MIN_SCORE || best.score >= PROJECT_RESOLUTION_CONTEXT_ANCHORED_MIN_SCORE && hasAnchoredProjectEvidence(best)) ? best.projectName : void 0;
586
593
  return {
587
594
  threadText: text,
588
595
  normalizedText,
@@ -601,6 +608,9 @@ function scoreProjectMatch(profile, haystack, queryTokens, idf) {
601
608
  matchedNameTokens: [],
602
609
  matchedContextTokens: [],
603
610
  matchedAliasTokens: [],
611
+ matchedFuzzyNameTokens: [],
612
+ matchedFuzzyContextTokens: [],
613
+ matchedFuzzyAliasTokens: [],
604
614
  cosineSimilarity: 0,
605
615
  coverageScore: 0,
606
616
  aliasScore: 0,
@@ -617,6 +627,14 @@ function scoreProjectMatch(profile, haystack, queryTokens, idf) {
617
627
  const matchedNameTokens = queryTokens.filter((token) => nameTokenSet.has(token));
618
628
  const matchedAliasTokens = queryTokens.filter((token) => aliasTokenSet.has(token));
619
629
  const matchedContextTokens = queryTokens.filter((token) => contextTokenSet.has(token) && !nameTokenSet.has(token) && !aliasTokenSet.has(token));
630
+ const fuzzyMatches = collectFuzzyTokenMatches(profile, queryTokens, /* @__PURE__ */ new Set([
631
+ ...matchedNameTokens,
632
+ ...matchedAliasTokens,
633
+ ...matchedContextTokens
634
+ ]));
635
+ const matchedFuzzyNameTokens = fuzzyMatches.filter((match) => match.category === "name").map((match) => `${match.queryToken}~${match.matchedToken}`);
636
+ const matchedFuzzyAliasTokens = fuzzyMatches.filter((match) => match.category === "alias").map((match) => `${match.queryToken}~${match.matchedToken}`);
637
+ const matchedFuzzyContextTokens = fuzzyMatches.filter((match) => match.category === "context").map((match) => `${match.queryToken}~${match.matchedToken}`);
620
638
  const queryVector = buildWeightedTokenMap(queryTokens, idf, fallbackIdf, 1);
621
639
  const documentVector = /* @__PURE__ */ new Map();
622
640
  accumulateTokenWeights(documentVector, profile.nameTokens, idf, fallbackIdf, 3.5);
@@ -626,11 +644,17 @@ function scoreProjectMatch(profile, haystack, queryTokens, idf) {
626
644
  const totalQueryWeight = sumWeights(queryVector) || 1;
627
645
  const matchedQueryWeight = [...queryTokenSet].filter((token) => documentTokenSet.has(token)).reduce((sum, token) => sum + (queryVector.get(token) ?? 0), 0);
628
646
  const matchedAliasWeight = [...queryTokenSet].filter((token) => aliasTokenSet.has(token)).reduce((sum, token) => sum + (queryVector.get(token) ?? 0), 0);
629
- const coverageScore = matchedQueryWeight / totalQueryWeight;
630
- const aliasScore = matchedAliasWeight / totalQueryWeight;
647
+ const fuzzyMatchedQueryWeight = fuzzyMatches.reduce(
648
+ (sum, match) => sum + (queryVector.get(match.queryToken) ?? 0) * PROJECT_RESOLUTION_FUZZY_MATCH_WEIGHT,
649
+ 0
650
+ );
651
+ const fuzzyMatchedAliasWeight = fuzzyMatches.filter((match) => match.category === "alias").reduce((sum, match) => sum + (queryVector.get(match.queryToken) ?? 0) * PROJECT_RESOLUTION_FUZZY_MATCH_WEIGHT, 0);
652
+ const coverageScore = (matchedQueryWeight + fuzzyMatchedQueryWeight) / totalQueryWeight;
653
+ const aliasScore = (matchedAliasWeight + fuzzyMatchedAliasWeight) / totalQueryWeight;
631
654
  const cosineSimilarity = computeCosineSimilarity(queryVector, documentVector);
632
655
  const phraseScore = haystack.includes(normalizedProject) ? 1 : 0;
633
- const score = phraseScore * 1.5 + cosineSimilarity * 1.2 + coverageScore * 1 + aliasScore * 0.8;
656
+ const fuzzySupportScore = fuzzyMatchedQueryWeight / totalQueryWeight;
657
+ const score = phraseScore * 1.5 + cosineSimilarity * 1.2 + coverageScore * 1 + aliasScore * 0.8 + fuzzySupportScore * PROJECT_RESOLUTION_FUZZY_SCORE_BOOST;
634
658
  return {
635
659
  projectName: source.projectName,
636
660
  normalizedProject,
@@ -638,6 +662,9 @@ function scoreProjectMatch(profile, haystack, queryTokens, idf) {
638
662
  matchedNameTokens,
639
663
  matchedContextTokens,
640
664
  matchedAliasTokens,
665
+ matchedFuzzyNameTokens,
666
+ matchedFuzzyContextTokens,
667
+ matchedFuzzyAliasTokens,
641
668
  cosineSimilarity,
642
669
  coverageScore,
643
670
  aliasScore,
@@ -646,7 +673,7 @@ function scoreProjectMatch(profile, haystack, queryTokens, idf) {
646
673
  };
647
674
  }
648
675
  function logProjectResolution(debug) {
649
- console.log("[slack] Project resolution strategy: weighted TF-IDF similarity over project name, latest project summary, and recent project context, with stemming, stopword removal, and acronym alias extraction; require unique best score >= 0.45 with margin >= 0.08 or abstain");
676
+ console.log("[slack] Project resolution strategy: weighted TF-IDF similarity over project name, latest project summary, and recent project context, with stemming, stopword removal, acronym alias extraction, and typo-tolerant token matching for longer words; select unique best score >= 0.45 or anchored name+context score >= 0.25, with margin >= 0.08, else abstain");
650
677
  console.log(`[slack] Registered projects: ${debug.candidates.length > 0 ? debug.candidates.map((candidate) => candidate.projectName).join(", ") : "none"}`);
651
678
  console.log(`[slack] Resolution text: ${summarizeText(debug.threadText, 180)}`);
652
679
  console.log(`[slack] Normalized text: ${debug.normalizedText || "<empty>"}`);
@@ -655,9 +682,14 @@ function logProjectResolution(debug) {
655
682
  return;
656
683
  }
657
684
  console.log(
658
- `[slack] Project candidate scores: ${debug.candidates.map((candidate) => `${candidate.projectName}=total:${formatScore(candidate.score)},cosine:${formatScore(candidate.cosineSimilarity)},coverage:${formatScore(candidate.coverageScore)},alias:${formatScore(candidate.aliasScore)},phrase:${formatScore(candidate.phraseScore)}${candidate.matchedNameTokens.length > 0 ? `,name:[${candidate.matchedNameTokens.join(", ")}]` : ""}${candidate.matchedContextTokens.length > 0 ? `,context:[${candidate.matchedContextTokens.join(", ")}]` : ""}${candidate.matchedAliasTokens.length > 0 ? `,aliases:[${candidate.matchedAliasTokens.join(", ")}]` : ""},summary:${JSON.stringify(summarizeText(candidate.latestSummary, 100))}`).join(" | ")}`
685
+ `[slack] Project candidate scores: ${debug.candidates.map((candidate) => `${candidate.projectName}=total:${formatScore(candidate.score)},cosine:${formatScore(candidate.cosineSimilarity)},coverage:${formatScore(candidate.coverageScore)},alias:${formatScore(candidate.aliasScore)},phrase:${formatScore(candidate.phraseScore)}${candidate.matchedNameTokens.length > 0 ? `,name:[${candidate.matchedNameTokens.join(", ")}]` : ""}${candidate.matchedContextTokens.length > 0 ? `,context:[${candidate.matchedContextTokens.join(", ")}]` : ""}${candidate.matchedAliasTokens.length > 0 ? `,aliases:[${candidate.matchedAliasTokens.join(", ")}]` : ""}${candidate.matchedFuzzyNameTokens.length > 0 ? `,fuzzyName:[${candidate.matchedFuzzyNameTokens.join(", ")}]` : ""}${candidate.matchedFuzzyContextTokens.length > 0 ? `,fuzzyContext:[${candidate.matchedFuzzyContextTokens.join(", ")}]` : ""}${candidate.matchedFuzzyAliasTokens.length > 0 ? `,fuzzyAliases:[${candidate.matchedFuzzyAliasTokens.join(", ")}]` : ""},summary:${JSON.stringify(summarizeText(candidate.latestSummary, 100))}`).join(" | ")}`
659
686
  );
660
687
  }
688
+ function hasAnchoredProjectEvidence(candidate) {
689
+ const nameSignalCount = candidate.matchedNameTokens.length + candidate.matchedAliasTokens.length + candidate.matchedFuzzyNameTokens.length + candidate.matchedFuzzyAliasTokens.length;
690
+ const contextSignalCount = candidate.matchedContextTokens.length + candidate.matchedFuzzyContextTokens.length;
691
+ return nameSignalCount > 0 && contextSignalCount > 0;
692
+ }
661
693
  function logSelectedProjectSummary(debug, projectName) {
662
694
  const selected = debug.candidates.find((candidate) => candidate.projectName === projectName);
663
695
  if (!selected?.latestSummary) {
@@ -793,6 +825,113 @@ function extractAcronymTokens(value) {
793
825
  }
794
826
  return [...aliases].map((token) => stemResolutionToken(token)).filter((token) => token.length > 1);
795
827
  }
828
+ function collectFuzzyTokenMatches(profile, queryTokens, alreadyMatched) {
829
+ const matches = [];
830
+ const usedTargets = /* @__PURE__ */ new Set();
831
+ for (const queryToken of queryTokens) {
832
+ if (alreadyMatched.has(queryToken)) {
833
+ continue;
834
+ }
835
+ const match = findBestFuzzyTokenMatch(profile, queryToken, usedTargets);
836
+ if (!match) {
837
+ continue;
838
+ }
839
+ matches.push(match);
840
+ usedTargets.add(`${match.category}:${match.matchedToken}`);
841
+ }
842
+ return matches;
843
+ }
844
+ function findBestFuzzyTokenMatch(profile, queryToken, usedTargets) {
845
+ const maxDistance = getMaxFuzzyDistance(queryToken);
846
+ if (maxDistance === 0) {
847
+ return void 0;
848
+ }
849
+ const candidates = [
850
+ ...profile.nameTokens.map((token) => ({ token, category: "name", priority: 0 })),
851
+ ...profile.aliasTokens.map((token) => ({ token, category: "alias", priority: 1 })),
852
+ ...profile.summaryTokens.map((token) => ({ token, category: "context", priority: 2 })),
853
+ ...profile.contextTokens.map((token) => ({ token, category: "context", priority: 2 }))
854
+ ];
855
+ let best;
856
+ for (const candidate of candidates) {
857
+ if (usedTargets.has(`${candidate.category}:${candidate.token}`)) {
858
+ continue;
859
+ }
860
+ if (!isFuzzyComparable(queryToken, candidate.token, maxDistance)) {
861
+ continue;
862
+ }
863
+ const distance = computeBoundedLevenshteinDistance(queryToken, candidate.token, maxDistance);
864
+ if (distance < 1 || distance > maxDistance) {
865
+ continue;
866
+ }
867
+ if (!best || distance < best.distance || distance === best.distance && candidate.priority < best.priority) {
868
+ best = {
869
+ queryToken,
870
+ matchedToken: candidate.token,
871
+ category: candidate.category,
872
+ distance,
873
+ priority: candidate.priority
874
+ };
875
+ }
876
+ }
877
+ if (!best) {
878
+ return void 0;
879
+ }
880
+ return {
881
+ queryToken: best.queryToken,
882
+ matchedToken: best.matchedToken,
883
+ category: best.category,
884
+ distance: best.distance
885
+ };
886
+ }
887
+ function getMaxFuzzyDistance(token) {
888
+ if (token.length >= 9) return 3;
889
+ if (token.length >= 7) return 2;
890
+ return 0;
891
+ }
892
+ function isFuzzyComparable(left, right, maxDistance) {
893
+ if (!left || !right || left === right) {
894
+ return false;
895
+ }
896
+ if (Math.abs(left.length - right.length) > maxDistance) {
897
+ return false;
898
+ }
899
+ if (left[0] !== right[0]) {
900
+ return false;
901
+ }
902
+ const requiredPrefixLength = Math.max(1, Math.min(3, maxDistance === 3 ? 2 : 1));
903
+ return left.slice(0, requiredPrefixLength) === right.slice(0, requiredPrefixLength);
904
+ }
905
+ function computeBoundedLevenshteinDistance(left, right, maxDistance) {
906
+ if (Math.abs(left.length - right.length) > maxDistance) {
907
+ return maxDistance + 1;
908
+ }
909
+ const previous = new Array(right.length + 1);
910
+ const current = new Array(right.length + 1);
911
+ for (let j = 0; j <= right.length; j += 1) {
912
+ previous[j] = j;
913
+ }
914
+ for (let i = 1; i <= left.length; i += 1) {
915
+ current[0] = i;
916
+ let rowMin = current[0];
917
+ for (let j = 1; j <= right.length; j += 1) {
918
+ const substitutionCost = left[i - 1] === right[j - 1] ? 0 : 1;
919
+ current[j] = Math.min(
920
+ previous[j] + 1,
921
+ current[j - 1] + 1,
922
+ previous[j - 1] + substitutionCost
923
+ );
924
+ rowMin = Math.min(rowMin, current[j]);
925
+ }
926
+ if (rowMin > maxDistance) {
927
+ return maxDistance + 1;
928
+ }
929
+ for (let j = 0; j <= right.length; j += 1) {
930
+ previous[j] = current[j];
931
+ }
932
+ }
933
+ return previous[right.length];
934
+ }
796
935
  function uniqueTokens(tokens) {
797
936
  return [...new Set(tokens.filter(Boolean))];
798
937
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/slack-listener.ts"],"sourcesContent":["import { google } from 'googleapis';\nimport { createContextManager } from '@personal-assistant/context-manager';\nimport {\n createSlackConnector,\n openSocketModeConnection,\n type SlackEventPayload,\n} from '@personal-assistant/integrations-slack';\nimport { WORKSPACE_PATHS as PATHS } from '@personal-assistant/core-types';\nimport type {\n AppConfig,\n DraftOutput,\n IntegrationConfig,\n LlmProvider,\n ReplyInput,\n ReplyInputEvidence,\n SlackChannelMapping,\n SlackRuntimeConfig,\n SlackThreadSnapshot,\n StorageBackend,\n} from '@personal-assistant/core-types';\nimport { generateDraft } from './provider-runtime.js';\nimport { resolveConfiguredStorage } from './storage.js';\n\nconst RECONNECT_DELAY_MS = 5000;\nconst RECENT_EVENT_TTL_MS = 5 * 60 * 1000;\nconst PROJECT_RESOLUTION_MIN_SCORE = 0.45;\nconst PROJECT_RESOLUTION_MIN_MARGIN = 0.08;\nconst recentEventKeys = new Map<string, number>();\nconst RESOLUTION_STOPWORDS = new Set([\n 'a', 'about', 'after', 'again', 'all', 'also', 'an', 'and', 'any', 'are', 'as', 'at',\n 'back', 'be', 'been', 'before', 'being', 'between', 'both', 'but', 'by', 'can', 'could',\n 'date', 'did', 'do', 'does', 'effective', 'for', 'from', 'had', 'has', 'have', 'help',\n 'here', 'how', 'i', 'if', 'in', 'into', 'is', 'it', 'its', 'just', 'latest', 'me', 'more',\n 'most', 'my', 'need', 'of', 'on', 'only', 'or', 'other', 'our', 'out', 'over', 'same',\n 'share', 'should', 'show', 'tell', 'than', 'that', 'the', 'their', 'them', 'there',\n 'these', 'they', 'this', 'those', 'through', 'to', 'under', 'update', 'us', 'using', 'want',\n 'was', 'we', 'what', 'when', 'where', 'which', 'while', 'who', 'why', 'with', 'would',\n 'you', 'your',\n]);\n\ntype RuntimeDeps = {\n config: AppConfig;\n storage: StorageBackend;\n slackIntegration: IntegrationConfig;\n slackRuntime: SlackRuntimeConfig;\n};\n\ntype ProjectResolutionSource = {\n projectName: string;\n latestSummary: string;\n contextText: string;\n};\n\ntype ProjectResolutionCandidate = {\n projectName: string;\n normalizedProject: string;\n latestSummary: string;\n matchedNameTokens: string[];\n matchedContextTokens: string[];\n matchedAliasTokens: string[];\n cosineSimilarity: number;\n coverageScore: number;\n aliasScore: number;\n phraseScore: number;\n score: number;\n};\n\ntype ProjectResolutionDebug = {\n threadText: string;\n normalizedText: string;\n queryTokens: string[];\n candidates: ProjectResolutionCandidate[];\n selectedProject?: string;\n};\n\ntype ProjectResolutionProfile = {\n source: ProjectResolutionSource;\n normalizedProject: string;\n nameTokens: string[];\n summaryTokens: string[];\n contextTokens: string[];\n aliasTokens: string[];\n documentTokens: string[];\n};\n\nexport async function startSlackListener(workspacePath: string): Promise<void> {\n const { config, storage } = await resolveConfiguredStorage(workspacePath);\n const slackRuntime = config.slackRuntime;\n if (!slackRuntime?.enabled) {\n throw new Error('Slack runtime is not enabled in the saved configuration');\n }\n if (slackRuntime.transport !== 'socket_mode') {\n throw new Error(`Slack transport \"${slackRuntime.transport}\" is not supported yet. Use Socket Mode for v1.`);\n }\n\n const slackIntegration = config.integrations.find((integration) => integration.type === 'slack' && integration.enabled);\n if (!slackIntegration?.credentials?.botToken) {\n throw new Error('Slack integration is enabled but the bot token is missing');\n }\n\n if (!slackRuntime.appToken) {\n throw new Error('Slack runtime app token is missing');\n }\n\n const deps: RuntimeDeps = {\n config,\n storage,\n slackIntegration,\n slackRuntime,\n };\n\n const connector = createSlackConnector();\n await connector.authenticate(slackIntegration);\n\n console.log('Starting real-time Slack listener...');\n console.log(`Transport: ${slackRuntime.transport}`);\n console.log(`Review mode: ${slackRuntime.reviewMode}`);\n console.log(`Generation enabled: ${slackRuntime.generationEnabled ? 'yes' : 'no'}`);\n if (slackRuntime.channelMappings.length > 0) {\n console.log(`Mapped channels: ${slackRuntime.channelMappings.map((mapping) => mapping.channelName ?? mapping.channelId).join(', ')}`);\n } else {\n console.log('Monitored channels: all Slack channels/private groups where the bot is present');\n }\n console.log(`Escalation owner: ${config.user.name}`);\n\n while (true) {\n try {\n const session = await openSocketModeConnection(slackRuntime.appToken, {\n onEvent: (event) => {\n void handleSlackEvent(event, deps).catch((err) => {\n console.error('Slack event handling failed:', err);\n });\n },\n onError: (err) => {\n console.error('Slack Socket Mode error:', err.message);\n },\n });\n\n await session.closed;\n console.warn(`Slack Socket Mode disconnected. Reconnecting in ${RECONNECT_DELAY_MS}ms...`);\n } catch (err) {\n console.error('Slack listener connection failed:', err);\n }\n\n await sleep(RECONNECT_DELAY_MS);\n }\n}\n\nexport async function handleSlackEvent(event: SlackEventPayload, deps: RuntimeDeps): Promise<void> {\n if (!shouldHandleSlackEvent(event) || !event.channel || !event.ts) {\n console.log(`[slack] Ignored unsupported event type=${event.type} subtype=${event.subtype ?? 'none'}`);\n return;\n }\n\n const eventKey = `${event.channel}:${event.ts}`;\n if (hasSeenRecentEvent(eventKey)) {\n console.log(`[slack] Ignored duplicate event ${eventKey}`);\n return;\n }\n markRecentEvent(eventKey);\n\n const mapping = createRuntimeChannelMapping(event.channel, deps.slackRuntime.channelMappings, deps.config.user.name);\n\n if (!isReplyCandidate(event, deps.config.user.assistantName)) {\n console.log(`[slack] Ignored non-request in #${mapping.channelName ?? event.channel}: ${summarizeText(event.text)}`);\n return;\n }\n\n console.log(`[slack] Processing candidate in #${mapping.channelName ?? event.channel}: ${summarizeText(event.text)}`);\n\n const connector = createSlackConnector();\n await connector.authenticate(deps.slackIntegration);\n\n const threadTs = event.thread_ts ?? event.ts;\n const thread = await connector.fetchThread(event.channel, threadTs, mapping.channelName);\n const threadMapping: SlackChannelMapping = {\n ...mapping,\n channelName: thread.channelName ?? mapping.channelName,\n };\n const resolution = await resolveProjectName(deps.storage, thread);\n logProjectResolution(resolution);\n const projectName = resolution.selectedProject;\n if (!projectName) {\n console.log('[slack] Could not resolve a project from the thread; falling back to abstain');\n } else {\n console.log(`[slack] Resolved project: ${projectName}`);\n logSelectedProjectSummary(resolution, projectName);\n }\n\n await writeArtifact(\n deps.storage,\n `${PATHS.context.raw.slack}/${artifactKey(event.channel, threadTs)}.json`,\n {\n receivedAt: new Date().toISOString(),\n event,\n thread,\n },\n );\n\n const replyInput = await buildReplyInput(deps.config, deps.storage, threadMapping, thread, projectName);\n logEvidenceSummary(replyInput.evidence);\n await writeArtifact(\n deps.storage,\n `${PATHS.context.derived.slack.replyInputs}/${artifactKey(event.channel, threadTs)}.json`,\n replyInput,\n );\n\n const draft = await buildDraftOutput(deps.config, replyInput, threadMapping);\n console.log(`[slack] Draft outcome for ${artifactKey(event.channel, threadTs)}: ${draft.outcome}`);\n logDraftPreview(draft);\n if (draft.citations.length > 0) {\n console.log(`[slack] Draft citations: ${draft.citations.join(' | ')}`);\n }\n if (draft.missingFacts.length > 0) {\n console.log(`[slack] Draft missing facts: ${draft.missingFacts.join(' | ')}`);\n }\n await writeArtifact(\n deps.storage,\n `${PATHS.context.derived.slack.drafts}/${artifactKey(event.channel, threadTs)}.json`,\n draft,\n );\n\n let delivery: Record<string, unknown>;\n try {\n delivery = await deliverDraft(connector, deps.slackRuntime, threadMapping, thread, draft, replyInput.projectName);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[slack] Delivery failed for ${artifactKey(event.channel, threadTs)}: ${message}`);\n delivery = {\n mode: deps.slackRuntime.reviewMode,\n deliveredAt: new Date().toISOString(),\n channelId: thread.channelId,\n threadTs: thread.threadTs,\n reviewerUserId: threadMapping.reviewerUserId,\n outcome: 'delivery_error',\n error: message,\n };\n }\n await writeArtifact(\n deps.storage,\n `${PATHS.context.derived.slack.deliveries}/${artifactKey(event.channel, threadTs)}.json`,\n delivery,\n );\n if (delivery.mode === 'human_review') {\n console.log(`[slack] Delivery mode: human_review (ephemeral draft only, visible to reviewer ${threadMapping.reviewerDisplayName ?? threadMapping.reviewerUserId})`);\n console.log(`[slack] Reviewer target: ${threadMapping.reviewerDisplayName ?? 'unknown'} (${threadMapping.reviewerUserId})`);\n } else if (delivery.mode === 'auto_send') {\n console.log('[slack] Delivery mode: auto_send (public thread reply posted)');\n console.log(`[slack] Public reply channel: #${threadMapping.channelName ?? thread.channelId} thread=${thread.threadTs}`);\n }\n console.log(`[slack] Delivery recorded for ${artifactKey(event.channel, threadTs)} (${String(delivery.outcome ?? 'unknown')})`);\n}\n\nexport function shouldHandleSlackEvent(event: SlackEventPayload): boolean {\n if (event.type !== 'message' && event.type !== 'app_mention') return false;\n if (!event.channel || !event.ts) return false;\n if (event.bot_id) return false;\n if (event.type === 'message' && event.subtype && event.subtype !== 'thread_broadcast') return false;\n return true;\n}\n\nexport function isReplyCandidate(event: SlackEventPayload, assistantName?: string): boolean {\n if (!shouldHandleSlackEvent(event)) return false;\n if (event.type === 'app_mention') return true;\n\n const text = event.text?.trim();\n if (!text) return false;\n\n if (assistantName?.trim()) {\n const assistantPattern = new RegExp(`\\\\b${escapeRegExp(assistantName.trim())}\\\\b`, 'i');\n if (assistantPattern.test(text)) return true;\n }\n\n // Keep channel-triggered drafts narrow in v1 so Atlas responds to requests,\n // not to general project chatter.\n return /[?]$/.test(text)\n || /\\b(status|owner|blocked|blocker|eta|when|who|what|where|which|why|how|help|update|decision)\\b/i.test(text)\n || /\\b(tell|show|give)\\s+me\\b/i.test(text)\n || /\\b(can|could|would|should)\\s+you\\b/i.test(text);\n}\n\nexport function resolveChannelMapping(\n channelId: string,\n mappings: SlackChannelMapping[],\n): SlackChannelMapping | undefined {\n return mappings.find((mapping) => mapping.channelId === channelId);\n}\n\nfunction createRuntimeChannelMapping(\n channelId: string,\n mappings: SlackChannelMapping[],\n ownerName: string,\n): SlackChannelMapping {\n return resolveChannelMapping(channelId, mappings) ?? {\n channelId,\n channelName: channelId,\n reviewerUserId: 'config-owner',\n reviewerDisplayName: ownerName || 'Configured user',\n };\n}\n\nexport function extractGitHubResources(text: string): Array<{ repo: string; kind: 'issues' | 'pull'; number: string }> {\n const matches = [...text.matchAll(/github\\.com\\/([^/\\s]+\\/[^/\\s]+)\\/(issues|pull)\\/(\\d+)/g)];\n return matches.map((match) => ({\n repo: match[1],\n kind: match[2] as 'issues' | 'pull',\n number: match[3],\n }));\n}\n\nexport function extractGoogleDocIds(text: string): string[] {\n return [...text.matchAll(/docs\\.google\\.com\\/document\\/d\\/([A-Za-z0-9_-]+)/g)].map((match) => match[1]);\n}\n\nasync function buildReplyInput(\n config: AppConfig,\n storage: StorageBackend,\n mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n projectName?: string,\n): Promise<ReplyInput> {\n const contextManager = createContextManager(storage);\n const projectContext = await contextManager.getProjectContext(projectName ?? '');\n const evidence: ReplyInputEvidence[] = [];\n\n for (const file of projectContext.canonicalFiles) {\n evidence.push({\n source: 'project',\n reference: file.path,\n content: truncate(file.content, 2000),\n });\n }\n\n for (const file of projectContext.derivedFiles) {\n evidence.push({\n source: 'project',\n reference: file.path,\n content: truncate(file.content, 1500),\n });\n }\n\n for (const file of projectContext.rawFiles) {\n evidence.push({\n source: 'raw',\n reference: file.path,\n content: truncate(file.content, 1500),\n });\n }\n\n evidence.push(...await collectGitHubEvidence(config, mapping, thread));\n evidence.push(...await collectGoogleDriveEvidence(config, mapping, thread));\n\n return {\n projectName: projectName ?? '',\n thread,\n evidence,\n reviewMode: config.slackRuntime?.reviewMode ?? 'auto_send',\n provider: config.slackRuntime?.defaultProvider,\n model: config.slackRuntime?.defaultModel,\n };\n}\n\nasync function buildDraftOutput(\n config: AppConfig,\n replyInput: ReplyInput,\n mapping: SlackChannelMapping,\n): Promise<DraftOutput> {\n if (!replyInput.projectName) {\n return {\n outcome: 'abstain',\n draftText: '',\n citations: [],\n missingFacts: ['Could not determine which project the Slack thread is asking about.'],\n escalationOwner: mapping.reviewerDisplayName ?? mapping.reviewerUserId,\n confidence: 'low',\n };\n }\n\n const provider = config.slackRuntime?.defaultProvider;\n const model = config.slackRuntime?.defaultModel;\n\n if (!config.slackRuntime?.generationEnabled || !provider || !model) {\n return {\n outcome: 'abstain',\n draftText: '',\n citations: replyInput.evidence.map((item) => item.reference).slice(0, 5),\n missingFacts: ['Generation is disabled or provider/model is not configured.'],\n escalationOwner: mapping.reviewerDisplayName ?? mapping.reviewerUserId,\n confidence: 'low',\n };\n }\n\n try {\n return await generateDraft(provider as LlmProvider, model, config.providers, replyInput);\n } catch (err) {\n return {\n outcome: 'abstain',\n draftText: '',\n citations: replyInput.evidence.map((item) => item.reference).slice(0, 5),\n missingFacts: [err instanceof Error ? err.message : String(err)],\n escalationOwner: mapping.reviewerDisplayName ?? mapping.reviewerUserId,\n confidence: 'low',\n };\n }\n}\n\nasync function deliverDraft(\n connector: ReturnType<typeof createSlackConnector>,\n slackRuntime: SlackRuntimeConfig,\n mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n draft: DraftOutput,\n projectName?: string,\n): Promise<Record<string, unknown>> {\n if (slackRuntime.reviewMode === 'auto_send') {\n const threadReply = renderAutoSendMessage(draft, projectName);\n const ts = await connector.postThreadReply(thread.channelId, thread.threadTs, threadReply);\n return {\n mode: 'auto_send',\n deliveredAt: new Date().toISOString(),\n channelId: thread.channelId,\n threadTs: thread.threadTs,\n messageTs: ts,\n outcome: draft.outcome,\n postedText: threadReply,\n };\n }\n\n // Human-review delivery is kept in the runtime for future reintroduction,\n // but onboarding defaults to auto_send and no longer exposes it in the UI.\n const reviewText = renderReviewMessage(mapping, draft, projectName);\n await connector.postEphemeral(thread.channelId, mapping.reviewerUserId, reviewText, thread.threadTs);\n return {\n mode: 'human_review',\n deliveredAt: new Date().toISOString(),\n channelId: thread.channelId,\n threadTs: thread.threadTs,\n reviewerUserId: mapping.reviewerUserId,\n outcome: draft.outcome,\n };\n}\n\nasync function collectGitHubEvidence(\n config: AppConfig,\n _mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n): Promise<ReplyInputEvidence[]> {\n const githubIntegration = config.integrations.find((integration) => integration.type === 'github' && integration.enabled);\n const token = githubIntegration?.credentials?.token;\n if (!token) {\n return [];\n }\n\n const linkedResources = extractGitHubResources(thread.messages.map((message) => message.text).join('\\n'))\n .slice(0, 3);\n if (linkedResources.length === 0) {\n return [];\n }\n\n const evidence: ReplyInputEvidence[] = [];\n for (const resource of linkedResources) {\n const endpoint = resource.kind === 'pull'\n ? `https://api.github.com/repos/${resource.repo}/pulls/${resource.number}`\n : `https://api.github.com/repos/${resource.repo}/issues/${resource.number}`;\n const response = await fetch(endpoint, {\n headers: {\n Authorization: `token ${token}`,\n Accept: 'application/vnd.github+json',\n 'User-Agent': 'personal-assistant',\n },\n });\n if (!response.ok) continue;\n\n const data = await response.json() as {\n html_url?: string;\n title?: string;\n body?: string;\n state?: string;\n };\n\n evidence.push({\n source: 'github',\n reference: data.html_url ?? `${resource.repo}#${resource.number}`,\n content: truncate(`${data.title ?? 'Untitled'}\\nState: ${data.state ?? 'unknown'}\\n${data.body ?? ''}`, 1500),\n });\n }\n\n return evidence;\n}\n\nasync function collectGoogleDriveEvidence(\n config: AppConfig,\n _mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n): Promise<ReplyInputEvidence[]> {\n const gdriveIntegration = config.integrations.find((integration) => integration.type === 'gdrive' && integration.enabled);\n const credentials = gdriveIntegration?.credentials;\n if (!credentials?.clientId || !credentials?.clientSecret || !credentials?.refreshToken) {\n return [];\n }\n\n const linkedDocIds = extractGoogleDocIds(thread.messages.map((message) => message.text).join('\\n'))\n .slice(0, 3);\n if (linkedDocIds.length === 0) {\n return [];\n }\n\n const oauth2Client = new google.auth.OAuth2(credentials.clientId, credentials.clientSecret);\n oauth2Client.setCredentials({ refresh_token: credentials.refreshToken });\n const drive = google.drive({ version: 'v3', auth: oauth2Client });\n\n const evidence: ReplyInputEvidence[] = [];\n for (const docId of linkedDocIds) {\n try {\n const meta = await drive.files.get({\n fileId: docId,\n fields: 'name,webViewLink',\n });\n const exported = await drive.files.export({\n fileId: docId,\n mimeType: 'text/plain',\n });\n\n evidence.push({\n source: 'gdrive',\n reference: meta.data.webViewLink ?? docId,\n content: truncate(`${meta.data.name ?? docId}\\n${String(exported.data)}`, 1500),\n });\n } catch {\n // Ignore individual doc failures so a missing doc does not break the Slack reply loop.\n }\n }\n\n return evidence;\n}\n\nfunction renderReviewMessage(mapping: SlackChannelMapping, draft: DraftOutput, projectName?: string): string {\n const lines = [\n `*Atlas draft for ${projectName || 'unknown project'}*`,\n draft.outcome === 'draft'\n ? (draft.draftText || '_No draft text was returned._')\n : '_Atlas abstained because the available context was insufficient._',\n ];\n\n if (draft.citations.length > 0) {\n lines.push(`*Citations*\\n• ${draft.citations.join('\\n• ')}`);\n }\n\n if (draft.missingFacts.length > 0) {\n lines.push(`*Missing facts*\\n• ${draft.missingFacts.join('\\n• ')}`);\n }\n\n if (draft.escalationOwner) {\n lines.push(`*Escalation owner*\\n${draft.escalationOwner}`);\n }\n\n return truncate(lines.join('\\n\\n'), 3500);\n}\n\nfunction renderAutoSendMessage(draft: DraftOutput, projectName?: string): string {\n if (draft.outcome === 'draft' && draft.draftText.trim()) {\n return truncate(draft.draftText.trim(), 3500);\n }\n\n const lines = [\n `I can't answer confidently from the current context${projectName ? ` for ${projectName}` : ''}.`,\n ];\n\n if (draft.missingFacts.length > 0) {\n lines.push(`Missing context: ${draft.missingFacts.join('; ')}`);\n }\n\n if (draft.escalationOwner) {\n lines.push(`Best escalation path: ${draft.escalationOwner}.`);\n }\n\n return truncate(lines.join('\\n\\n'), 3500);\n}\n\nfunction artifactKey(channelId: string, threadTs: string): string {\n return `${channelId}-${threadTs.replace(/[^\\dA-Za-z_-]/g, '_')}`;\n}\n\nasync function writeArtifact(storage: StorageBackend, filePath: string, data: unknown): Promise<void> {\n await storage.write(filePath, JSON.stringify(data, null, 2));\n}\n\nfunction truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\nfunction summarizeText(text: string | undefined, maxLength = 100): string {\n if (!text?.trim()) return '<no text>';\n return truncate(text.replace(/\\s+/g, ' ').trim(), maxLength);\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nasync function resolveProjectName(\n storage: StorageBackend,\n thread: SlackThreadSnapshot,\n): Promise<ProjectResolutionDebug> {\n const contextManager = createContextManager(storage);\n const projectNames = await contextManager.listProjects().catch(() => []);\n const threadText = thread.messages.map((message) => message.text).join(' ');\n const sources = await loadProjectResolutionSources(storage, projectNames);\n return inspectProjectResolution(sources, threadText);\n}\n\nexport function resolveProjectFromText(projectNames: string[], text: string): string | undefined {\n const sources = projectNames.map((projectName) => ({\n projectName,\n latestSummary: '',\n contextText: '',\n }));\n return inspectProjectResolution(sources, text).selectedProject;\n}\n\nexport function inspectProjectResolution(sources: ProjectResolutionSource[], text: string): ProjectResolutionDebug {\n const normalizedText = normalizeForMatching(text);\n const queryTokens = uniqueTokens(tokenizeResolutionText(text));\n if (sources.length === 0) {\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates: [],\n selectedProject: undefined,\n };\n }\n\n if (!normalizedText) {\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates: sources.map((source) => ({\n projectName: source.projectName,\n normalizedProject: normalizeForMatching(source.projectName),\n latestSummary: source.latestSummary,\n matchedNameTokens: [],\n matchedContextTokens: [],\n matchedAliasTokens: [],\n cosineSimilarity: 0,\n coverageScore: 0,\n aliasScore: 0,\n phraseScore: 0,\n score: 0,\n })),\n selectedProject: undefined,\n };\n }\n\n const profiles = sources.map((source) => createProjectResolutionProfile(source));\n const idf = buildInverseDocumentFrequencies(profiles);\n const candidates = profiles.map((profile) => scoreProjectMatch(profile, normalizedText, queryTokens, idf));\n candidates.sort((a, b) => b.score - a.score || a.projectName.localeCompare(b.projectName));\n\n if (sources.length === 1) {\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates,\n selectedProject: candidates[0]?.projectName,\n };\n }\n\n const [best, second] = candidates;\n const selectedProject = best\n && best.score >= PROJECT_RESOLUTION_MIN_SCORE\n && best.score - (second?.score ?? 0) >= PROJECT_RESOLUTION_MIN_MARGIN\n ? best.projectName\n : undefined;\n\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates,\n selectedProject,\n };\n}\n\nfunction scoreProjectMatch(\n profile: ProjectResolutionProfile,\n haystack: string,\n queryTokens: string[],\n idf: Map<string, number>,\n): ProjectResolutionCandidate {\n const { source, normalizedProject } = profile;\n if (!normalizedProject) {\n return {\n projectName: source.projectName,\n normalizedProject,\n latestSummary: source.latestSummary,\n matchedNameTokens: [],\n matchedContextTokens: [],\n matchedAliasTokens: [],\n cosineSimilarity: 0,\n coverageScore: 0,\n aliasScore: 0,\n phraseScore: 0,\n score: 0,\n };\n }\n\n const nameTokenSet = new Set(profile.nameTokens);\n const contextTokenSet = new Set([...profile.summaryTokens, ...profile.contextTokens]);\n const aliasTokenSet = new Set(profile.aliasTokens);\n const documentTokenSet = new Set(profile.documentTokens);\n const queryTokenSet = new Set(queryTokens);\n const fallbackIdf = Math.log((idf.size + 2) / 0.5) + 1;\n\n const matchedNameTokens = queryTokens.filter((token) => nameTokenSet.has(token));\n const matchedAliasTokens = queryTokens.filter((token) => aliasTokenSet.has(token));\n const matchedContextTokens = queryTokens.filter((token) =>\n contextTokenSet.has(token) && !nameTokenSet.has(token) && !aliasTokenSet.has(token));\n\n const queryVector = buildWeightedTokenMap(queryTokens, idf, fallbackIdf, 1);\n const documentVector = new Map<string, number>();\n accumulateTokenWeights(documentVector, profile.nameTokens, idf, fallbackIdf, 3.5);\n accumulateTokenWeights(documentVector, profile.summaryTokens, idf, fallbackIdf, 2.5);\n accumulateTokenWeights(documentVector, profile.contextTokens, idf, fallbackIdf, 1.2);\n accumulateTokenWeights(documentVector, profile.aliasTokens, idf, fallbackIdf, 3);\n\n const totalQueryWeight = sumWeights(queryVector) || 1;\n const matchedQueryWeight = [...queryTokenSet]\n .filter((token) => documentTokenSet.has(token))\n .reduce((sum, token) => sum + (queryVector.get(token) ?? 0), 0);\n const matchedAliasWeight = [...queryTokenSet]\n .filter((token) => aliasTokenSet.has(token))\n .reduce((sum, token) => sum + (queryVector.get(token) ?? 0), 0);\n\n const coverageScore = matchedQueryWeight / totalQueryWeight;\n const aliasScore = matchedAliasWeight / totalQueryWeight;\n const cosineSimilarity = computeCosineSimilarity(queryVector, documentVector);\n const phraseScore = haystack.includes(normalizedProject) ? 1 : 0;\n const score =\n (phraseScore * 1.5)\n + (cosineSimilarity * 1.2)\n + (coverageScore * 1)\n + (aliasScore * 0.8);\n\n return {\n projectName: source.projectName,\n normalizedProject,\n latestSummary: source.latestSummary,\n matchedNameTokens,\n matchedContextTokens,\n matchedAliasTokens,\n cosineSimilarity,\n coverageScore,\n aliasScore,\n phraseScore,\n score,\n };\n}\n\nfunction logProjectResolution(debug: ProjectResolutionDebug): void {\n console.log('[slack] Project resolution strategy: weighted TF-IDF similarity over project name, latest project summary, and recent project context, with stemming, stopword removal, and acronym alias extraction; require unique best score >= 0.45 with margin >= 0.08 or abstain');\n console.log(`[slack] Registered projects: ${debug.candidates.length > 0 ? debug.candidates.map((candidate) => candidate.projectName).join(', ') : 'none'}`);\n console.log(`[slack] Resolution text: ${summarizeText(debug.threadText, 180)}`);\n console.log(`[slack] Normalized text: ${debug.normalizedText || '<empty>'}`);\n console.log(`[slack] Query tokens: ${debug.queryTokens.length > 0 ? debug.queryTokens.join(', ') : 'none'}`);\n\n if (debug.candidates.length === 0) {\n return;\n }\n\n console.log(\n `[slack] Project candidate scores: ${debug.candidates.map((candidate) => `${candidate.projectName}=total:${formatScore(candidate.score)},cosine:${formatScore(candidate.cosineSimilarity)},coverage:${formatScore(candidate.coverageScore)},alias:${formatScore(candidate.aliasScore)},phrase:${formatScore(candidate.phraseScore)}${candidate.matchedNameTokens.length > 0 ? `,name:[${candidate.matchedNameTokens.join(', ')}]` : ''}${candidate.matchedContextTokens.length > 0 ? `,context:[${candidate.matchedContextTokens.join(', ')}]` : ''}${candidate.matchedAliasTokens.length > 0 ? `,aliases:[${candidate.matchedAliasTokens.join(', ')}]` : ''},summary:${JSON.stringify(summarizeText(candidate.latestSummary, 100))}`).join(' | ')}`,\n );\n}\n\nfunction logSelectedProjectSummary(debug: ProjectResolutionDebug, projectName: string): void {\n const selected = debug.candidates.find((candidate) => candidate.projectName === projectName);\n if (!selected?.latestSummary) {\n console.log('[slack] Selected project summary: <empty>');\n return;\n }\n\n console.log('[slack] Selected project summary begin');\n for (const line of selected.latestSummary.split('\\n')) {\n console.log(`[slack] > ${line}`);\n }\n console.log('[slack] Selected project summary end');\n}\n\nfunction logEvidenceSummary(evidence: ReplyInputEvidence[]): void {\n const counts = new Map<string, number>();\n for (const item of evidence) {\n counts.set(item.source, (counts.get(item.source) ?? 0) + 1);\n }\n\n console.log(`[slack] Evidence counts: ${[...counts.entries()].map(([source, count]) => `${source}=${count}`).join(' | ') || 'none'}`);\n\n const rawRefs = evidence\n .filter((item) => item.source === 'raw')\n .map((item) => item.reference);\n if (rawRefs.length > 0) {\n console.log(`[slack] Raw context references: ${rawRefs.join(' | ')}`);\n }\n}\n\nfunction logDraftPreview(draft: DraftOutput): void {\n if (!draft.draftText?.trim()) {\n console.log('[slack] Draft text: <empty>');\n return;\n }\n\n console.log('[slack] Draft text begin');\n for (const line of draft.draftText.trim().split('\\n')) {\n console.log(`[slack] | ${line}`);\n }\n console.log('[slack] Draft text end');\n}\n\nfunction normalizeForMatching(value: string): string {\n return value\n .replace(/<@[A-Z0-9]+>/gi, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .toLowerCase()\n .replace(/[^a-z0-9\\s]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nasync function loadProjectResolutionSources(\n storage: StorageBackend,\n projectNames: string[],\n): Promise<ProjectResolutionSource[]> {\n return Promise.all(projectNames.map(async (projectName) => {\n const filePath = `${PATHS.context.canonical.projects}/${projectName}.md`;\n let content = '';\n try {\n content = await storage.read(filePath);\n } catch {\n // Ignore missing project file and fall back to name-only matching.\n }\n\n return {\n projectName,\n latestSummary: extractLatestProjectSummary(content),\n contextText: extractRecentProjectContext(content),\n };\n }));\n}\n\nfunction extractLatestProjectSummary(content: string): string {\n const normalized = content.trim();\n if (!normalized) return '';\n\n const noteSections = normalized.split(/\\n## Note - [^\\n]+\\n+/).map((section) => section.trim()).filter(Boolean);\n if (noteSections.length > 0) {\n return truncate(noteSections[noteSections.length - 1], 800);\n }\n\n return truncate(normalized, 800);\n}\n\nfunction extractRecentProjectContext(content: string): string {\n const normalized = content.trim();\n if (!normalized) return '';\n return truncate(normalized.slice(-2500), 2500);\n}\n\nfunction tokenizeResolutionText(value: string): string[] {\n if (!value) return [];\n\n return normalizeForMatching(value)\n .split(' ')\n .map((token) => stemResolutionToken(token.trim()))\n .filter((token) => token.length > 2 && !RESOLUTION_STOPWORDS.has(token));\n}\n\nfunction stemResolutionToken(token: string): string {\n if (!token) return '';\n\n let stemmed = token.toLowerCase();\n if (stemmed.endsWith(\"'s\")) {\n stemmed = stemmed.slice(0, -2);\n }\n\n if (stemmed.length > 4 && stemmed.endsWith('ies')) {\n return `${stemmed.slice(0, -3)}y`;\n }\n if (stemmed.length > 5 && stemmed.endsWith('ing')) {\n stemmed = stemmed.slice(0, -3);\n } else if (stemmed.length > 4 && stemmed.endsWith('ed')) {\n stemmed = stemmed.slice(0, -2);\n }\n\n if (stemmed.length > 4 && stemmed.endsWith('es')) {\n stemmed = stemmed.slice(0, -2);\n } else if (stemmed.length > 3 && stemmed.endsWith('s') && !stemmed.endsWith('ss')) {\n stemmed = stemmed.slice(0, -1);\n }\n\n return stemmed;\n}\n\nfunction createProjectResolutionProfile(source: ProjectResolutionSource): ProjectResolutionProfile {\n const nameTokens = tokenizeResolutionText(source.projectName);\n const summaryTokens = tokenizeResolutionText(source.latestSummary);\n const contextTokens = tokenizeResolutionText(source.contextText);\n const aliasTokens = uniqueTokens([\n ...extractAcronymTokens(source.projectName),\n ...extractAcronymTokens(source.latestSummary),\n ...extractAcronymTokens(source.contextText),\n ]);\n\n return {\n source,\n normalizedProject: normalizeForMatching(source.projectName),\n nameTokens,\n summaryTokens,\n contextTokens,\n aliasTokens,\n documentTokens: uniqueTokens([...nameTokens, ...summaryTokens, ...contextTokens, ...aliasTokens]),\n };\n}\n\nfunction extractAcronymTokens(value: string): string[] {\n if (!value) return [];\n\n const aliases = new Set<string>();\n\n for (const match of value.matchAll(/\\b(?:[A-Z][a-z0-9]+){2,}\\b/g)) {\n const acronym = (match[0].match(/[A-Z]/g) ?? []).join('').toLowerCase();\n if (acronym.length >= 2) {\n aliases.add(acronym);\n }\n }\n\n for (const match of value.matchAll(/\\b[A-Z][A-Za-z0-9]+(?:[\\s_-]+[A-Z][A-Za-z0-9]+){1,4}\\b/g)) {\n const words = match[0].split(/[\\s_-]+/).filter(Boolean);\n const acronym = words.map((word) => word[0]?.toLowerCase() ?? '').join('');\n if (acronym.length >= 2) {\n aliases.add(acronym);\n }\n }\n\n const separatorParts = value\n .split(/[\\s_-]+/)\n .map((part) => part.replace(/[^A-Za-z0-9]/g, ''))\n .filter(Boolean);\n if (separatorParts.length >= 2 && separatorParts.length <= 5) {\n const acronym = separatorParts.map((part) => part[0]?.toLowerCase() ?? '').join('');\n if (acronym.length >= 2) {\n aliases.add(acronym);\n }\n }\n\n return [...aliases].map((token) => stemResolutionToken(token)).filter((token) => token.length > 1);\n}\n\nfunction uniqueTokens(tokens: string[]): string[] {\n return [...new Set(tokens.filter(Boolean))];\n}\n\nfunction buildInverseDocumentFrequencies(profiles: ProjectResolutionProfile[]): Map<string, number> {\n const frequencies = new Map<string, number>();\n\n for (const profile of profiles) {\n for (const token of new Set(profile.documentTokens)) {\n frequencies.set(token, (frequencies.get(token) ?? 0) + 1);\n }\n }\n\n const totalDocuments = Math.max(profiles.length, 1);\n const idf = new Map<string, number>();\n for (const [token, frequency] of frequencies.entries()) {\n idf.set(token, Math.log((totalDocuments + 1) / (frequency + 0.5)) + 1);\n }\n return idf;\n}\n\nfunction buildWeightedTokenMap(\n tokens: string[],\n idf: Map<string, number>,\n fallbackIdf: number,\n multiplier: number,\n): Map<string, number> {\n const weights = new Map<string, number>();\n accumulateTokenWeights(weights, tokens, idf, fallbackIdf, multiplier);\n return weights;\n}\n\nfunction accumulateTokenWeights(\n weights: Map<string, number>,\n tokens: string[],\n idf: Map<string, number>,\n fallbackIdf: number,\n multiplier: number,\n): void {\n for (const token of tokens) {\n const idfWeight = idf.get(token) ?? fallbackIdf;\n weights.set(token, (weights.get(token) ?? 0) + (idfWeight * multiplier));\n }\n}\n\nfunction sumWeights(weights: Map<string, number>): number {\n let total = 0;\n for (const value of weights.values()) {\n total += value;\n }\n return total;\n}\n\nfunction computeCosineSimilarity(left: Map<string, number>, right: Map<string, number>): number {\n let dot = 0;\n let leftNorm = 0;\n let rightNorm = 0;\n\n for (const value of left.values()) {\n leftNorm += value * value;\n }\n\n for (const value of right.values()) {\n rightNorm += value * value;\n }\n\n for (const [token, leftValue] of left.entries()) {\n dot += leftValue * (right.get(token) ?? 0);\n }\n\n if (leftNorm === 0 || rightNorm === 0) {\n return 0;\n }\n\n return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));\n}\n\nfunction formatScore(value: number): string {\n return value.toFixed(3);\n}\n\nfunction hasSeenRecentEvent(eventKey: string): boolean {\n const seenAt = recentEventKeys.get(eventKey);\n if (!seenAt) return false;\n if (Date.now() - seenAt > RECENT_EVENT_TTL_MS) {\n recentEventKeys.delete(eventKey);\n return false;\n }\n return true;\n}\n\nfunction markRecentEvent(eventKey: string): void {\n pruneRecentEvents();\n recentEventKeys.set(eventKey, Date.now());\n}\n\nfunction pruneRecentEvents(): void {\n const now = Date.now();\n for (const [eventKey, seenAt] of recentEventKeys.entries()) {\n if (now - seenAt > RECENT_EVENT_TTL_MS) {\n recentEventKeys.delete(eventKey);\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/slack-listener.ts"],"sourcesContent":["import { google } from 'googleapis';\nimport { createContextManager } from '@personal-assistant/context-manager';\nimport {\n createSlackConnector,\n openSocketModeConnection,\n type SlackEventPayload,\n} from '@personal-assistant/integrations-slack';\nimport { WORKSPACE_PATHS as PATHS } from '@personal-assistant/core-types';\nimport type {\n AppConfig,\n DraftOutput,\n IntegrationConfig,\n LlmProvider,\n ReplyInput,\n ReplyInputEvidence,\n SlackChannelMapping,\n SlackRuntimeConfig,\n SlackThreadSnapshot,\n StorageBackend,\n} from '@personal-assistant/core-types';\nimport { generateDraft } from './provider-runtime.js';\nimport { resolveConfiguredStorage } from './storage.js';\n\nconst RECONNECT_DELAY_MS = 5000;\nconst RECENT_EVENT_TTL_MS = 5 * 60 * 1000;\nconst PROJECT_RESOLUTION_MIN_SCORE = 0.45;\nconst PROJECT_RESOLUTION_CONTEXT_ANCHORED_MIN_SCORE = 0.25;\nconst PROJECT_RESOLUTION_MIN_MARGIN = 0.08;\nconst PROJECT_RESOLUTION_FUZZY_MATCH_WEIGHT = 0.85;\nconst PROJECT_RESOLUTION_FUZZY_SCORE_BOOST = 0.35;\nconst recentEventKeys = new Map<string, number>();\nconst RESOLUTION_STOPWORDS = new Set([\n 'a', 'about', 'after', 'again', 'all', 'also', 'an', 'and', 'any', 'are', 'as', 'at',\n 'back', 'be', 'been', 'before', 'being', 'between', 'both', 'but', 'by', 'can', 'could',\n 'date', 'did', 'do', 'does', 'effective', 'for', 'from', 'had', 'has', 'have', 'help',\n 'here', 'how', 'i', 'if', 'in', 'into', 'is', 'it', 'its', 'just', 'latest', 'me', 'more',\n 'most', 'my', 'need', 'of', 'on', 'only', 'or', 'other', 'our', 'out', 'over', 'same',\n 'share', 'should', 'show', 'tell', 'than', 'that', 'the', 'their', 'them', 'there',\n 'these', 'they', 'this', 'those', 'through', 'to', 'under', 'update', 'us', 'using', 'want',\n 'was', 'we', 'what', 'when', 'where', 'which', 'while', 'who', 'why', 'with', 'would',\n 'you', 'your',\n]);\n\ntype RuntimeDeps = {\n config: AppConfig;\n storage: StorageBackend;\n slackIntegration: IntegrationConfig;\n slackRuntime: SlackRuntimeConfig;\n};\n\ntype ProjectResolutionSource = {\n projectName: string;\n latestSummary: string;\n contextText: string;\n};\n\ntype ProjectResolutionCandidate = {\n projectName: string;\n normalizedProject: string;\n latestSummary: string;\n matchedNameTokens: string[];\n matchedContextTokens: string[];\n matchedAliasTokens: string[];\n matchedFuzzyNameTokens: string[];\n matchedFuzzyContextTokens: string[];\n matchedFuzzyAliasTokens: string[];\n cosineSimilarity: number;\n coverageScore: number;\n aliasScore: number;\n phraseScore: number;\n score: number;\n};\n\ntype ProjectResolutionDebug = {\n threadText: string;\n normalizedText: string;\n queryTokens: string[];\n candidates: ProjectResolutionCandidate[];\n selectedProject?: string;\n};\n\ntype ProjectResolutionProfile = {\n source: ProjectResolutionSource;\n normalizedProject: string;\n nameTokens: string[];\n summaryTokens: string[];\n contextTokens: string[];\n aliasTokens: string[];\n documentTokens: string[];\n};\n\nexport async function startSlackListener(workspacePath: string): Promise<void> {\n const { config, storage } = await resolveConfiguredStorage(workspacePath);\n const slackRuntime = config.slackRuntime;\n if (!slackRuntime?.enabled) {\n throw new Error('Slack runtime is not enabled in the saved configuration');\n }\n if (slackRuntime.transport !== 'socket_mode') {\n throw new Error(`Slack transport \"${slackRuntime.transport}\" is not supported yet. Use Socket Mode for v1.`);\n }\n\n const slackIntegration = config.integrations.find((integration) => integration.type === 'slack' && integration.enabled);\n if (!slackIntegration?.credentials?.botToken) {\n throw new Error('Slack integration is enabled but the bot token is missing');\n }\n\n if (!slackRuntime.appToken) {\n throw new Error('Slack runtime app token is missing');\n }\n\n const deps: RuntimeDeps = {\n config,\n storage,\n slackIntegration,\n slackRuntime,\n };\n\n const connector = createSlackConnector();\n await connector.authenticate(slackIntegration);\n\n console.log('Starting real-time Slack listener...');\n console.log(`Transport: ${slackRuntime.transport}`);\n console.log(`Review mode: ${slackRuntime.reviewMode}`);\n console.log(`Generation enabled: ${slackRuntime.generationEnabled ? 'yes' : 'no'}`);\n if (slackRuntime.channelMappings.length > 0) {\n console.log(`Mapped channels: ${slackRuntime.channelMappings.map((mapping) => mapping.channelName ?? mapping.channelId).join(', ')}`);\n } else {\n console.log('Monitored channels: all Slack channels/private groups where the bot is present');\n }\n console.log(`Escalation owner: ${config.user.name}`);\n\n while (true) {\n try {\n const session = await openSocketModeConnection(slackRuntime.appToken, {\n onEvent: (event) => {\n void handleSlackEvent(event, deps).catch((err) => {\n console.error('Slack event handling failed:', err);\n });\n },\n onError: (err) => {\n console.error('Slack Socket Mode error:', err.message);\n },\n });\n\n await session.closed;\n console.warn(`Slack Socket Mode disconnected. Reconnecting in ${RECONNECT_DELAY_MS}ms...`);\n } catch (err) {\n console.error('Slack listener connection failed:', err);\n }\n\n await sleep(RECONNECT_DELAY_MS);\n }\n}\n\nexport async function handleSlackEvent(event: SlackEventPayload, deps: RuntimeDeps): Promise<void> {\n if (!shouldHandleSlackEvent(event) || !event.channel || !event.ts) {\n console.log(`[slack] Ignored unsupported event type=${event.type} subtype=${event.subtype ?? 'none'}`);\n return;\n }\n\n const eventKey = `${event.channel}:${event.ts}`;\n if (hasSeenRecentEvent(eventKey)) {\n console.log(`[slack] Ignored duplicate event ${eventKey}`);\n return;\n }\n markRecentEvent(eventKey);\n\n const mapping = createRuntimeChannelMapping(event.channel, deps.slackRuntime.channelMappings, deps.config.user.name);\n\n if (!isReplyCandidate(event, deps.config.user.assistantName)) {\n console.log(`[slack] Ignored non-request in #${mapping.channelName ?? event.channel}: ${summarizeText(event.text)}`);\n return;\n }\n\n console.log(`[slack] Processing candidate in #${mapping.channelName ?? event.channel}: ${summarizeText(event.text)}`);\n\n const connector = createSlackConnector();\n await connector.authenticate(deps.slackIntegration);\n\n const threadTs = event.thread_ts ?? event.ts;\n const thread = await connector.fetchThread(event.channel, threadTs, mapping.channelName);\n const threadMapping: SlackChannelMapping = {\n ...mapping,\n channelName: thread.channelName ?? mapping.channelName,\n };\n const resolution = await resolveProjectName(deps.storage, thread);\n logProjectResolution(resolution);\n const projectName = resolution.selectedProject;\n if (!projectName) {\n console.log('[slack] Could not resolve a project from the thread; falling back to abstain');\n } else {\n console.log(`[slack] Resolved project: ${projectName}`);\n logSelectedProjectSummary(resolution, projectName);\n }\n\n await writeArtifact(\n deps.storage,\n `${PATHS.context.raw.slack}/${artifactKey(event.channel, threadTs)}.json`,\n {\n receivedAt: new Date().toISOString(),\n event,\n thread,\n },\n );\n\n const replyInput = await buildReplyInput(deps.config, deps.storage, threadMapping, thread, projectName);\n logEvidenceSummary(replyInput.evidence);\n await writeArtifact(\n deps.storage,\n `${PATHS.context.derived.slack.replyInputs}/${artifactKey(event.channel, threadTs)}.json`,\n replyInput,\n );\n\n const draft = await buildDraftOutput(deps.config, replyInput, threadMapping);\n console.log(`[slack] Draft outcome for ${artifactKey(event.channel, threadTs)}: ${draft.outcome}`);\n logDraftPreview(draft);\n if (draft.citations.length > 0) {\n console.log(`[slack] Draft citations: ${draft.citations.join(' | ')}`);\n }\n if (draft.missingFacts.length > 0) {\n console.log(`[slack] Draft missing facts: ${draft.missingFacts.join(' | ')}`);\n }\n await writeArtifact(\n deps.storage,\n `${PATHS.context.derived.slack.drafts}/${artifactKey(event.channel, threadTs)}.json`,\n draft,\n );\n\n let delivery: Record<string, unknown>;\n try {\n delivery = await deliverDraft(connector, deps.slackRuntime, threadMapping, thread, draft, replyInput.projectName);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[slack] Delivery failed for ${artifactKey(event.channel, threadTs)}: ${message}`);\n delivery = {\n mode: deps.slackRuntime.reviewMode,\n deliveredAt: new Date().toISOString(),\n channelId: thread.channelId,\n threadTs: thread.threadTs,\n reviewerUserId: threadMapping.reviewerUserId,\n outcome: 'delivery_error',\n error: message,\n };\n }\n await writeArtifact(\n deps.storage,\n `${PATHS.context.derived.slack.deliveries}/${artifactKey(event.channel, threadTs)}.json`,\n delivery,\n );\n if (delivery.mode === 'human_review') {\n console.log(`[slack] Delivery mode: human_review (ephemeral draft only, visible to reviewer ${threadMapping.reviewerDisplayName ?? threadMapping.reviewerUserId})`);\n console.log(`[slack] Reviewer target: ${threadMapping.reviewerDisplayName ?? 'unknown'} (${threadMapping.reviewerUserId})`);\n } else if (delivery.mode === 'auto_send') {\n console.log('[slack] Delivery mode: auto_send (public thread reply posted)');\n console.log(`[slack] Public reply channel: #${threadMapping.channelName ?? thread.channelId} thread=${thread.threadTs}`);\n }\n console.log(`[slack] Delivery recorded for ${artifactKey(event.channel, threadTs)} (${String(delivery.outcome ?? 'unknown')})`);\n}\n\nexport function shouldHandleSlackEvent(event: SlackEventPayload): boolean {\n if (event.type !== 'message' && event.type !== 'app_mention') return false;\n if (!event.channel || !event.ts) return false;\n if (event.bot_id) return false;\n if (event.type === 'message' && event.subtype && event.subtype !== 'thread_broadcast') return false;\n return true;\n}\n\nexport function isReplyCandidate(event: SlackEventPayload, assistantName?: string): boolean {\n if (!shouldHandleSlackEvent(event)) return false;\n if (event.type === 'app_mention') return true;\n\n const text = event.text?.trim();\n if (!text) return false;\n\n if (assistantName?.trim()) {\n const assistantPattern = new RegExp(`\\\\b${escapeRegExp(assistantName.trim())}\\\\b`, 'i');\n if (assistantPattern.test(text)) return true;\n }\n\n // Keep channel-triggered drafts narrow in v1 so Atlas responds to requests,\n // not to general project chatter.\n return /[?]$/.test(text)\n || /\\b(status|owner|blocked|blocker|eta|when|who|what|where|which|why|how|help|update|decision)\\b/i.test(text)\n || /\\b(tell|show|give)\\s+me\\b/i.test(text)\n || /\\b(can|could|would|should)\\s+you\\b/i.test(text);\n}\n\nexport function resolveChannelMapping(\n channelId: string,\n mappings: SlackChannelMapping[],\n): SlackChannelMapping | undefined {\n return mappings.find((mapping) => mapping.channelId === channelId);\n}\n\nfunction createRuntimeChannelMapping(\n channelId: string,\n mappings: SlackChannelMapping[],\n ownerName: string,\n): SlackChannelMapping {\n return resolveChannelMapping(channelId, mappings) ?? {\n channelId,\n channelName: channelId,\n reviewerUserId: 'config-owner',\n reviewerDisplayName: ownerName || 'Configured user',\n };\n}\n\nexport function extractGitHubResources(text: string): Array<{ repo: string; kind: 'issues' | 'pull'; number: string }> {\n const matches = [...text.matchAll(/github\\.com\\/([^/\\s]+\\/[^/\\s]+)\\/(issues|pull)\\/(\\d+)/g)];\n return matches.map((match) => ({\n repo: match[1],\n kind: match[2] as 'issues' | 'pull',\n number: match[3],\n }));\n}\n\nexport function extractGoogleDocIds(text: string): string[] {\n return [...text.matchAll(/docs\\.google\\.com\\/document\\/d\\/([A-Za-z0-9_-]+)/g)].map((match) => match[1]);\n}\n\nasync function buildReplyInput(\n config: AppConfig,\n storage: StorageBackend,\n mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n projectName?: string,\n): Promise<ReplyInput> {\n const contextManager = createContextManager(storage);\n const projectContext = await contextManager.getProjectContext(projectName ?? '');\n const evidence: ReplyInputEvidence[] = [];\n\n for (const file of projectContext.canonicalFiles) {\n evidence.push({\n source: 'project',\n reference: file.path,\n content: truncate(file.content, 2000),\n });\n }\n\n for (const file of projectContext.derivedFiles) {\n evidence.push({\n source: 'project',\n reference: file.path,\n content: truncate(file.content, 1500),\n });\n }\n\n for (const file of projectContext.rawFiles) {\n evidence.push({\n source: 'raw',\n reference: file.path,\n content: truncate(file.content, 1500),\n });\n }\n\n evidence.push(...await collectGitHubEvidence(config, mapping, thread));\n evidence.push(...await collectGoogleDriveEvidence(config, mapping, thread));\n\n return {\n projectName: projectName ?? '',\n thread,\n evidence,\n reviewMode: config.slackRuntime?.reviewMode ?? 'auto_send',\n provider: config.slackRuntime?.defaultProvider,\n model: config.slackRuntime?.defaultModel,\n };\n}\n\nasync function buildDraftOutput(\n config: AppConfig,\n replyInput: ReplyInput,\n mapping: SlackChannelMapping,\n): Promise<DraftOutput> {\n if (!replyInput.projectName) {\n return {\n outcome: 'abstain',\n draftText: '',\n citations: [],\n missingFacts: ['Could not determine which project the Slack thread is asking about.'],\n escalationOwner: mapping.reviewerDisplayName ?? mapping.reviewerUserId,\n confidence: 'low',\n };\n }\n\n const provider = config.slackRuntime?.defaultProvider;\n const model = config.slackRuntime?.defaultModel;\n\n if (!config.slackRuntime?.generationEnabled || !provider || !model) {\n return {\n outcome: 'abstain',\n draftText: '',\n citations: replyInput.evidence.map((item) => item.reference).slice(0, 5),\n missingFacts: ['Generation is disabled or provider/model is not configured.'],\n escalationOwner: mapping.reviewerDisplayName ?? mapping.reviewerUserId,\n confidence: 'low',\n };\n }\n\n try {\n return await generateDraft(provider as LlmProvider, model, config.providers, replyInput);\n } catch (err) {\n return {\n outcome: 'abstain',\n draftText: '',\n citations: replyInput.evidence.map((item) => item.reference).slice(0, 5),\n missingFacts: [err instanceof Error ? err.message : String(err)],\n escalationOwner: mapping.reviewerDisplayName ?? mapping.reviewerUserId,\n confidence: 'low',\n };\n }\n}\n\nasync function deliverDraft(\n connector: ReturnType<typeof createSlackConnector>,\n slackRuntime: SlackRuntimeConfig,\n mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n draft: DraftOutput,\n projectName?: string,\n): Promise<Record<string, unknown>> {\n if (slackRuntime.reviewMode === 'auto_send') {\n const threadReply = renderAutoSendMessage(draft, projectName);\n const ts = await connector.postThreadReply(thread.channelId, thread.threadTs, threadReply);\n return {\n mode: 'auto_send',\n deliveredAt: new Date().toISOString(),\n channelId: thread.channelId,\n threadTs: thread.threadTs,\n messageTs: ts,\n outcome: draft.outcome,\n postedText: threadReply,\n };\n }\n\n // Human-review delivery is kept in the runtime for future reintroduction,\n // but onboarding defaults to auto_send and no longer exposes it in the UI.\n const reviewText = renderReviewMessage(mapping, draft, projectName);\n await connector.postEphemeral(thread.channelId, mapping.reviewerUserId, reviewText, thread.threadTs);\n return {\n mode: 'human_review',\n deliveredAt: new Date().toISOString(),\n channelId: thread.channelId,\n threadTs: thread.threadTs,\n reviewerUserId: mapping.reviewerUserId,\n outcome: draft.outcome,\n };\n}\n\nasync function collectGitHubEvidence(\n config: AppConfig,\n _mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n): Promise<ReplyInputEvidence[]> {\n const githubIntegration = config.integrations.find((integration) => integration.type === 'github' && integration.enabled);\n const token = githubIntegration?.credentials?.token;\n if (!token) {\n return [];\n }\n\n const linkedResources = extractGitHubResources(thread.messages.map((message) => message.text).join('\\n'))\n .slice(0, 3);\n if (linkedResources.length === 0) {\n return [];\n }\n\n const evidence: ReplyInputEvidence[] = [];\n for (const resource of linkedResources) {\n const endpoint = resource.kind === 'pull'\n ? `https://api.github.com/repos/${resource.repo}/pulls/${resource.number}`\n : `https://api.github.com/repos/${resource.repo}/issues/${resource.number}`;\n const response = await fetch(endpoint, {\n headers: {\n Authorization: `token ${token}`,\n Accept: 'application/vnd.github+json',\n 'User-Agent': 'personal-assistant',\n },\n });\n if (!response.ok) continue;\n\n const data = await response.json() as {\n html_url?: string;\n title?: string;\n body?: string;\n state?: string;\n };\n\n evidence.push({\n source: 'github',\n reference: data.html_url ?? `${resource.repo}#${resource.number}`,\n content: truncate(`${data.title ?? 'Untitled'}\\nState: ${data.state ?? 'unknown'}\\n${data.body ?? ''}`, 1500),\n });\n }\n\n return evidence;\n}\n\nasync function collectGoogleDriveEvidence(\n config: AppConfig,\n _mapping: SlackChannelMapping,\n thread: SlackThreadSnapshot,\n): Promise<ReplyInputEvidence[]> {\n const gdriveIntegration = config.integrations.find((integration) => integration.type === 'gdrive' && integration.enabled);\n const credentials = gdriveIntegration?.credentials;\n if (!credentials?.clientId || !credentials?.clientSecret || !credentials?.refreshToken) {\n return [];\n }\n\n const linkedDocIds = extractGoogleDocIds(thread.messages.map((message) => message.text).join('\\n'))\n .slice(0, 3);\n if (linkedDocIds.length === 0) {\n return [];\n }\n\n const oauth2Client = new google.auth.OAuth2(credentials.clientId, credentials.clientSecret);\n oauth2Client.setCredentials({ refresh_token: credentials.refreshToken });\n const drive = google.drive({ version: 'v3', auth: oauth2Client });\n\n const evidence: ReplyInputEvidence[] = [];\n for (const docId of linkedDocIds) {\n try {\n const meta = await drive.files.get({\n fileId: docId,\n fields: 'name,webViewLink',\n });\n const exported = await drive.files.export({\n fileId: docId,\n mimeType: 'text/plain',\n });\n\n evidence.push({\n source: 'gdrive',\n reference: meta.data.webViewLink ?? docId,\n content: truncate(`${meta.data.name ?? docId}\\n${String(exported.data)}`, 1500),\n });\n } catch {\n // Ignore individual doc failures so a missing doc does not break the Slack reply loop.\n }\n }\n\n return evidence;\n}\n\nfunction renderReviewMessage(mapping: SlackChannelMapping, draft: DraftOutput, projectName?: string): string {\n const lines = [\n `*Atlas draft for ${projectName || 'unknown project'}*`,\n draft.outcome === 'draft'\n ? (draft.draftText || '_No draft text was returned._')\n : '_Atlas abstained because the available context was insufficient._',\n ];\n\n if (draft.citations.length > 0) {\n lines.push(`*Citations*\\n• ${draft.citations.join('\\n• ')}`);\n }\n\n if (draft.missingFacts.length > 0) {\n lines.push(`*Missing facts*\\n• ${draft.missingFacts.join('\\n• ')}`);\n }\n\n if (draft.escalationOwner) {\n lines.push(`*Escalation owner*\\n${draft.escalationOwner}`);\n }\n\n return truncate(lines.join('\\n\\n'), 3500);\n}\n\nfunction renderAutoSendMessage(draft: DraftOutput, projectName?: string): string {\n if (draft.outcome === 'draft' && draft.draftText.trim()) {\n return truncate(draft.draftText.trim(), 3500);\n }\n\n const lines = [\n `I can't answer confidently from the current context${projectName ? ` for ${projectName}` : ''}.`,\n ];\n\n if (draft.missingFacts.length > 0) {\n lines.push(`Missing context: ${draft.missingFacts.join('; ')}`);\n }\n\n if (draft.escalationOwner) {\n lines.push(`Best escalation path: ${draft.escalationOwner}.`);\n }\n\n return truncate(lines.join('\\n\\n'), 3500);\n}\n\nfunction artifactKey(channelId: string, threadTs: string): string {\n return `${channelId}-${threadTs.replace(/[^\\dA-Za-z_-]/g, '_')}`;\n}\n\nasync function writeArtifact(storage: StorageBackend, filePath: string, data: unknown): Promise<void> {\n await storage.write(filePath, JSON.stringify(data, null, 2));\n}\n\nfunction truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\nfunction summarizeText(text: string | undefined, maxLength = 100): string {\n if (!text?.trim()) return '<no text>';\n return truncate(text.replace(/\\s+/g, ' ').trim(), maxLength);\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nasync function resolveProjectName(\n storage: StorageBackend,\n thread: SlackThreadSnapshot,\n): Promise<ProjectResolutionDebug> {\n const contextManager = createContextManager(storage);\n const projectNames = await contextManager.listProjects().catch(() => []);\n const threadText = thread.messages.map((message) => message.text).join(' ');\n const sources = await loadProjectResolutionSources(storage, projectNames);\n return inspectProjectResolution(sources, threadText);\n}\n\nexport function resolveProjectFromText(projectNames: string[], text: string): string | undefined {\n const sources = projectNames.map((projectName) => ({\n projectName,\n latestSummary: '',\n contextText: '',\n }));\n return inspectProjectResolution(sources, text).selectedProject;\n}\n\nexport function inspectProjectResolution(sources: ProjectResolutionSource[], text: string): ProjectResolutionDebug {\n const normalizedText = normalizeForMatching(text);\n const queryTokens = uniqueTokens(tokenizeResolutionText(text));\n if (sources.length === 0) {\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates: [],\n selectedProject: undefined,\n };\n }\n\n if (!normalizedText) {\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates: sources.map((source) => ({\n projectName: source.projectName,\n normalizedProject: normalizeForMatching(source.projectName),\n latestSummary: source.latestSummary,\n matchedNameTokens: [],\n matchedContextTokens: [],\n matchedAliasTokens: [],\n matchedFuzzyNameTokens: [],\n matchedFuzzyContextTokens: [],\n matchedFuzzyAliasTokens: [],\n cosineSimilarity: 0,\n coverageScore: 0,\n aliasScore: 0,\n phraseScore: 0,\n score: 0,\n })),\n selectedProject: undefined,\n };\n }\n\n const profiles = sources.map((source) => createProjectResolutionProfile(source));\n const idf = buildInverseDocumentFrequencies(profiles);\n const candidates = profiles.map((profile) => scoreProjectMatch(profile, normalizedText, queryTokens, idf));\n candidates.sort((a, b) => b.score - a.score || a.projectName.localeCompare(b.projectName));\n\n if (sources.length === 1) {\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates,\n selectedProject: candidates[0]?.projectName,\n };\n }\n\n const [best, second] = candidates;\n const margin = best ? best.score - (second?.score ?? 0) : 0;\n const selectedProject = best\n && margin >= PROJECT_RESOLUTION_MIN_MARGIN\n && (\n best.score >= PROJECT_RESOLUTION_MIN_SCORE\n || (best.score >= PROJECT_RESOLUTION_CONTEXT_ANCHORED_MIN_SCORE && hasAnchoredProjectEvidence(best))\n )\n ? best.projectName\n : undefined;\n\n return {\n threadText: text,\n normalizedText,\n queryTokens,\n candidates,\n selectedProject,\n };\n}\n\nfunction scoreProjectMatch(\n profile: ProjectResolutionProfile,\n haystack: string,\n queryTokens: string[],\n idf: Map<string, number>,\n): ProjectResolutionCandidate {\n const { source, normalizedProject } = profile;\n if (!normalizedProject) {\n return {\n projectName: source.projectName,\n normalizedProject,\n latestSummary: source.latestSummary,\n matchedNameTokens: [],\n matchedContextTokens: [],\n matchedAliasTokens: [],\n matchedFuzzyNameTokens: [],\n matchedFuzzyContextTokens: [],\n matchedFuzzyAliasTokens: [],\n cosineSimilarity: 0,\n coverageScore: 0,\n aliasScore: 0,\n phraseScore: 0,\n score: 0,\n };\n }\n\n const nameTokenSet = new Set(profile.nameTokens);\n const contextTokenSet = new Set([...profile.summaryTokens, ...profile.contextTokens]);\n const aliasTokenSet = new Set(profile.aliasTokens);\n const documentTokenSet = new Set(profile.documentTokens);\n const queryTokenSet = new Set(queryTokens);\n const fallbackIdf = Math.log((idf.size + 2) / 0.5) + 1;\n\n const matchedNameTokens = queryTokens.filter((token) => nameTokenSet.has(token));\n const matchedAliasTokens = queryTokens.filter((token) => aliasTokenSet.has(token));\n const matchedContextTokens = queryTokens.filter((token) =>\n contextTokenSet.has(token) && !nameTokenSet.has(token) && !aliasTokenSet.has(token));\n const fuzzyMatches = collectFuzzyTokenMatches(profile, queryTokens, new Set([\n ...matchedNameTokens,\n ...matchedAliasTokens,\n ...matchedContextTokens,\n ]));\n const matchedFuzzyNameTokens = fuzzyMatches\n .filter((match) => match.category === 'name')\n .map((match) => `${match.queryToken}~${match.matchedToken}`);\n const matchedFuzzyAliasTokens = fuzzyMatches\n .filter((match) => match.category === 'alias')\n .map((match) => `${match.queryToken}~${match.matchedToken}`);\n const matchedFuzzyContextTokens = fuzzyMatches\n .filter((match) => match.category === 'context')\n .map((match) => `${match.queryToken}~${match.matchedToken}`);\n\n const queryVector = buildWeightedTokenMap(queryTokens, idf, fallbackIdf, 1);\n const documentVector = new Map<string, number>();\n accumulateTokenWeights(documentVector, profile.nameTokens, idf, fallbackIdf, 3.5);\n accumulateTokenWeights(documentVector, profile.summaryTokens, idf, fallbackIdf, 2.5);\n accumulateTokenWeights(documentVector, profile.contextTokens, idf, fallbackIdf, 1.2);\n accumulateTokenWeights(documentVector, profile.aliasTokens, idf, fallbackIdf, 3);\n\n const totalQueryWeight = sumWeights(queryVector) || 1;\n const matchedQueryWeight = [...queryTokenSet]\n .filter((token) => documentTokenSet.has(token))\n .reduce((sum, token) => sum + (queryVector.get(token) ?? 0), 0);\n const matchedAliasWeight = [...queryTokenSet]\n .filter((token) => aliasTokenSet.has(token))\n .reduce((sum, token) => sum + (queryVector.get(token) ?? 0), 0);\n const fuzzyMatchedQueryWeight = fuzzyMatches.reduce(\n (sum, match) => sum + ((queryVector.get(match.queryToken) ?? 0) * PROJECT_RESOLUTION_FUZZY_MATCH_WEIGHT),\n 0,\n );\n const fuzzyMatchedAliasWeight = fuzzyMatches\n .filter((match) => match.category === 'alias')\n .reduce((sum, match) => sum + ((queryVector.get(match.queryToken) ?? 0) * PROJECT_RESOLUTION_FUZZY_MATCH_WEIGHT), 0);\n\n const coverageScore = (matchedQueryWeight + fuzzyMatchedQueryWeight) / totalQueryWeight;\n const aliasScore = (matchedAliasWeight + fuzzyMatchedAliasWeight) / totalQueryWeight;\n const cosineSimilarity = computeCosineSimilarity(queryVector, documentVector);\n const phraseScore = haystack.includes(normalizedProject) ? 1 : 0;\n const fuzzySupportScore = fuzzyMatchedQueryWeight / totalQueryWeight;\n const score =\n (phraseScore * 1.5)\n + (cosineSimilarity * 1.2)\n + (coverageScore * 1)\n + (aliasScore * 0.8)\n + (fuzzySupportScore * PROJECT_RESOLUTION_FUZZY_SCORE_BOOST);\n\n return {\n projectName: source.projectName,\n normalizedProject,\n latestSummary: source.latestSummary,\n matchedNameTokens,\n matchedContextTokens,\n matchedAliasTokens,\n matchedFuzzyNameTokens,\n matchedFuzzyContextTokens,\n matchedFuzzyAliasTokens,\n cosineSimilarity,\n coverageScore,\n aliasScore,\n phraseScore,\n score,\n };\n}\n\nfunction logProjectResolution(debug: ProjectResolutionDebug): void {\n console.log('[slack] Project resolution strategy: weighted TF-IDF similarity over project name, latest project summary, and recent project context, with stemming, stopword removal, acronym alias extraction, and typo-tolerant token matching for longer words; select unique best score >= 0.45 or anchored name+context score >= 0.25, with margin >= 0.08, else abstain');\n console.log(`[slack] Registered projects: ${debug.candidates.length > 0 ? debug.candidates.map((candidate) => candidate.projectName).join(', ') : 'none'}`);\n console.log(`[slack] Resolution text: ${summarizeText(debug.threadText, 180)}`);\n console.log(`[slack] Normalized text: ${debug.normalizedText || '<empty>'}`);\n console.log(`[slack] Query tokens: ${debug.queryTokens.length > 0 ? debug.queryTokens.join(', ') : 'none'}`);\n\n if (debug.candidates.length === 0) {\n return;\n }\n\n console.log(\n `[slack] Project candidate scores: ${debug.candidates.map((candidate) => `${candidate.projectName}=total:${formatScore(candidate.score)},cosine:${formatScore(candidate.cosineSimilarity)},coverage:${formatScore(candidate.coverageScore)},alias:${formatScore(candidate.aliasScore)},phrase:${formatScore(candidate.phraseScore)}${candidate.matchedNameTokens.length > 0 ? `,name:[${candidate.matchedNameTokens.join(', ')}]` : ''}${candidate.matchedContextTokens.length > 0 ? `,context:[${candidate.matchedContextTokens.join(', ')}]` : ''}${candidate.matchedAliasTokens.length > 0 ? `,aliases:[${candidate.matchedAliasTokens.join(', ')}]` : ''}${candidate.matchedFuzzyNameTokens.length > 0 ? `,fuzzyName:[${candidate.matchedFuzzyNameTokens.join(', ')}]` : ''}${candidate.matchedFuzzyContextTokens.length > 0 ? `,fuzzyContext:[${candidate.matchedFuzzyContextTokens.join(', ')}]` : ''}${candidate.matchedFuzzyAliasTokens.length > 0 ? `,fuzzyAliases:[${candidate.matchedFuzzyAliasTokens.join(', ')}]` : ''},summary:${JSON.stringify(summarizeText(candidate.latestSummary, 100))}`).join(' | ')}`,\n );\n}\n\nfunction hasAnchoredProjectEvidence(candidate: ProjectResolutionCandidate): boolean {\n const nameSignalCount = candidate.matchedNameTokens.length\n + candidate.matchedAliasTokens.length\n + candidate.matchedFuzzyNameTokens.length\n + candidate.matchedFuzzyAliasTokens.length;\n const contextSignalCount = candidate.matchedContextTokens.length + candidate.matchedFuzzyContextTokens.length;\n return nameSignalCount > 0 && contextSignalCount > 0;\n}\n\nfunction logSelectedProjectSummary(debug: ProjectResolutionDebug, projectName: string): void {\n const selected = debug.candidates.find((candidate) => candidate.projectName === projectName);\n if (!selected?.latestSummary) {\n console.log('[slack] Selected project summary: <empty>');\n return;\n }\n\n console.log('[slack] Selected project summary begin');\n for (const line of selected.latestSummary.split('\\n')) {\n console.log(`[slack] > ${line}`);\n }\n console.log('[slack] Selected project summary end');\n}\n\nfunction logEvidenceSummary(evidence: ReplyInputEvidence[]): void {\n const counts = new Map<string, number>();\n for (const item of evidence) {\n counts.set(item.source, (counts.get(item.source) ?? 0) + 1);\n }\n\n console.log(`[slack] Evidence counts: ${[...counts.entries()].map(([source, count]) => `${source}=${count}`).join(' | ') || 'none'}`);\n\n const rawRefs = evidence\n .filter((item) => item.source === 'raw')\n .map((item) => item.reference);\n if (rawRefs.length > 0) {\n console.log(`[slack] Raw context references: ${rawRefs.join(' | ')}`);\n }\n}\n\nfunction logDraftPreview(draft: DraftOutput): void {\n if (!draft.draftText?.trim()) {\n console.log('[slack] Draft text: <empty>');\n return;\n }\n\n console.log('[slack] Draft text begin');\n for (const line of draft.draftText.trim().split('\\n')) {\n console.log(`[slack] | ${line}`);\n }\n console.log('[slack] Draft text end');\n}\n\nfunction normalizeForMatching(value: string): string {\n return value\n .replace(/<@[A-Z0-9]+>/gi, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .toLowerCase()\n .replace(/[^a-z0-9\\s]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nasync function loadProjectResolutionSources(\n storage: StorageBackend,\n projectNames: string[],\n): Promise<ProjectResolutionSource[]> {\n return Promise.all(projectNames.map(async (projectName) => {\n const filePath = `${PATHS.context.canonical.projects}/${projectName}.md`;\n let content = '';\n try {\n content = await storage.read(filePath);\n } catch {\n // Ignore missing project file and fall back to name-only matching.\n }\n\n return {\n projectName,\n latestSummary: extractLatestProjectSummary(content),\n contextText: extractRecentProjectContext(content),\n };\n }));\n}\n\nfunction extractLatestProjectSummary(content: string): string {\n const normalized = content.trim();\n if (!normalized) return '';\n\n const noteSections = normalized.split(/\\n## Note - [^\\n]+\\n+/).map((section) => section.trim()).filter(Boolean);\n if (noteSections.length > 0) {\n return truncate(noteSections[noteSections.length - 1], 800);\n }\n\n return truncate(normalized, 800);\n}\n\nfunction extractRecentProjectContext(content: string): string {\n const normalized = content.trim();\n if (!normalized) return '';\n return truncate(normalized.slice(-2500), 2500);\n}\n\nfunction tokenizeResolutionText(value: string): string[] {\n if (!value) return [];\n\n return normalizeForMatching(value)\n .split(' ')\n .map((token) => stemResolutionToken(token.trim()))\n .filter((token) => token.length > 2 && !RESOLUTION_STOPWORDS.has(token));\n}\n\nfunction stemResolutionToken(token: string): string {\n if (!token) return '';\n\n let stemmed = token.toLowerCase();\n if (stemmed.endsWith(\"'s\")) {\n stemmed = stemmed.slice(0, -2);\n }\n\n if (stemmed.length > 4 && stemmed.endsWith('ies')) {\n return `${stemmed.slice(0, -3)}y`;\n }\n if (stemmed.length > 5 && stemmed.endsWith('ing')) {\n stemmed = stemmed.slice(0, -3);\n } else if (stemmed.length > 4 && stemmed.endsWith('ed')) {\n stemmed = stemmed.slice(0, -2);\n }\n\n if (stemmed.length > 4 && stemmed.endsWith('es')) {\n stemmed = stemmed.slice(0, -2);\n } else if (stemmed.length > 3 && stemmed.endsWith('s') && !stemmed.endsWith('ss')) {\n stemmed = stemmed.slice(0, -1);\n }\n\n return stemmed;\n}\n\nfunction createProjectResolutionProfile(source: ProjectResolutionSource): ProjectResolutionProfile {\n const nameTokens = tokenizeResolutionText(source.projectName);\n const summaryTokens = tokenizeResolutionText(source.latestSummary);\n const contextTokens = tokenizeResolutionText(source.contextText);\n const aliasTokens = uniqueTokens([\n ...extractAcronymTokens(source.projectName),\n ...extractAcronymTokens(source.latestSummary),\n ...extractAcronymTokens(source.contextText),\n ]);\n\n return {\n source,\n normalizedProject: normalizeForMatching(source.projectName),\n nameTokens,\n summaryTokens,\n contextTokens,\n aliasTokens,\n documentTokens: uniqueTokens([...nameTokens, ...summaryTokens, ...contextTokens, ...aliasTokens]),\n };\n}\n\nfunction extractAcronymTokens(value: string): string[] {\n if (!value) return [];\n\n const aliases = new Set<string>();\n\n for (const match of value.matchAll(/\\b(?:[A-Z][a-z0-9]+){2,}\\b/g)) {\n const acronym = (match[0].match(/[A-Z]/g) ?? []).join('').toLowerCase();\n if (acronym.length >= 2) {\n aliases.add(acronym);\n }\n }\n\n for (const match of value.matchAll(/\\b[A-Z][A-Za-z0-9]+(?:[\\s_-]+[A-Z][A-Za-z0-9]+){1,4}\\b/g)) {\n const words = match[0].split(/[\\s_-]+/).filter(Boolean);\n const acronym = words.map((word) => word[0]?.toLowerCase() ?? '').join('');\n if (acronym.length >= 2) {\n aliases.add(acronym);\n }\n }\n\n const separatorParts = value\n .split(/[\\s_-]+/)\n .map((part) => part.replace(/[^A-Za-z0-9]/g, ''))\n .filter(Boolean);\n if (separatorParts.length >= 2 && separatorParts.length <= 5) {\n const acronym = separatorParts.map((part) => part[0]?.toLowerCase() ?? '').join('');\n if (acronym.length >= 2) {\n aliases.add(acronym);\n }\n }\n\n return [...aliases].map((token) => stemResolutionToken(token)).filter((token) => token.length > 1);\n}\n\ntype FuzzyTokenCategory = 'name' | 'context' | 'alias';\n\ntype FuzzyTokenMatch = {\n queryToken: string;\n matchedToken: string;\n category: FuzzyTokenCategory;\n distance: number;\n};\n\nfunction collectFuzzyTokenMatches(\n profile: ProjectResolutionProfile,\n queryTokens: string[],\n alreadyMatched: Set<string>,\n): FuzzyTokenMatch[] {\n const matches: FuzzyTokenMatch[] = [];\n const usedTargets = new Set<string>();\n\n for (const queryToken of queryTokens) {\n if (alreadyMatched.has(queryToken)) {\n continue;\n }\n\n const match = findBestFuzzyTokenMatch(profile, queryToken, usedTargets);\n if (!match) {\n continue;\n }\n\n matches.push(match);\n usedTargets.add(`${match.category}:${match.matchedToken}`);\n }\n\n return matches;\n}\n\nfunction findBestFuzzyTokenMatch(\n profile: ProjectResolutionProfile,\n queryToken: string,\n usedTargets: Set<string>,\n): FuzzyTokenMatch | undefined {\n const maxDistance = getMaxFuzzyDistance(queryToken);\n if (maxDistance === 0) {\n return undefined;\n }\n\n const candidates: Array<{ token: string; category: FuzzyTokenCategory; priority: number }> = [\n ...profile.nameTokens.map((token) => ({ token, category: 'name' as const, priority: 0 })),\n ...profile.aliasTokens.map((token) => ({ token, category: 'alias' as const, priority: 1 })),\n ...profile.summaryTokens.map((token) => ({ token, category: 'context' as const, priority: 2 })),\n ...profile.contextTokens.map((token) => ({ token, category: 'context' as const, priority: 2 })),\n ];\n\n let best: (FuzzyTokenMatch & { priority: number }) | undefined;\n\n for (const candidate of candidates) {\n if (usedTargets.has(`${candidate.category}:${candidate.token}`)) {\n continue;\n }\n if (!isFuzzyComparable(queryToken, candidate.token, maxDistance)) {\n continue;\n }\n\n const distance = computeBoundedLevenshteinDistance(queryToken, candidate.token, maxDistance);\n if (distance < 1 || distance > maxDistance) {\n continue;\n }\n\n if (\n !best\n || distance < best.distance\n || (distance === best.distance && candidate.priority < best.priority)\n ) {\n best = {\n queryToken,\n matchedToken: candidate.token,\n category: candidate.category,\n distance,\n priority: candidate.priority,\n };\n }\n }\n\n if (!best) {\n return undefined;\n }\n\n return {\n queryToken: best.queryToken,\n matchedToken: best.matchedToken,\n category: best.category,\n distance: best.distance,\n };\n}\n\nfunction getMaxFuzzyDistance(token: string): number {\n if (token.length >= 9) return 3;\n if (token.length >= 7) return 2;\n return 0;\n}\n\nfunction isFuzzyComparable(left: string, right: string, maxDistance: number): boolean {\n if (!left || !right || left === right) {\n return false;\n }\n if (Math.abs(left.length - right.length) > maxDistance) {\n return false;\n }\n if (left[0] !== right[0]) {\n return false;\n }\n\n const requiredPrefixLength = Math.max(1, Math.min(3, maxDistance === 3 ? 2 : 1));\n return left.slice(0, requiredPrefixLength) === right.slice(0, requiredPrefixLength);\n}\n\nfunction computeBoundedLevenshteinDistance(left: string, right: string, maxDistance: number): number {\n if (Math.abs(left.length - right.length) > maxDistance) {\n return maxDistance + 1;\n }\n\n const previous = new Array<number>(right.length + 1);\n const current = new Array<number>(right.length + 1);\n\n for (let j = 0; j <= right.length; j += 1) {\n previous[j] = j;\n }\n\n for (let i = 1; i <= left.length; i += 1) {\n current[0] = i;\n let rowMin = current[0];\n\n for (let j = 1; j <= right.length; j += 1) {\n const substitutionCost = left[i - 1] === right[j - 1] ? 0 : 1;\n current[j] = Math.min(\n previous[j] + 1,\n current[j - 1] + 1,\n previous[j - 1] + substitutionCost,\n );\n rowMin = Math.min(rowMin, current[j]);\n }\n\n if (rowMin > maxDistance) {\n return maxDistance + 1;\n }\n\n for (let j = 0; j <= right.length; j += 1) {\n previous[j] = current[j];\n }\n }\n\n return previous[right.length];\n}\n\nfunction uniqueTokens(tokens: string[]): string[] {\n return [...new Set(tokens.filter(Boolean))];\n}\n\nfunction buildInverseDocumentFrequencies(profiles: ProjectResolutionProfile[]): Map<string, number> {\n const frequencies = new Map<string, number>();\n\n for (const profile of profiles) {\n for (const token of new Set(profile.documentTokens)) {\n frequencies.set(token, (frequencies.get(token) ?? 0) + 1);\n }\n }\n\n const totalDocuments = Math.max(profiles.length, 1);\n const idf = new Map<string, number>();\n for (const [token, frequency] of frequencies.entries()) {\n idf.set(token, Math.log((totalDocuments + 1) / (frequency + 0.5)) + 1);\n }\n return idf;\n}\n\nfunction buildWeightedTokenMap(\n tokens: string[],\n idf: Map<string, number>,\n fallbackIdf: number,\n multiplier: number,\n): Map<string, number> {\n const weights = new Map<string, number>();\n accumulateTokenWeights(weights, tokens, idf, fallbackIdf, multiplier);\n return weights;\n}\n\nfunction accumulateTokenWeights(\n weights: Map<string, number>,\n tokens: string[],\n idf: Map<string, number>,\n fallbackIdf: number,\n multiplier: number,\n): void {\n for (const token of tokens) {\n const idfWeight = idf.get(token) ?? fallbackIdf;\n weights.set(token, (weights.get(token) ?? 0) + (idfWeight * multiplier));\n }\n}\n\nfunction sumWeights(weights: Map<string, number>): number {\n let total = 0;\n for (const value of weights.values()) {\n total += value;\n }\n return total;\n}\n\nfunction computeCosineSimilarity(left: Map<string, number>, right: Map<string, number>): number {\n let dot = 0;\n let leftNorm = 0;\n let rightNorm = 0;\n\n for (const value of left.values()) {\n leftNorm += value * value;\n }\n\n for (const value of right.values()) {\n rightNorm += value * value;\n }\n\n for (const [token, leftValue] of left.entries()) {\n dot += leftValue * (right.get(token) ?? 0);\n }\n\n if (leftNorm === 0 || rightNorm === 0) {\n return 0;\n }\n\n return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));\n}\n\nfunction formatScore(value: number): string {\n return value.toFixed(3);\n}\n\nfunction hasSeenRecentEvent(eventKey: string): boolean {\n const seenAt = recentEventKeys.get(eventKey);\n if (!seenAt) return false;\n if (Date.now() - seenAt > RECENT_EVENT_TTL_MS) {\n recentEventKeys.delete(eventKey);\n return false;\n }\n return true;\n}\n\nfunction markRecentEvent(eventKey: string): void {\n pruneRecentEvents();\n recentEventKeys.set(eventKey, Date.now());\n}\n\nfunction pruneRecentEvents(): void {\n const now = Date.now();\n for (const [eventKey, seenAt] of recentEventKeys.entries()) {\n if (now - seenAt > RECENT_EVENT_TTL_MS) {\n recentEventKeys.delete(eventKey);\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mukulaggarwal/pacman",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Privacy-first personal assistant context manager with MCP, onboarding, and Slack runtime",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../integrations-slack/dist/index.js"],"sourcesContent":["// src/index.ts\nimport WebSocket from \"ws\";\nvar SLACK_API_BASE = \"https://slack.com/api\";\nvar SlackConnector = class {\n type = \"slack\";\n token;\n channels;\n async authenticate(config) {\n this.token = config.credentials?.botToken;\n this.channels = config.channels;\n if (!this.token) {\n throw new Error(\"Slack bot token is required\");\n }\n }\n async healthCheck() {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n const data = await slackApiRequest(\n this.token,\n \"auth.test\"\n );\n return {\n summary: `Connected as ${data.user ?? \"unknown\"} in ${data.team ?? \"unknown workspace\"}`\n };\n }\n async fetchDelta(cursor) {\n if (!this.token) throw new Error(\"Not authenticated\");\n const channelIds = this.channels?.length ? this.channels : (await this.listChannels()).map((channel) => channel.id);\n const oldest = cursor ? Math.floor(new Date(cursor).getTime() / 1e3).toString() : void 0;\n const items = [];\n for (const channelId of channelIds) {\n const data = await slackApiRequest(\n this.token,\n \"conversations.history\",\n {\n channel: channelId,\n limit: \"50\",\n ...oldest ? { oldest } : {}\n }\n );\n for (const msg of data.messages ?? []) {\n items.push({\n id: `${channelId}:${msg.ts}`,\n source: \"slack\",\n type: \"message\",\n content: msg.text ?? \"\",\n metadata: {\n user: msg.user,\n botId: msg.bot_id,\n channel: channelId,\n subtype: msg.subtype,\n threadTs: msg.thread_ts\n },\n timestamp: new Date(parseFloat(msg.ts) * 1e3).toISOString()\n });\n }\n }\n return {\n items,\n newCursor: (/* @__PURE__ */ new Date()).toISOString(),\n hasMore: false\n };\n }\n async normalize(rawItems) {\n return rawItems.map((item) => ({\n id: item.id,\n source: \"slack\",\n title: `Slack message from ${item.metadata.user ?? \"unknown\"}`,\n body: item.content,\n entities: extractMentions(item.content),\n timestamp: item.timestamp\n }));\n }\n async listChannels() {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n const channels = [];\n let cursor;\n do {\n const data = await slackApiRequest(\n this.token,\n \"conversations.list\",\n {\n limit: \"200\",\n types: \"public_channel,private_channel\",\n exclude_archived: \"true\",\n ...cursor ? { cursor } : {}\n }\n );\n for (const channel of data.channels ?? []) {\n if (!channel.id || !channel.name) continue;\n channels.push({\n id: channel.id,\n name: channel.name,\n isPrivate: !!channel.is_private\n });\n }\n cursor = data.response_metadata?.next_cursor || void 0;\n } while (cursor);\n return channels.sort((a, b) => a.name.localeCompare(b.name));\n }\n async listUsers() {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n const users = [];\n let cursor;\n do {\n const data = await slackApiRequest(\n this.token,\n \"users.list\",\n {\n limit: \"200\",\n ...cursor ? { cursor } : {}\n }\n );\n for (const member of data.members ?? []) {\n if (!member.id || member.deleted || member.is_bot) continue;\n users.push({\n id: member.id,\n name: member.name ?? member.real_name ?? member.id,\n realName: member.real_name\n });\n }\n cursor = data.response_metadata?.next_cursor || void 0;\n } while (cursor);\n return users.sort((a, b) => a.name.localeCompare(b.name));\n }\n async fetchThread(channelId, threadTs, channelName) {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n const data = await slackApiRequest(\n this.token,\n \"conversations.replies\",\n {\n channel: channelId,\n ts: threadTs,\n limit: \"200\"\n }\n );\n const permalink = await this.getPermalink(channelId, threadTs).catch(() => void 0);\n const messages = (data.messages ?? []).map(toSlackThreadMessage);\n return {\n channelId,\n channelName,\n threadTs,\n permalink,\n messages\n };\n }\n async getPermalink(channelId, messageTs) {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n const data = await slackApiRequest(\n this.token,\n \"chat.getPermalink\",\n { channel: channelId, message_ts: messageTs }\n );\n return data.permalink;\n }\n async postThreadReply(channelId, threadTs, text) {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n const data = await slackApiRequest(\n this.token,\n \"chat.postMessage\",\n void 0,\n {\n channel: channelId,\n thread_ts: threadTs,\n text\n }\n );\n return data.ts ?? threadTs;\n }\n async postEphemeral(channelId, userId, text, threadTs) {\n if (!this.token) throw new Error(\"Slack bot token is required\");\n await slackApiRequest(\n this.token,\n \"chat.postEphemeral\",\n void 0,\n {\n channel: channelId,\n user: userId,\n thread_ts: threadTs,\n text\n }\n );\n }\n};\nasync function validateSlackAppToken(appToken) {\n await slackApiRequest(\n appToken,\n \"apps.connections.open\",\n void 0,\n {},\n \"POST\"\n );\n}\nasync function openSocketModeConnection(appToken, handlers, createSocket = (url) => new WebSocket(url)) {\n const data = await slackApiRequest(\n appToken,\n \"apps.connections.open\",\n void 0,\n {},\n \"POST\"\n );\n if (!data.url) {\n throw new Error(\"Slack did not return a Socket Mode URL\");\n }\n const socket = createSocket(data.url);\n await new Promise((resolve, reject) => {\n let settled = false;\n socket.on(\"open\", () => {\n settled = true;\n resolve();\n });\n socket.on(\"error\", (err) => {\n if (!settled) {\n reject(err);\n return;\n }\n handlers.onError?.(err);\n });\n });\n socket.on(\"message\", (rawData) => {\n try {\n const envelope = JSON.parse(rawData.toString());\n if (envelope.envelope_id) {\n socket.send(JSON.stringify({ envelope_id: envelope.envelope_id }));\n }\n handlers.onEnvelope?.(envelope);\n const event = envelope.payload?.event;\n if (event) {\n handlers.onEvent?.(event);\n }\n } catch (err) {\n handlers.onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n });\n const closed = new Promise((resolve) => {\n socket.on(\"close\", () => {\n resolve();\n });\n });\n return {\n close: () => socket.close(),\n closed\n };\n}\nfunction toSlackThreadMessage(message) {\n return {\n ts: message.ts,\n text: message.text ?? \"\",\n userId: message.user,\n botId: message.bot_id,\n subtype: message.subtype,\n threadTs: message.thread_ts\n };\n}\nfunction extractMentions(text) {\n const mentions = text.match(/<@[\\w]+>/g) ?? [];\n return mentions.map((m) => m.replace(/<@|>/g, \"\"));\n}\nasync function slackApiRequest(token, method, params, body, httpMethod = body ? \"POST\" : \"GET\") {\n const url = new URL(`${SLACK_API_BASE}/${method}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== void 0 && value !== \"\") {\n url.searchParams.set(key, value);\n }\n }\n }\n const response = await fetch(url, {\n method: httpMethod,\n headers: {\n Authorization: `Bearer ${token}`,\n ...body ? { \"Content-Type\": \"application/json; charset=utf-8\" } : {}\n },\n ...body ? { body: JSON.stringify(body) } : {}\n });\n const data = await response.json();\n if (!response.ok || !data.ok) {\n throw new Error(`Slack API ${method} failed: ${data.error ?? response.statusText}`);\n }\n return data;\n}\nfunction createSlackConnector() {\n return new SlackConnector();\n}\nexport {\n SlackConnector,\n createSlackConnector,\n openSocketModeConnection,\n validateSlackAppToken\n};\n"],"mappings":"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","names":[]}