@indexnetwork/protocol 3.12.0-rc.287.1 → 4.0.0-rc.289.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/chat/chat.prompt.js +5 -5
  2. package/dist/chat/chat.prompt.js.map +1 -1
  3. package/dist/chat/tests/chat.graph.mocks.d.ts +1 -7
  4. package/dist/chat/tests/chat.graph.mocks.d.ts.map +1 -1
  5. package/dist/chat/tests/chat.graph.mocks.js +1 -2
  6. package/dist/chat/tests/chat.graph.mocks.js.map +1 -1
  7. package/dist/{profile/profile.enricher.d.ts → enrichment/enrichment.enricher.d.ts} +1 -1
  8. package/dist/enrichment/enrichment.enricher.d.ts.map +1 -0
  9. package/dist/{profile/profile.enricher.js → enrichment/enrichment.enricher.js} +1 -1
  10. package/dist/enrichment/enrichment.enricher.js.map +1 -0
  11. package/dist/{profile/profile.generator.d.ts → enrichment/enrichment.generator.d.ts} +3 -3
  12. package/dist/enrichment/enrichment.generator.d.ts.map +1 -0
  13. package/dist/{profile/profile.generator.js → enrichment/enrichment.generator.js} +6 -6
  14. package/dist/enrichment/enrichment.generator.js.map +1 -0
  15. package/dist/{profile/profile.graph.d.ts → enrichment/enrichment.graph.d.ts} +124 -191
  16. package/dist/enrichment/enrichment.graph.d.ts.map +1 -0
  17. package/dist/{profile/profile.graph.js → enrichment/enrichment.graph.js} +33 -241
  18. package/dist/enrichment/enrichment.graph.js.map +1 -0
  19. package/dist/{profile/profile.state.d.ts → enrichment/enrichment.state.d.ts} +31 -19
  20. package/dist/enrichment/enrichment.state.d.ts.map +1 -0
  21. package/dist/{profile/profile.state.js → enrichment/enrichment.state.js} +5 -13
  22. package/dist/enrichment/enrichment.state.js.map +1 -0
  23. package/dist/enrichment/enrichment.tools.d.ts +3 -0
  24. package/dist/enrichment/enrichment.tools.d.ts.map +1 -0
  25. package/dist/{profile/profile.tools.js → enrichment/enrichment.tools.js} +74 -87
  26. package/dist/enrichment/enrichment.tools.js.map +1 -0
  27. package/dist/index.d.ts +4 -4
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/intent/intent.tools.js +3 -3
  32. package/dist/intent/intent.tools.js.map +1 -1
  33. package/dist/opportunity/opportunity.graph.d.ts +1 -7
  34. package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
  35. package/dist/opportunity/opportunity.graph.js +5 -22
  36. package/dist/opportunity/opportunity.graph.js.map +1 -1
  37. package/dist/opportunity/opportunity.presenter.d.ts.map +1 -1
  38. package/dist/opportunity/opportunity.presenter.js +3 -11
  39. package/dist/opportunity/opportunity.presenter.js.map +1 -1
  40. package/dist/opportunity/opportunity.state.d.ts +2 -8
  41. package/dist/opportunity/opportunity.state.d.ts.map +1 -1
  42. package/dist/opportunity/opportunity.state.js.map +1 -1
  43. package/dist/questioner/questioner.presets.js +1 -1
  44. package/dist/questioner/questioner.presets.js.map +1 -1
  45. package/dist/questioner/questioner.tools.d.ts +1 -1
  46. package/dist/questioner/questioner.tools.js +2 -2
  47. package/dist/questioner/questioner.tools.js.map +1 -1
  48. package/dist/shared/agent/tool.factory.js +6 -6
  49. package/dist/shared/agent/tool.factory.js.map +1 -1
  50. package/dist/shared/agent/tool.helpers.d.ts +8 -8
  51. package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
  52. package/dist/shared/agent/tool.helpers.js.map +1 -1
  53. package/dist/shared/agent/tool.registry.js +2 -2
  54. package/dist/shared/agent/tool.registry.js.map +1 -1
  55. package/dist/shared/hyde/hyde.graph.d.ts +6 -6
  56. package/dist/shared/hyde/hyde.state.d.ts +2 -2
  57. package/dist/shared/hyde/hyde.state.js.map +1 -1
  58. package/dist/shared/interfaces/database.interface.d.ts +14 -14
  59. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  60. package/dist/shared/interfaces/database.interface.js.map +1 -1
  61. package/dist/shared/interfaces/{profile-run.interface.d.ts → enrichment-run.interface.d.ts} +21 -21
  62. package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -0
  63. package/dist/shared/interfaces/enrichment-run.interface.js +2 -0
  64. package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -0
  65. package/dist/shared/schemas/discovery-question.schema.d.ts +2 -2
  66. package/dist/shared/schemas/identity.schema.d.ts +45 -0
  67. package/dist/shared/schemas/identity.schema.d.ts.map +1 -0
  68. package/dist/shared/schemas/identity.schema.js +20 -0
  69. package/dist/shared/schemas/identity.schema.js.map +1 -0
  70. package/dist/shared/schemas/question.schema.d.ts +4 -4
  71. package/dist/shared/schemas/question.schema.d.ts.map +1 -1
  72. package/dist/shared/schemas/question.schema.js +1 -1
  73. package/dist/shared/schemas/question.schema.js.map +1 -1
  74. package/package.json +1 -1
  75. package/dist/profile/profile.enricher.d.ts.map +0 -1
  76. package/dist/profile/profile.enricher.js.map +0 -1
  77. package/dist/profile/profile.generator.d.ts.map +0 -1
  78. package/dist/profile/profile.generator.js.map +0 -1
  79. package/dist/profile/profile.graph.d.ts.map +0 -1
  80. package/dist/profile/profile.graph.js.map +0 -1
  81. package/dist/profile/profile.state.d.ts.map +0 -1
  82. package/dist/profile/profile.state.js.map +0 -1
  83. package/dist/profile/profile.tools.d.ts +0 -3
  84. package/dist/profile/profile.tools.d.ts.map +0 -1
  85. package/dist/profile/profile.tools.js.map +0 -1
  86. package/dist/shared/interfaces/profile-run.interface.d.ts.map +0 -1
  87. package/dist/shared/interfaces/profile-run.interface.js +0 -2
  88. package/dist/shared/interfaces/profile-run.interface.js.map +0 -1
  89. package/dist/shared/schemas/profile.schema.d.ts +0 -100
  90. package/dist/shared/schemas/profile.schema.d.ts.map +0 -1
  91. package/dist/shared/schemas/profile.schema.js +0 -26
  92. package/dist/shared/schemas/profile.schema.js.map +0 -1
@@ -1,14 +1,13 @@
1
1
  import { StateGraph, START, END } from "@langchain/langgraph";
2
- import { ProfileGraphState } from "./profile.state.js";
3
- import { ProfileGenerator } from "./profile.generator.js";
4
- import { shouldEnrichGhostDisplayNameFromParallel, isEnrichedNameMeaningful } from "./profile.enricher.js";
2
+ import { EnrichmentGraphState } from "./enrichment.state.js";
3
+ import { shouldEnrichGhostDisplayNameFromParallel, isEnrichedNameMeaningful } from "./enrichment.enricher.js";
5
4
  import { socialsToEnrichmentRequest } from "../shared/utils/social-label.js";
6
5
  import { protocolLogger } from "../shared/observability/protocol.logger.js";
7
6
  import { timed } from "../shared/observability/performance.js";
8
7
  import { requestContext } from "../shared/observability/request-context.js";
9
8
  import { PremiseDecomposer } from "../premise/premise.decomposer.js";
10
9
  import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
11
- const logger = protocolLogger("ProfileGraphFactory");
10
+ const logger = protocolLogger("EnrichmentGraphFactory");
12
11
  /** Minimum length for input to be considered meaningful (e.g. not just "Yes") */
13
12
  const MIN_MEANINGFUL_INPUT_LENGTH = 20;
14
13
  /** Phrases that are confirmations only and must not be used as profile content */
@@ -51,7 +50,7 @@ function isMeaningfulProfileInput(input) {
51
50
  * - Read/Write separation (query vs write)
52
51
  * - Conditional generation (skip generation if profile already exists)
53
52
  */
54
- export class ProfileGraphFactory {
53
+ export class EnrichmentGraphFactory {
55
54
  constructor(database, scraper, enricher, questionerEnqueue, premiseGraph) {
56
55
  this.database = database;
57
56
  this.scraper = scraper;
@@ -60,7 +59,6 @@ export class ProfileGraphFactory {
60
59
  this.premiseGraph = premiseGraph;
61
60
  }
62
61
  createGraph() {
63
- const profileGenerator = new ProfileGenerator();
64
62
  const premiseDecomposer = new PremiseDecomposer();
65
63
  // ─────────────────────────────────────────────────────────
66
64
  // NODE: Check State
@@ -476,171 +474,12 @@ export class ProfileGraphFactory {
476
474
  });
477
475
  };
478
476
  // ─────────────────────────────────────────────────────────
479
- // NODE: Generate Profile
480
- // Generates profile from input using ProfileGenerator agent.
481
- // If updating existing profile, merges new information intelligently.
482
- // ─────────────────────────────────────────────────────────
483
- const generateProfileNode = async (state) => {
484
- return timed("ProfileGraph.generateProfile", async () => {
485
- if (!state.input) {
486
- logger.error("No input provided for profile generation");
487
- return {
488
- error: "Input required for profile generation"
489
- };
490
- }
491
- logger.verbose("Starting profile generation...", {
492
- hasExistingProfile: !!state.profile,
493
- isUpdate: state.forceUpdate,
494
- inputLength: state.input.length
495
- });
496
- const agentTimingsAccum = [];
497
- try {
498
- // If updating existing profile, include it in the input for context
499
- let inputWithContext = state.input;
500
- if (state.profile && state.forceUpdate) {
501
- if (state.isAggregate) {
502
- inputWithContext = `EXISTING PROFILE:\n${JSON.stringify(state.profile, null, 2)}\n\nPREMISE SYNTHESIS:\n${state.input}\n\nRegenerate the profile by synthesizing the premises above. Use the existing profile as context for continuity, but the premises are the authoritative source. Output the full updated profile.`;
503
- logger.verbose("Aggregate synthesis with existing profile context");
504
- }
505
- else {
506
- inputWithContext = `EXISTING PROFILE:\n${JSON.stringify(state.profile, null, 2)}\n\nUSER REQUEST:\n${state.input}\n\nApply the user's request to the existing profile. Preserve existing data unless the user asks to change or remove it. You may add, update, or remove skills and interests as requested. Output the full updated profile.`;
507
- logger.verbose("Merging with existing profile");
508
- }
509
- }
510
- const _traceEmitterProfileGen = requestContext.getStore()?.traceEmitter;
511
- const profileGeneratorStart = Date.now();
512
- _traceEmitterProfileGen?.({ type: "agent_start", name: "profile-generator" });
513
- const result = await profileGenerator.invoke(inputWithContext);
514
- agentTimingsAccum.push({ name: 'profile.generator', durationMs: Date.now() - profileGeneratorStart });
515
- _traceEmitterProfileGen?.({ type: "agent_end", name: "profile-generator", durationMs: Date.now() - profileGeneratorStart, summary: `Generated profile for ${result.output.identity.name || "user"}` });
516
- logger.verbose("✅ Profile generated successfully", {
517
- name: result.output.identity.name,
518
- skillsCount: result.output.attributes.skills.length,
519
- interestsCount: result.output.attributes.interests.length
520
- });
521
- return {
522
- profile: {
523
- ...result.output,
524
- userId: state.userId,
525
- },
526
- agentTimings: agentTimingsAccum,
527
- operationsPerformed: { generatedProfile: true }
528
- };
529
- }
530
- catch (error) {
531
- logger.error("Profile generation failed", {
532
- error: error instanceof Error ? error.message : String(error)
533
- });
534
- return {
535
- error: `Profile generation failed: ${error instanceof Error ? error.message : String(error)}`,
536
- agentTimings: agentTimingsAccum
537
- };
538
- }
539
- });
540
- };
541
- // ─────────────────────────────────────────────────────────
542
- // NODE: Save Profile
543
- // Saves the generated profile to DB (no embedding)
544
- // ─────────────────────────────────────────────────────────
545
- const saveProfileNode = async (state) => {
546
- return timed("ProfileGraph.saveProfile", async () => {
547
- if (!state.profile || !state.profile.identity) {
548
- logger.error("Profile or identity missing in save step");
549
- return {
550
- error: "Profile missing in save step"
551
- };
552
- }
553
- logger.verbose("Saving profile to DB...", {
554
- userId: state.userId
555
- });
556
- try {
557
- const profile = { ...state.profile };
558
- await this.database.saveProfile(state.userId, profile);
559
- logger.verbose("✅ Profile saved successfully");
560
- // Fetch active premises so the questioner can see what's already covered
561
- let existingPremises = [];
562
- try {
563
- const activePremises = await this.database.getPremisesForUser(state.userId, 'ACTIVE');
564
- existingPremises = activePremises.map(p => p.assertion.text);
565
- logger.verbose("Fetched active premises for questioner context", {
566
- userId: state.userId,
567
- count: existingPremises.length,
568
- });
569
- }
570
- catch (premiseErr) {
571
- logger.error("Failed to fetch premises for questioner context — continuing with empty list", {
572
- userId: state.userId,
573
- error: premiseErr instanceof Error ? premiseErr.message : String(premiseErr),
574
- });
575
- }
576
- // Compute profile gaps from missing fields
577
- const gaps = [];
578
- if (!profile.identity?.location)
579
- gaps.push('location');
580
- if (!profile.attributes?.skills?.length)
581
- gaps.push('skills');
582
- if (!profile.attributes?.interests?.length)
583
- gaps.push('interests');
584
- if (!profile.narrative?.context)
585
- gaps.push('current work');
586
- if (gaps.length > 0 && this.questionerEnqueue) {
587
- const userContext = (await this.database.getUserContext(state.userId, null))?.text ?? '';
588
- this.questionerEnqueue({
589
- mode: 'profile',
590
- userId: state.userId,
591
- sourceType: 'profile',
592
- sourceId: state.userId,
593
- context: {
594
- userContext,
595
- gaps,
596
- existingPremises,
597
- },
598
- }).catch((err) => logger.error('Failed to enqueue profile question generation', { userId: state.userId, error: err }));
599
- }
600
- return {
601
- profile,
602
- operationsPerformed: { savedProfile: true }
603
- };
604
- }
605
- catch (error) {
606
- logger.error("Failed to save profile", {
607
- error: error instanceof Error ? error.message : String(error)
608
- });
609
- return {
610
- error: `Failed to save profile: ${error instanceof Error ? error.message : String(error)}`
611
- };
612
- }
613
- });
614
- };
615
- // ─────────────────────────────────────────────────────────
616
- // NODE: Aggregate Profile
617
- // Fetches the user's active premises and synthesizes them into profile input.
618
- // Sets state.input and flags so the existing generate_profile pipeline runs.
619
- // ─────────────────────────────────────────────────────────
620
- const aggregateProfileNode = async (state) => {
621
- return timed("ProfileGraph.aggregateProfile", async () => {
622
- logger.verbose("Aggregating profile from premises...", { userId: state.userId });
623
- const premises = await this.database.getPremisesForUser(state.userId, 'ACTIVE');
624
- if (premises.length === 0) {
625
- logger.verbose("No active premises found — skipping aggregate");
626
- return { operationMode: 'query' };
627
- }
628
- const premiseTexts = premises.map(p => p.assertion.text);
629
- const aggregateInput = `The following are self-descriptions (premises) the user has asserted about themselves:\n\n${premiseTexts.map((t, i) => `${i + 1}. ${t}`).join('\n')}\n\nSynthesize these into a cohesive profile.`;
630
- logger.verbose(`Aggregated ${premises.length} premise(s) into profile input`);
631
- return {
632
- input: aggregateInput,
633
- needsProfileGeneration: true,
634
- forceUpdate: true,
635
- isAggregate: true,
636
- };
637
- });
638
- };
639
- // ─────────────────────────────────────────────────────────
640
477
  // NODE: Decompose Premises
641
- // Decomposes free-text input (chat or scraped) into individual premises,
642
- // creates each via the premise graph, then routes to aggregate_profile
643
- // to synthesize the profile from all active premises.
478
+ // Decomposes free-text input (chat or scraped) into individual premises
479
+ // and creates each via the premise graph. Premise creation is the terminal
480
+ // effect: premise lifecycle events drive user_context regeneration downstream
481
+ // (the legacy aggregate→generate→save profile tail was removed in WS8/IND-365,
482
+ // along with the user_profiles table it wrote to).
644
483
  // ─────────────────────────────────────────────────────────
645
484
  const decomposePremisesNode = async (state) => {
646
485
  return timed("ProfileGraph.decomposePremises", async () => {
@@ -649,13 +488,9 @@ export class ProfileGraphFactory {
649
488
  return { error: "Input required for premise decomposition" };
650
489
  }
651
490
  if (!this.premiseGraph) {
652
- // Fallback: if no premise graph is available, skip decomposition
653
- // and route directly to profile generation (legacy behavior)
654
- logger.warn("No premise graph injected — falling back to direct profile generation");
655
- return {
656
- needsProfileGeneration: true,
657
- forceUpdate: true,
658
- };
491
+ // No premise graph injected nothing to decompose into. End the run.
492
+ logger.warn("No premise graph injected skipping premise decomposition");
493
+ return {};
659
494
  }
660
495
  logger.verbose("Decomposing input into premises...", {
661
496
  userId: state.userId,
@@ -676,12 +511,8 @@ export class ProfileGraphFactory {
676
511
  summary: `Decomposed into ${result.premises.length} premise(s)`,
677
512
  });
678
513
  if (result.premises.length === 0) {
679
- logger.verbose("No premises extracted — skipping decomposition");
680
- // No premises found; fall through to aggregate which will
681
- // synthesize from any existing premises, or to generate_profile
682
- // if needsProfileGeneration is still set from check_state
514
+ logger.verbose("No premises extracted — nothing to create");
683
515
  return {
684
- operationMode: 'aggregate',
685
516
  agentTimings: agentTimingsAccum,
686
517
  };
687
518
  }
@@ -732,9 +563,7 @@ export class ProfileGraphFactory {
732
563
  logger.verbose(`Created ${created}/${result.premises.length} premise(s) (${skippedDuplicates} skipped as near-duplicates)`, {
733
564
  userId: state.userId,
734
565
  });
735
- // Route to aggregate mode to rebuild the profile from all active premises
736
566
  return {
737
- operationMode: 'aggregate',
738
567
  agentTimings: agentTimingsAccum,
739
568
  operationsPerformed: { decomposedPremises: true },
740
569
  };
@@ -743,10 +572,7 @@ export class ProfileGraphFactory {
743
572
  logger.error("Premise decomposition failed", {
744
573
  error: err instanceof Error ? err.message : String(err),
745
574
  });
746
- // Fallback: route to direct profile generation
747
575
  return {
748
- needsProfileGeneration: true,
749
- forceUpdate: true,
750
576
  agentTimings: agentTimingsAccum,
751
577
  };
752
578
  }
@@ -765,11 +591,6 @@ export class ProfileGraphFactory {
765
591
  logger.verbose("Query mode - ending (fast path)");
766
592
  return END;
767
593
  }
768
- // Aggregate mode: Synthesize profile from active premises
769
- if (state.operationMode === 'aggregate') {
770
- logger.verbose("Aggregate mode - synthesizing profile from premises");
771
- return "aggregate_profile";
772
- }
773
594
  // Generate mode: use enrichUserProfile Chat API to auto-generate
774
595
  if (state.operationMode === 'generate') {
775
596
  logger.verbose("Generate mode - routing to auto_generate");
@@ -788,14 +609,15 @@ export class ProfileGraphFactory {
788
609
  // Only use provided input if it's meaningful (not just "Yes" / confirmation)
789
610
  if (state.input && isMeaningfulProfileInput(state.input)) {
790
611
  // Route through premise decomposition when a premise graph is available.
791
- // The decompose node extracts atomic premises, creates them, then
792
- // routes to aggregate_profile for profile synthesis.
612
+ // The decompose node extracts atomic premises and creates them; premise
613
+ // events drive user_context regeneration. Without a premise graph there
614
+ // is nothing to do, so end the run.
793
615
  if (this.premiseGraph) {
794
616
  logger.verbose("Profile generation needed — decomposing input into premises");
795
617
  return "decompose_premises";
796
618
  }
797
- logger.verbose("Profile generation needed with meaningful input provided");
798
- return "generate_profile";
619
+ logger.verbose("Profile generation needed but no premise graph injected — ending");
620
+ return END;
799
621
  }
800
622
  else {
801
623
  logger.verbose("Profile generation needed - scraping first (no meaningful input)");
@@ -810,78 +632,48 @@ export class ProfileGraphFactory {
810
632
  // GRAPH ASSEMBLY
811
633
  // Conditional flow based on operation mode and detected needs
812
634
  // ─────────────────────────────────────────────────────────
813
- const workflow = new StateGraph(ProfileGraphState)
635
+ const workflow = new StateGraph(EnrichmentGraphState)
814
636
  // Add all nodes
815
637
  .addNode("check_state", checkStateNode)
816
638
  .addNode("scrape", scrapeNode)
817
639
  .addNode("decompose_premises", decomposePremisesNode)
818
640
  .addNode("auto_generate", autoGenerateNode)
819
- .addNode("aggregate_profile", aggregateProfileNode)
820
- .addNode("generate_profile", generateProfileNode)
821
- .addNode("save_profile", saveProfileNode)
822
641
  // Start with state check
823
642
  .addEdge(START, "check_state")
824
643
  // Conditional routing from check_state
825
644
  .addConditionalEdges("check_state", checkStateCondition, {
826
645
  auto_generate: "auto_generate", // Generate mode -> Chat API enrichment
827
- aggregate_profile: "aggregate_profile", // Aggregate mode -> synthesize from premises
828
- decompose_premises: "decompose_premises", // Write mode + input + premise graph -> decompose first
829
- scrape: "scrape", // Need profile, no input -> scrape first
830
- generate_profile: "generate_profile", // Need profile, have input -> generate (legacy, no premise graph)
831
- [END]: END // Query mode or everything exists
646
+ decompose_premises: "decompose_premises", // Write mode + input + premise graph -> decompose
647
+ scrape: "scrape", // Need premises, no input -> scrape first
648
+ [END]: END // Query mode, no premise graph, or everything exists
832
649
  })
833
- // Decompose premises routes to aggregate (normal) or generate_profile (fallback)
834
- .addConditionalEdges("decompose_premises", (state) => {
835
- if (state.operationMode === 'aggregate')
836
- return "aggregate_profile";
837
- // Fallback when decomposition failed (no premise graph or error)
838
- if (state.needsProfileGeneration)
839
- return "generate_profile";
840
- return "aggregate_profile";
841
- }, {
842
- aggregate_profile: "aggregate_profile",
843
- generate_profile: "generate_profile",
844
- })
845
- // Aggregate profile: generate if premises found, END if none
846
- .addConditionalEdges("aggregate_profile", (state) => {
847
- if (state.needsProfileGeneration)
848
- return "generate_profile";
849
- logger.verbose("Aggregate mode — no premises, ending");
850
- return END;
851
- }, { generate_profile: "generate_profile", [END]: END })
852
- // Auto-generate routes to decompose_premises (when premise graph
853
- // available) or generate_profile (legacy, no premise graph)
650
+ // Decompose premises creates premises as a side effect, then ends.
651
+ // user_context regeneration is handled downstream by premise lifecycle events.
652
+ .addEdge("decompose_premises", END)
653
+ // Auto-generate routes to premise decomposition (when premise graph
654
+ // available + enrichment produced input), otherwise ends.
854
655
  .addConditionalEdges("auto_generate", (state) => {
855
656
  if (state.input && this.premiseGraph) {
856
657
  logger.verbose("Enrichment produced input — routing to premise decomposition");
857
658
  return "decompose_premises";
858
659
  }
859
- if (state.input) {
860
- logger.verbose("Enrichment produced input — routing to LLM generation (no premise graph)");
861
- return "generate_profile";
862
- }
863
- logger.verbose("Enrichment ended without data (ghost soft-deleted or error) — done");
660
+ logger.verbose("Enrichment ended without usable input (ghost soft-deleted, error, or no premise graph) — done");
864
661
  return END;
865
662
  }, {
866
663
  decompose_premises: "decompose_premises",
867
- generate_profile: "generate_profile",
868
664
  [END]: END,
869
665
  })
870
- // Scrape -> decompose_premises (when premise graph available) or generate_profile (legacy)
666
+ // Scrape -> decompose_premises (when premise graph available), else ends.
871
667
  .addConditionalEdges("scrape", (_state) => {
872
668
  if (this.premiseGraph)
873
669
  return "decompose_premises";
874
- return "generate_profile";
670
+ return END;
875
671
  }, {
876
672
  decompose_premises: "decompose_premises",
877
- generate_profile: "generate_profile",
878
- })
879
- // Generate profile -> Save profile (linear)
880
- .addEdge("generate_profile", "save_profile")
881
- // Save profile -> END (linear)
882
- .addEdge("save_profile", END);
673
+ [END]: END,
674
+ });
883
675
  logger.verbose("Graph built successfully");
884
676
  return workflow.compile();
885
677
  }
886
678
  }
887
- //# sourceMappingURL=profile.graph.js.map
679
+ //# sourceMappingURL=enrichment.graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrichment.graph.js","sourceRoot":"/","sources":["enrichment/enrichment.graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { StateGraph, START, END } from \"@langchain/langgraph\";\nimport { EnrichmentGraphState } from \"./enrichment.state.js\";\nimport { EnrichmentGraphDatabase, PremiseProvenance } from \"../shared/interfaces/database.interface.js\";\nimport { Scraper } from \"../shared/interfaces/scraper.interface.js\";\nimport type { ProfileEnricher } from \"../shared/interfaces/enrichment.interface.js\";\nimport { shouldEnrichGhostDisplayNameFromParallel, isEnrichedNameMeaningful } from \"./enrichment.enricher.js\";\nimport { socialsToEnrichmentRequest } from \"../shared/utils/social-label.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport type { QuestionerEnqueueFn } from \"../questioner/questioner.types.js\";\nimport { timed } from \"../shared/observability/performance.js\";\nimport { requestContext } from \"../shared/observability/request-context.js\";\nimport type { DebugMetaAgent } from \"../chat/chat-streaming.types.js\";\nimport { PremiseDecomposer } from \"../premise/premise.decomposer.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\n/**\n * Compiled premise graph interface. Matches the invoke signature of a compiled LangGraph.\n * Accepted as an optional dependency so write-mode input can be decomposed into premises.\n */\nexport interface CompiledPremiseGraph {\n invoke(input: {\n userId: string;\n assertionText: string;\n tier: 'assertive' | 'contextual';\n operationMode: 'create';\n /** Volatile premises auto-retract once their validity window lapses. */\n volatile?: boolean;\n /** ISO timestamp after which a contextual premise is no longer valid. */\n validUntil?: string;\n provenanceSource?: PremiseProvenance['source'];\n provenanceSourceId?: string;\n }): Promise<{\n premise?: { id: string } | undefined;\n /** Set when the create was skipped because a near-duplicate already exists. */\n duplicateOf?: { premiseId: string; assertionText: string; similarity: number } | undefined;\n error?: string | undefined;\n }>;\n}\n\nconst logger = protocolLogger(\"EnrichmentGraphFactory\");\n\n/** Minimum length for input to be considered meaningful (e.g. not just \"Yes\") */\nconst MIN_MEANINGFUL_INPUT_LENGTH = 20;\n\n/** Phrases that are confirmations only and must not be used as profile content */\nconst CONFIRMATION_PHRASES = new Set([\n \"yes\", \"yeah\", \"yep\", \"sure\", \"ok\", \"okay\", \"go ahead\", \"do it\", \"please\",\n \"correct\", \"right\", \"exactly\", \"absolutely\", \"of course\", \"sounds good\",\n \"create one\", \"create it\", \"set one up\", \"set it up\", \"create my profile\",\n \"create profile\", \"set up profile\", \"create a profile\"\n]);\n\n/**\n * Returns true only if the input contains real profile information.\n * Confirmation-only replies (e.g. \"Yes\" to \"Would you like to create a profile?\")\n * must not be treated as input so we ask for user info / use scraper instead of inventing a profile.\n */\nfunction isMeaningfulProfileInput(input: string | undefined): boolean {\n if (!input || typeof input !== \"string\") return false;\n const trimmed = input.trim();\n if (trimmed.length < MIN_MEANINGFUL_INPUT_LENGTH) return false;\n const lower = trimmed.toLowerCase();\n if (CONFIRMATION_PHRASES.has(lower)) return false;\n if (CONFIRMATION_PHRASES.has(lower.replace(/[.!?]+$/, \"\"))) return false;\n return true;\n}\n\n\n/**\n * Factory class to build and compile the Profile Generation Graph.\n *\n * Flow:\n * 1. check_state - Detect whether profile needs generation\n * 2. Conditional routing based on operation mode and missing components:\n * - Query mode: Return immediately (fast path)\n * - Write mode: Generate only what's needed\n * 3. Profile generation (if needed)\n * 4. Save profile to DB\n *\n * Key Features:\n * - Read/Write separation (query vs write)\n * - Conditional generation (skip generation if profile already exists)\n */\nexport class EnrichmentGraphFactory {\n constructor(\n private database: EnrichmentGraphDatabase,\n private scraper: Scraper,\n private enricher?: ProfileEnricher,\n private questionerEnqueue?: QuestionerEnqueueFn,\n private premiseGraph?: CompiledPremiseGraph,\n ) { }\n\n public createGraph() {\n const premiseDecomposer = new PremiseDecomposer();\n\n // ─────────────────────────────────────────────────────────\n // NODE: Check State\n // Loads existing profile from DB and detects what needs generation:\n // - Profile missing\n // - User information insufficient for scraping\n // ─────────────────────────────────────────────────────────\n const checkStateNode = async (state: typeof EnrichmentGraphState.State) => {\n return timed(\"ProfileGraph.checkState\", async () => {\n if (!state.userId) {\n logger.error(\"Missing userId\");\n return {\n error: \"userId is required\"\n };\n }\n\n logger.verbose(\"Checking profile state...\", {\n userId: state.userId,\n operationMode: state.operationMode,\n forceUpdate: state.forceUpdate\n });\n\n try {\n const profile = await this.database.getProfile(state.userId) as any;\n // \"Has a profile\" now means the user has been enriched into ACTIVE premises\n // (the user_profiles replacement). `getProfile` returns a users-sourced row\n // for every existing user, so its presence no longer signals enrichment --\n // the premise graph is the source of truth for whether generation has run.\n const hasBeenEnriched = (await this.database.getPremisesForUser(state.userId, 'ACTIVE')).length > 0;\n\n // Query mode: Just return the profile (fast path)\n if (state.operationMode === 'query') {\n logger.verbose(\"🚀 Query mode - returning existing profile (fast path)\", {\n hasProfile: hasBeenEnriched\n });\n const profileWithId = hasBeenEnriched ? await this.database.getProfileByUserId(state.userId) : null;\n return {\n profile: hasBeenEnriched ? (profile || undefined) : undefined,\n readResult: hasBeenEnriched\n ? {\n hasProfile: true,\n // Thin identity only. The structured skills/interests attributes are\n // retired (user_profiles removal, WS6); the rich identity text now\n // comes from the global user_context, injected by read_user_profiles.\n profile: {\n id: profileWithId?.id,\n name: profile?.identity?.name,\n bio: profile?.identity?.bio,\n location: profile?.identity?.location,\n },\n }\n : {\n hasProfile: false,\n message:\n \"You don't have a profile yet. Would you like to create one? You can share your LinkedIn, GitHub, or X/Twitter profile, or just tell me about yourself.\",\n },\n };\n }\n\n // Write mode: Detect what needs generation\n // Treat confirmation-only input (e.g. \"Yes\") as no input so we ask for info / use scraper\n const hasMeaningfulInput = !!state.input && isMeaningfulProfileInput(state.input);\n const needsProfileGeneration = !hasBeenEnriched || (state.forceUpdate && hasMeaningfulInput);\n\n // Check if we need to scrape (profile generation needed but no meaningful input provided)\n const willNeedScraping = needsProfileGeneration && !hasMeaningfulInput;\n\n // If we need to scrape, check if we have sufficient user information\n let needsUserInfo = false;\n const missingUserInfo: string[] = [];\n\n if (willNeedScraping) {\n logger.verbose(\"Will need scraping - checking user information...\");\n\n const user = await this.database.getUser(state.userId);\n\n if (!user) {\n logger.error(\"User not found\", { userId: state.userId });\n return {\n error: `User not found: ${state.userId}`\n };\n }\n\n // Check what information we have from the user table (schema: users)\n // Required fields: email, name (always present)\n // Optional fields: intro, avatar, location, socials\n\n const socials = await this.database.getUserSocials(state.userId);\n const hasSocials = socials.length > 0;\n\n // Check if name is a full name (not just email username)\n // For scraping to work well, we need first + last name\n const hasMeaningfulName = user.name &&\n user.name.trim() !== '' &&\n !user.name.includes('@') &&\n user.name.split(/\\s+/).filter(Boolean).length >= 2;\n\n const hasLocation = !!(user.location && user.location.trim() !== '');\n\n // Minimum requirement for accurate scraping:\n // - At least ONE social link (preferred - most reliable for finding the right person)\n // - OR a full name (first + last) - less reliable but workable\n // Location helps disambiguate but is not required\n\n const hasMinimumInfo = hasSocials || hasMeaningfulName;\n\n if (!hasMinimumInfo) {\n needsUserInfo = true;\n\n // Build precise list of what's missing and would help\n if (!hasSocials) {\n missingUserInfo.push('social_urls');\n }\n if (!hasMeaningfulName) {\n missingUserInfo.push('full_name');\n }\n if (!hasLocation) {\n missingUserInfo.push('location'); // Nice to have\n }\n\n logger.verbose(\"⚠️ Insufficient user information for scraping\", {\n hasSocials,\n hasMeaningfulName,\n hasLocation,\n currentName: user.name,\n missingUserInfo\n });\n } else {\n logger.verbose(\"✅ Sufficient user information for scraping\", {\n hasSocials,\n hasMeaningfulName,\n hasLocation,\n willProceedWith: hasSocials ? 'social links' : 'full name'\n });\n }\n }\n\n logger.verbose(\"📊 State detection complete\", {\n hasProfile: hasBeenEnriched,\n needsProfileGeneration,\n needsUserInfo,\n missingUserInfo,\n forceUpdate: state.forceUpdate,\n hasInput: !!state.input,\n hasMeaningfulInput,\n });\n\n return {\n // Only treat the (users-sourced) profile as existing state when the user has\n // actually been enriched; un-enriched users keep `undefined` so the generate\n // path runs from scratch rather than merging into an empty users row.\n profile: hasBeenEnriched ? (profile || undefined) : undefined,\n needsProfileGeneration,\n needsUserInfo,\n missingUserInfo\n };\n } catch (error) {\n logger.error(\"Failed to load profile\", {\n error: error instanceof Error ? error.message : String(error)\n });\n return {\n profile: undefined,\n error: `Failed to load profile from database: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n });\n };\n\n // ─────────────────────────────────────────────────────────\n // NODE: Scrape\n // Scrapes data from web if input is not provided\n // ─────────────────────────────────────────────────────────\n const scrapeNode = async (state: typeof EnrichmentGraphState.State) => {\n return timed(\"ProfileGraph.scrape\", async () => {\n if (state.input && isMeaningfulProfileInput(state.input)) {\n logger.verbose(\"Meaningful input already provided - skipping scrape\");\n return {};\n }\n\n logger.verbose(\"Starting web scrape...\", {\n userId: state.userId\n });\n\n try {\n // Fetch user details to construct objective for web scraping\n const user = await this.database.getUser(state.userId);\n\n if (!user) {\n logger.error(\"User not found\", { userId: state.userId });\n return {\n error: `User not found: ${state.userId}`\n };\n }\n\n // Build scraping objective from available user information\n // Priority: social links (most reliable) > name + location > email\n const socialParts: string[] = [];\n const socials = await this.database.getUserSocials(state.userId);\n for (const s of socials) {\n switch (s.label) {\n case 'twitter': socialParts.push(`X/Twitter: ${s.value}`); break;\n case 'linkedin': socialParts.push(`LinkedIn: ${s.value}`); break;\n case 'github': socialParts.push(`GitHub: ${s.value}`); break;\n case 'telegram': socialParts.push(`Telegram: ${s.value}`); break;\n default: socialParts.push(`Website: ${s.value}`); break;\n }\n }\n\n // Construct objective based on what we have\n let objective = `Find information about ${user.name || 'this person'}`;\n\n if (user.location) {\n objective += ` located in ${user.location}`;\n }\n\n objective += '.\\n\\n';\n\n if (socialParts.length > 0) {\n objective += `Their social profiles:\\n${socialParts.join('\\n')}\\n\\n`;\n objective += 'Use these links to find accurate information about their professional background, skills, and interests.';\n } else if (user.email) {\n objective += `Their email: ${user.email}\\n\\n`;\n objective += 'Search for professional information, skills, and background about this person.';\n } else {\n objective += 'Search for professional information and background about this person.';\n }\n\n logger.verbose(\"Constructed scraping objective\", {\n hasSocials: socialParts.length > 0,\n hasLocation: !!user.location,\n objectivePreview: objective.substring(0, 100)\n });\n\n const scrapedData = await this.scraper.scrape(objective);\n\n logger.verbose(\"✅ Scrape complete\", {\n dataLength: scrapedData?.length || 0\n });\n\n return {\n objective,\n input: scrapedData,\n activeSocialIds: socials.map(s => s.id),\n operationsPerformed: { scraped: true }\n };\n } catch (error) {\n logger.error(\"Scrape failed\", {\n error: error instanceof Error ? error.message : String(error)\n });\n return {\n error: `Web scrape failed: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n });\n };\n\n // ─────────────────────────────────────────────────────────\n // NODE: Auto-Generate (Parallel Chat API enrichment)\n // Calls enrichUserProfile to get structured data, then builds a\n // text blob as input for the decompose → aggregate → generate\n // pipeline. This ensures enriched users get premises with\n // embeddings, making them discoverable via premise-to-premise\n // matching.\n // On failure, falls back to basic user info for LLM generation.\n // Used in 'generate' mode only.\n // ─────────────────────────────────────────────────────────\n const autoGenerateNode = async (state: typeof EnrichmentGraphState.State) => {\n return timed(\"ProfileGraph.autoGenerate\", async () => {\n logger.verbose(\"Starting auto-generate via Chat API enrichment\", {\n userId: state.userId,\n });\n\n try {\n const user = await this.database.getUser(state.userId);\n if (!user) {\n logger.error(\"User not found for auto-generate\", { userId: state.userId });\n return { error: `User not found: ${state.userId}` };\n }\n\n const socials = await this.database.getUserSocials(state.userId);\n const enrichmentSocials = socialsToEnrichmentRequest(socials);\n const request = {\n name: user.name || undefined,\n email: user.email || undefined,\n linkedin: enrichmentSocials.linkedin || undefined,\n twitter: enrichmentSocials.twitter || undefined,\n github: enrichmentSocials.github || undefined,\n telegram: enrichmentSocials.telegram || undefined,\n websites: enrichmentSocials.websites?.length ? enrichmentSocials.websites : undefined,\n };\n\n const buildBasicInfo = () => {\n const parts = [\n user.name ? `Name: ${user.name}` : '',\n user.email ? `Email: ${user.email}` : '',\n user.location ? `Location: ${user.location}` : '',\n user.intro ? `Bio: ${user.intro}` : '',\n ].filter(Boolean).join('\\n');\n return parts || \"No information available\";\n };\n\n if (!this.enricher) {\n logger.warn(\"No enricher configured — falling back to basic info\", { userId: state.userId });\n return {\n input: buildBasicInfo(),\n needsUserInfo: false,\n needsProfileGeneration: true,\n operationsPerformed: { scraped: true },\n };\n }\n\n try {\n const enrichment = await this.enricher.enrichUserProfile(request);\n\n if (enrichment && !enrichment.isHuman) {\n logger.info(\"Enrichment detected non-human entity, soft-deleting ghost\", { userId: state.userId });\n await this.database.softDeleteGhost(state.userId);\n return { error: \"Non-human entity detected\" };\n }\n\n const hasMeaningfulEnrichment = !!enrichment &&\n enrichment.confidentMatch &&\n (\n enrichment.identity.bio.trim().length > 0 ||\n enrichment.narrative.context.trim().length > 0 ||\n enrichment.attributes.skills.length > 0 ||\n enrichment.attributes.interests.length > 0\n );\n\n if (hasMeaningfulEnrichment) {\n if (user.isGhost && !isEnrichedNameMeaningful(user.email || '', enrichment!.identity.name || '')) {\n logger.info(\"Enrichment has content but no real name for ghost, soft-deleting\", { userId: state.userId });\n await this.database.softDeleteGhost(state.userId);\n return { error: \"No real name found for ghost user\" };\n }\n\n logger.verbose(\"Chat API enrichment succeeded\", {\n userId: state.userId,\n skillsCount: enrichment!.attributes.skills.length,\n interestsCount: enrichment!.attributes.interests.length,\n });\n\n // Update user record with enriched data\n const updatePayload: {\n name?: string;\n intro?: string;\n location?: string;\n } = {};\n const enrichedName = enrichment!.identity.name?.trim();\n if (\n enrichedName &&\n shouldEnrichGhostDisplayNameFromParallel(\n { isGhost: !!user.isGhost, name: user.name ?? '', email: user.email ?? '' },\n enrichedName,\n )\n ) {\n updatePayload.name = enrichedName;\n }\n if (enrichment!.identity.bio?.trim()) updatePayload.intro = enrichment!.identity.bio.trim();\n if (enrichment!.identity.location?.trim()) updatePayload.location = enrichment!.identity.location.trim();\n\n const enrichedSocials: { label: string; value: string }[] = [];\n if (enrichment!.socials.twitter) enrichedSocials.push({ label: 'twitter', value: enrichment!.socials.twitter });\n if (enrichment!.socials.linkedin) enrichedSocials.push({ label: 'linkedin', value: enrichment!.socials.linkedin });\n if (enrichment!.socials.github) enrichedSocials.push({ label: 'github', value: enrichment!.socials.github });\n if (enrichment!.socials.telegram) enrichedSocials.push({ label: 'telegram', value: enrichment!.socials.telegram });\n if (enrichment!.socials.websites?.length) {\n for (const w of enrichment!.socials.websites) enrichedSocials.push({ label: 'custom', value: w });\n }\n if (enrichedSocials.length > 0) {\n const existingSocials = await this.database.getUserSocials(state.userId);\n const enrichedLabels = new Set(enrichedSocials.map(s => s.label));\n const kept = existingSocials\n .filter(s => !enrichedLabels.has(s.label) || s.label === 'custom')\n .map(s => ({ label: s.label, value: s.value }));\n const merged = enrichedLabels.has('custom')\n ? [...kept.filter(s => s.label !== 'custom'), ...enrichedSocials]\n : [...kept, ...enrichedSocials];\n await this.database.setUserSocials(state.userId, merged);\n }\n\n if (Object.keys(updatePayload).length > 0) {\n await this.database.updateUser(state.userId, updatePayload);\n }\n\n // Post-enrichment dedup: check if this ghost matches an existing user\n if (user.isGhost) {\n const currentSocials = await this.database.getUserSocials(state.userId);\n const duplicate = await this.database.findDuplicateUser(state.userId, currentSocials);\n if (duplicate) {\n logger.info(\"Post-enrichment dedup: merging ghost into existing user\", {\n ghostId: state.userId,\n targetId: duplicate.id,\n });\n await this.database.mergeGhostUser(state.userId, duplicate.id);\n return { error: `Merged as duplicate of user ${duplicate.id}` };\n }\n }\n\n // Build a text blob from the enrichment result so it flows\n // through premise decomposition (when available) rather than\n // bypassing premises via prePopulatedProfile.\n const enrichmentParts = [\n enrichment!.identity.name ? `My name is ${enrichment!.identity.name}.` : '',\n enrichment!.identity.location ? `I am based in ${enrichment!.identity.location}.` : '',\n enrichment!.identity.bio || '',\n enrichment!.narrative.context || '',\n enrichment!.attributes.skills.length ? `My skills include ${enrichment!.attributes.skills.join(', ')}.` : '',\n enrichment!.attributes.interests.length ? `My interests include ${enrichment!.attributes.interests.join(', ')}.` : '',\n ].filter(Boolean).join('\\n');\n\n return {\n input: enrichmentParts,\n needsUserInfo: false,\n needsProfileGeneration: true,\n forceUpdate: true,\n activeSocialIds: socials.map(s => s.id),\n operationsPerformed: { scraped: true },\n };\n }\n\n if (user.isGhost) {\n logger.info(\"Low-confidence enrichment for ghost, soft-deleting\", { userId: state.userId });\n await this.database.softDeleteGhost(state.userId);\n return { error: \"Enrichment not confident for ghost user\" };\n }\n logger.warn(\"Chat API returned low-signal enrichment, falling back to basic info\", { userId: state.userId });\n } catch (enrichErr) {\n if (user.isGhost) {\n logger.info(\"Enrichment failed for ghost, soft-deleting\", { userId: state.userId });\n await this.database.softDeleteGhost(state.userId);\n return { error: \"Enrichment failed for ghost user\" };\n }\n logger.warn(\"Chat API enrichment failed, falling back to basic info\", {\n userId: state.userId,\n error: enrichErr instanceof Error ? enrichErr.message : String(enrichErr),\n });\n }\n\n return {\n input: buildBasicInfo(),\n needsUserInfo: false,\n needsProfileGeneration: true,\n operationsPerformed: { scraped: true },\n };\n } catch (err) {\n logger.error(\"Auto-generate failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n return { error: `Auto-generate failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n });\n };\n\n // ─────────────────────────────────────────────────────────\n // NODE: Decompose Premises\n // Decomposes free-text input (chat or scraped) into individual premises\n // and creates each via the premise graph. Premise creation is the terminal\n // effect: premise lifecycle events drive user_context regeneration downstream\n // (the legacy aggregate→generate→save profile tail was removed in WS8/IND-365,\n // along with the user_profiles table it wrote to).\n // ─────────────────────────────────────────────────────────\n const decomposePremisesNode = async (state: typeof EnrichmentGraphState.State) => {\n return timed(\"ProfileGraph.decomposePremises\", async () => {\n if (!state.input) {\n logger.error(\"No input for premise decomposition\");\n return { error: \"Input required for premise decomposition\" };\n }\n\n if (!this.premiseGraph) {\n // No premise graph injected — nothing to decompose into. End the run.\n logger.warn(\"No premise graph injected — skipping premise decomposition\");\n return {};\n }\n\n logger.verbose(\"Decomposing input into premises...\", {\n userId: state.userId,\n inputLength: state.input.length,\n });\n\n const agentTimingsAccum: DebugMetaAgent[] = [];\n\n try {\n const _traceEmitter = requestContext.getStore()?.traceEmitter;\n\n const decomposeStart = Date.now();\n _traceEmitter?.({ type: \"agent_start\", name: \"premise-decomposer\" });\n const result = await premiseDecomposer.invoke(state.input);\n const decomposeMs = Date.now() - decomposeStart;\n agentTimingsAccum.push({ name: \"premise.decomposer\", durationMs: decomposeMs });\n _traceEmitter?.({\n type: \"agent_end\",\n name: \"premise-decomposer\",\n durationMs: decomposeMs,\n summary: `Decomposed into ${result.premises.length} premise(s)`,\n });\n\n if (result.premises.length === 0) {\n logger.verbose(\"No premises extracted — nothing to create\");\n return {\n agentTimings: agentTimingsAccum,\n };\n }\n\n logger.verbose(`Creating ${result.premises.length} premise(s) via premise graph`, {\n userId: state.userId,\n });\n\n let created = 0;\n let skippedDuplicates = 0;\n for (const p of result.premises) {\n try {\n // Contextual premises carry an LLM-inferred validity window and are\n // volatile (auto-retract on expiry); assertive premises do not expire.\n const isContextual = p.tier === 'contextual';\n const validUntil = isContextual && p.validityDays\n ? new Date(Date.now() + p.validityDays * 24 * 60 * 60 * 1000).toISOString()\n : undefined;\n\n const premiseResult = await invokeWithAbortSignal(this.premiseGraph, {\n userId: state.userId,\n assertionText: p.text,\n tier: p.tier,\n operationMode: 'create',\n volatile: isContextual,\n ...(validUntil ? { validUntil } : {}),\n ...(state.activeSocialIds?.length\n ? { provenanceSource: 'integration' as const, provenanceSourceId: state.activeSocialIds[0] }\n : {}),\n });\n\n if (premiseResult.premise) {\n created++;\n } else if (premiseResult.duplicateOf) {\n skippedDuplicates++;\n } else if (premiseResult.error) {\n logger.warn(\"Premise creation failed\", {\n text: p.text.substring(0, 60),\n error: premiseResult.error,\n });\n }\n } catch (err) {\n logger.warn(\"Premise creation threw\", {\n text: p.text.substring(0, 60),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n logger.verbose(`Created ${created}/${result.premises.length} premise(s) (${skippedDuplicates} skipped as near-duplicates)`, {\n userId: state.userId,\n });\n\n return {\n agentTimings: agentTimingsAccum,\n operationsPerformed: { decomposedPremises: true },\n };\n } catch (err) {\n logger.error(\"Premise decomposition failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n return {\n agentTimings: agentTimingsAccum,\n };\n }\n });\n };\n\n // ─────────────────────────────────────────────────────────\n // ROUTING CONDITIONS\n // Smart conditional routing based on operation mode and missing components\n // ─────────────────────────────────────────────────────────\n\n /**\n * Route from check_state to next step based on operation mode and detected needs.\n */\n const checkStateCondition = (state: typeof EnrichmentGraphState.State): string => {\n // Query mode: Return immediately (fast path)\n if (state.operationMode === 'query') {\n logger.verbose(\"Query mode - ending (fast path)\");\n return END;\n }\n\n // Generate mode: use enrichUserProfile Chat API to auto-generate\n if (state.operationMode === 'generate') {\n logger.verbose(\"Generate mode - routing to auto_generate\");\n return \"auto_generate\";\n }\n\n // Check if user information is insufficient for scraping\n // Return early so chat graph can request the missing information\n if (state.needsUserInfo) {\n logger.verbose(\"⚠️ Insufficient user info - requesting from user\", {\n missingInfo: state.missingUserInfo\n });\n return END;\n }\n\n // Write mode: Check what needs generation\n if (state.needsProfileGeneration) {\n // Only use provided input if it's meaningful (not just \"Yes\" / confirmation)\n if (state.input && isMeaningfulProfileInput(state.input)) {\n // Route through premise decomposition when a premise graph is available.\n // The decompose node extracts atomic premises and creates them; premise\n // events drive user_context regeneration. Without a premise graph there\n // is nothing to do, so end the run.\n if (this.premiseGraph) {\n logger.verbose(\"Profile generation needed — decomposing input into premises\");\n return \"decompose_premises\";\n }\n logger.verbose(\"Profile generation needed but no premise graph injected — ending\");\n return END;\n } else {\n logger.verbose(\"Profile generation needed - scraping first (no meaningful input)\");\n return \"scrape\";\n }\n }\n\n // Everything exists and is up to date\n logger.verbose(\"All components exist - ending\");\n return END;\n };\n\n\n // ─────────────────────────────────────────────────────────\n // GRAPH ASSEMBLY\n // Conditional flow based on operation mode and detected needs\n // ─────────────────────────────────────────────────────────\n\n const workflow = new StateGraph(EnrichmentGraphState)\n // Add all nodes\n .addNode(\"check_state\", checkStateNode)\n .addNode(\"scrape\", scrapeNode)\n .addNode(\"decompose_premises\", decomposePremisesNode)\n .addNode(\"auto_generate\", autoGenerateNode)\n\n // Start with state check\n .addEdge(START, \"check_state\")\n\n // Conditional routing from check_state\n .addConditionalEdges(\n \"check_state\",\n checkStateCondition,\n {\n auto_generate: \"auto_generate\", // Generate mode -> Chat API enrichment\n decompose_premises: \"decompose_premises\", // Write mode + input + premise graph -> decompose\n scrape: \"scrape\", // Need premises, no input -> scrape first\n [END]: END // Query mode, no premise graph, or everything exists\n }\n )\n\n // Decompose premises creates premises as a side effect, then ends.\n // user_context regeneration is handled downstream by premise lifecycle events.\n .addEdge(\"decompose_premises\", END)\n\n // Auto-generate routes to premise decomposition (when premise graph\n // available + enrichment produced input), otherwise ends.\n .addConditionalEdges(\n \"auto_generate\",\n (state: typeof EnrichmentGraphState.State) => {\n if (state.input && this.premiseGraph) {\n logger.verbose(\"Enrichment produced input — routing to premise decomposition\");\n return \"decompose_premises\";\n }\n logger.verbose(\"Enrichment ended without usable input (ghost soft-deleted, error, or no premise graph) — done\");\n return END;\n },\n {\n decompose_premises: \"decompose_premises\",\n [END]: END,\n }\n )\n\n // Scrape -> decompose_premises (when premise graph available), else ends.\n .addConditionalEdges(\n \"scrape\",\n (_state: typeof EnrichmentGraphState.State) => {\n if (this.premiseGraph) return \"decompose_premises\";\n return END;\n },\n {\n decompose_premises: \"decompose_premises\",\n [END]: END,\n },\n );\n\n logger.verbose(\"Graph built successfully\");\n return workflow.compile();\n }\n}\n"]}
@@ -1,9 +1,8 @@
1
- import { ProfileDocument } from "./profile.generator.js";
2
1
  import type { DebugMetaAgent } from '../chat/chat-streaming.types.js';
3
2
  /**
4
3
  * The Graph State for Profile Generation.
5
4
  */
6
- export declare const ProfileGraphState: import("@langchain/langgraph").AnnotationRoot<{
5
+ export declare const EnrichmentGraphState: import("@langchain/langgraph").AnnotationRoot<{
7
6
  /**
8
7
  * The User ID to link the profile to.
9
8
  */
@@ -15,21 +14,16 @@ export declare const ProfileGraphState: import("@langchain/langgraph").Annotatio
15
14
  /**
16
15
  * Operation mode controls graph flow:
17
16
  * - 'query': Fast path - only retrieve existing profile (no generation)
18
- * - 'write': Full pipeline - generate/update profile and hyde as needed
19
- * - 'generate': Auto-generate profile from user table data via enrichUserProfile Chat API
20
- * - 'aggregate': Synthesize profile from the user's active premises
17
+ * - 'write': Decompose provided/scraped input into premises
18
+ * - 'generate': Auto-enrich from user table data via enrichUserProfile Chat API,
19
+ * then decompose the enrichment into premises
21
20
  */
22
- operationMode: import("@langchain/langgraph").BaseChannel<"query" | "write" | "generate" | "aggregate", "query" | "write" | "generate" | "aggregate" | import("@langchain/langgraph").OverwriteValue<"query" | "write" | "generate" | "aggregate">, unknown>;
21
+ operationMode: import("@langchain/langgraph").BaseChannel<"query" | "write" | "generate", "query" | "write" | "generate" | import("@langchain/langgraph").OverwriteValue<"query" | "write" | "generate">, unknown>;
23
22
  /**
24
23
  * Flag to force profile regeneration even if profile exists.
25
24
  * When true with new input, the graph will re-generate and update the profile.
26
25
  */
27
26
  forceUpdate: import("@langchain/langgraph").BaseChannel<boolean, boolean | import("@langchain/langgraph").OverwriteValue<boolean>, unknown>;
28
- /**
29
- * Flag indicating the profile is being regenerated from aggregated premises.
30
- * When true, `generate_profile` uses synthesis framing instead of "apply user request" framing.
31
- */
32
- isAggregate: import("@langchain/langgraph").BaseChannel<boolean, boolean | import("@langchain/langgraph").OverwriteValue<boolean>, unknown>;
33
27
  /**
34
28
  * Internal objective constructed from user data.
35
29
  */
@@ -48,7 +42,31 @@ export declare const ProfileGraphState: import("@langchain/langgraph").Annotatio
48
42
  /**
49
43
  * The generated or loaded profile document.
50
44
  */
51
- profile: import("@langchain/langgraph").BaseChannel<ProfileDocument | undefined, ProfileDocument | import("@langchain/langgraph").OverwriteValue<ProfileDocument | undefined> | undefined, unknown>;
45
+ profile: import("@langchain/langgraph").BaseChannel<{
46
+ identity: {
47
+ name: string;
48
+ bio: string;
49
+ location: string;
50
+ };
51
+ context: string;
52
+ userId?: string | undefined;
53
+ } | undefined, {
54
+ identity: {
55
+ name: string;
56
+ bio: string;
57
+ location: string;
58
+ };
59
+ context: string;
60
+ userId?: string | undefined;
61
+ } | import("@langchain/langgraph").OverwriteValue<{
62
+ identity: {
63
+ name: string;
64
+ bio: string;
65
+ location: string;
66
+ };
67
+ context: string;
68
+ userId?: string | undefined;
69
+ } | undefined> | undefined, unknown>;
52
70
  /**
53
71
  * Flags to track what needs to be generated.
54
72
  */
@@ -99,8 +117,6 @@ export declare const ProfileGraphState: import("@langchain/langgraph").Annotatio
99
117
  name: string;
100
118
  bio: string;
101
119
  location: string;
102
- skills: string[];
103
- interests: string[];
104
120
  };
105
121
  message?: string;
106
122
  } | undefined, {
@@ -110,8 +126,6 @@ export declare const ProfileGraphState: import("@langchain/langgraph").Annotatio
110
126
  name: string;
111
127
  bio: string;
112
128
  location: string;
113
- skills: string[];
114
- interests: string[];
115
129
  };
116
130
  message?: string;
117
131
  } | import("@langchain/langgraph").OverwriteValue<{
@@ -121,10 +135,8 @@ export declare const ProfileGraphState: import("@langchain/langgraph").Annotatio
121
135
  name: string;
122
136
  bio: string;
123
137
  location: string;
124
- skills: string[];
125
- interests: string[];
126
138
  };
127
139
  message?: string;
128
140
  } | undefined> | undefined, unknown>;
129
141
  }>;
130
- //# sourceMappingURL=profile.state.d.ts.map
142
+ //# sourceMappingURL=enrichment.state.d.ts.map