thumbgate 1.35.0 → 1.37.2

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 (204) hide show
  1. package/.agents/skills/cobble-hot-store-compare-not-clone/SKILL.md +80 -0
  2. package/.agents/skills/colab-compute-honesty-not-clone/SKILL.md +70 -0
  3. package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
  4. package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
  5. package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
  6. package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
  7. package/.agents/skills/token-shunt-honesty-not-clone/SKILL.md +67 -0
  8. package/.agents/skills/typesafe-typed-questions-not-clone/SKILL.md +82 -0
  9. package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
  10. package/.claude-plugin/plugin.json +1 -1
  11. package/.well-known/llms.txt +1 -0
  12. package/.well-known/mcp/server-card.json +1 -1
  13. package/CONTRIBUTING.md +95 -0
  14. package/README.md +195 -632
  15. package/THIRD_PARTY_NOTICES.md +89 -0
  16. package/adapters/claude/.mcp.json +2 -2
  17. package/adapters/forge/forge.yaml +3 -3
  18. package/adapters/future-agi/.mcp.json +8 -0
  19. package/adapters/future-agi/FUTURE_AGI.md +23 -0
  20. package/adapters/future-agi/config.toml +3 -0
  21. package/adapters/future-agi/future-agi-bridge.js +9 -0
  22. package/adapters/future-agi/opencode.json +8 -0
  23. package/adapters/herdr/herdr-plugin.toml +18 -0
  24. package/adapters/mcp/server-stdio.js +238 -25
  25. package/adapters/opencode/opencode.json +1 -1
  26. package/adapters/workos/WORKOS.md +52 -0
  27. package/bin/cli.js +465 -3
  28. package/bin/futureagi-bridge +9 -0
  29. package/config/gate-templates.json +629 -4
  30. package/config/gates/actor-critic-audit.json +34 -0
  31. package/config/gates/default.json +9 -3
  32. package/config/gates/five-walls-governance.json +34 -0
  33. package/config/gates/future-agi-guardrails.json +34 -0
  34. package/config/gates/radware-threat-defense-2026.json +61 -0
  35. package/config/gates/simatree-data-governance.json +33 -0
  36. package/config/mcp-allowlists.json +4 -0
  37. package/config/merge-quality-checks.json +6 -0
  38. package/config/model-candidates.json +312 -29
  39. package/config/model-tiers.json +18 -0
  40. package/config/post-deploy-marketing-pages.json +10 -0
  41. package/config/progressive/01-wire-only.json +11 -0
  42. package/config/progressive/02-dashboard-empty-ok.json +10 -0
  43. package/config/progressive/03-one-lesson.json +10 -0
  44. package/config/progressive/04-warn-fires.json +11 -0
  45. package/config/progressive/05-strict-optional.json +11 -0
  46. package/config/progressive/README.md +15 -0
  47. package/config/schemas/broker-execution-receipt.schema.json +139 -0
  48. package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
  49. package/conformance/provider-attestation/vectors.json +320 -0
  50. package/docs/specs/provider-execution-attestation-v1.md +69 -0
  51. package/openapi/openapi.yaml +15 -0
  52. package/package.json +408 -150
  53. package/public/about.html +2 -2
  54. package/public/ai-malpractice-prevention.html +7 -7
  55. package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
  56. package/public/blog/a-receipt-is-not-world-state.html +388 -0
  57. package/public/blog/git-at-agent-scale.html +374 -0
  58. package/public/blog/no-llm-in-the-gate.html +133 -0
  59. package/public/blog.html +80 -0
  60. package/public/case-studies.html +16 -1
  61. package/public/compare.html +28 -0
  62. package/public/diagnostic.html +216 -7
  63. package/public/docs/connectors.html +39 -0
  64. package/public/federal.html +2 -2
  65. package/public/founders.html +639 -0
  66. package/public/index.html +87 -9
  67. package/public/install.html +8 -8
  68. package/public/learn.html +39 -0
  69. package/public/numbers.html +2 -2
  70. package/public/peter.html +310 -0
  71. package/public/platform-partners.html +119 -0
  72. package/public/pricing.html +24 -3
  73. package/public/privacy.html +117 -0
  74. package/public/pro.html +17 -0
  75. package/public/support.html +62 -0
  76. package/public/terms.html +130 -0
  77. package/public/third-party-notices.html +95 -0
  78. package/public/yt.html +351 -0
  79. package/scripts/action-receipts.js +133 -3
  80. package/scripts/adaptive-governance-arena.js +349 -0
  81. package/scripts/admin-override.js +205 -0
  82. package/scripts/agent-action-inventory.js +869 -0
  83. package/scripts/agent-audit-trace.js +42 -2
  84. package/scripts/agent-egress-policy.js +1117 -0
  85. package/scripts/agent-memory-lifecycle.js +141 -2
  86. package/scripts/agent-operations-planner.js +441 -1
  87. package/scripts/agent-readiness.js +68 -0
  88. package/scripts/agent-security-central.js +647 -0
  89. package/scripts/allowlist-bridge-honesty.js +417 -0
  90. package/scripts/async-job-runner.js +102 -11
  91. package/scripts/audit-trail.js +212 -0
  92. package/scripts/billing.js +1 -1
  93. package/scripts/broker-execution-receipts.js +719 -0
  94. package/scripts/budget-aware-gates-proof.js +423 -0
  95. package/scripts/claude-feedback-sync.js +29 -3
  96. package/scripts/claw-harness-production.js +237 -0
  97. package/scripts/cli-schema.js +203 -1
  98. package/scripts/cobble-hot-store-split.js +600 -0
  99. package/scripts/codex-runbook-flywheel.js +318 -0
  100. package/scripts/colab-compute-honesty.js +281 -0
  101. package/scripts/context-footprint.js +186 -0
  102. package/scripts/contextfs.js +143 -61
  103. package/scripts/dashboard.js +251 -32
  104. package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
  105. package/scripts/docker-sandbox-planner.js +18 -0
  106. package/scripts/double-blind-eval-protocol.js +252 -0
  107. package/scripts/edotenv-rl-gateway.js +259 -0
  108. package/scripts/ensure-production-search-corpus.js +162 -0
  109. package/scripts/eval-holdout.js +311 -0
  110. package/scripts/feedback-aggregate.js +21 -2
  111. package/scripts/feedback-loop.js +87 -5
  112. package/scripts/feedback-quality.js +9 -0
  113. package/scripts/file-ledger-lock.js +4 -1
  114. package/scripts/financial-control-plane.js +41 -1
  115. package/scripts/find-dormant-requires.js +118 -0
  116. package/scripts/fs-utils.js +84 -8
  117. package/scripts/gates-engine.js +826 -68
  118. package/scripts/generate-case-study-outreach.js +24 -15
  119. package/scripts/git-at-scale.js +628 -0
  120. package/scripts/governance-conflict-audit.js +1650 -0
  121. package/scripts/governance-difficulty-curriculum.js +328 -0
  122. package/scripts/graphrag-retrieval.js +275 -0
  123. package/scripts/gurobi-optimizer.js +324 -0
  124. package/scripts/gurobi_optimizer.py +485 -0
  125. package/scripts/harness-selector.js +82 -1
  126. package/scripts/hidden-entry-points.js +284 -0
  127. package/scripts/human-escalation.js +199 -1
  128. package/scripts/hybrid-feedback-context.js +152 -19
  129. package/scripts/intent-governed-execution.js +602 -0
  130. package/scripts/intervention-policy.js +123 -20
  131. package/scripts/jit-harness-compose.js +628 -0
  132. package/scripts/jsonl-watcher.js +10 -0
  133. package/scripts/lesson-embedding-index.js +95 -12
  134. package/scripts/lesson-retrieval.js +105 -19
  135. package/scripts/local-model-profile.js +19 -2
  136. package/scripts/mailer/resend-mailer.js +1 -1
  137. package/scripts/matryoshka-embedding.js +235 -0
  138. package/scripts/mcp-oauth.js +42 -4
  139. package/scripts/mcp-session-handles.js +1016 -0
  140. package/scripts/mcp-wiring-doctor.js +314 -0
  141. package/scripts/memory-firewall.js +115 -2
  142. package/scripts/memory-scope-readiness.js +299 -0
  143. package/scripts/memory-vs-rag-route.js +161 -0
  144. package/scripts/model-tier-router.js +148 -21
  145. package/scripts/nvidia-specdecode-al-doctor.js +536 -0
  146. package/scripts/openui-catalog-compose-honesty.js +593 -0
  147. package/scripts/operational-integrity.js +19 -1
  148. package/scripts/override-audit.js +213 -0
  149. package/scripts/package-manager-honesty-doctor.js +458 -0
  150. package/scripts/pr-manager.js +63 -1
  151. package/scripts/prove-herdr-adapter.js +52 -0
  152. package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
  153. package/scripts/prove-workos.js +73 -0
  154. package/scripts/provider-attestation-conformance.js +192 -0
  155. package/scripts/provider-receipt-contract.js +136 -0
  156. package/scripts/qwen38-max-cost-optimizer.js +401 -0
  157. package/scripts/radware-threat-defense.js +280 -0
  158. package/scripts/rag-embedding-identity.js +221 -0
  159. package/scripts/rag-precision-guardrails.js +112 -2
  160. package/scripts/remote-feedback-capture.js +159 -0
  161. package/scripts/research-agent-harness.js +256 -0
  162. package/scripts/rsi-safety-hillclimb.js +200 -0
  163. package/scripts/rule-sprawl.js +188 -0
  164. package/scripts/schedule-manager.js +147 -0
  165. package/scripts/self-heal.js +8 -0
  166. package/scripts/session-lease.js +415 -0
  167. package/scripts/simatree-data-governance.js +347 -0
  168. package/scripts/slo-alert-engine.js +172 -7
  169. package/scripts/solver-parity.js +539 -0
  170. package/scripts/stealth-memory-injection-gate.js +333 -0
  171. package/scripts/switchyard-router.js +366 -0
  172. package/scripts/telemetry-analytics.js +84 -27
  173. package/scripts/temporal-decay-weighting.js +138 -0
  174. package/scripts/test-all.js +165 -0
  175. package/scripts/token-savings.js +42 -0
  176. package/scripts/token-shunt-honesty.js +502 -0
  177. package/scripts/tool-kpi-tracker.js +108 -5
  178. package/scripts/tool-registry.js +193 -5
  179. package/scripts/typesafe-typed-questions.js +813 -0
  180. package/scripts/universal-claim-evaluator.js +14 -2
  181. package/scripts/vector-store.js +279 -9
  182. package/scripts/workflow-notebook.js +391 -0
  183. package/scripts/workflow-sentinel.js +111 -12
  184. package/scripts/workos-production-guard.js +260 -0
  185. package/scripts/workspace-search-route.js +515 -0
  186. package/server.json +2 -2
  187. package/src/agent-identity-boundary.js +76 -0
  188. package/src/agent-retrieval-cache.js +155 -0
  189. package/src/alert-noise-ledger.js +502 -0
  190. package/src/api/server.js +724 -153
  191. package/src/git-fast-cache.js +220 -0
  192. package/src/git-wal-sync.js +156 -0
  193. package/src/hash-anchored-edit.js +82 -0
  194. package/src/hermes-platform-protocol.js +475 -0
  195. package/src/hermes-sync-plane.js +241 -0
  196. package/src/index.js +30 -1
  197. package/src/iso42001-compliance-guard.js +97 -0
  198. package/src/latency-budget.js +244 -0
  199. package/src/mcp-writeguard.js +316 -0
  200. package/src/miminions-adapter.js +106 -0
  201. package/src/pipeline-compass.js +104 -0
  202. package/src/ppl-alert-pipeline.js +284 -0
  203. package/src/rendezvous-router.js +90 -0
  204. package/src/security-questionnaire.js +195 -0
@@ -133,6 +133,16 @@ function normalizeId(value) {
133
133
  }
134
134
 
135
135
  function normalizeScope(input = {}) {
136
+ // Supermemory-style containerTag is an opaque namespace. If present and
137
+ // parseable as our four-field encoding, prefer it; otherwise fall through
138
+ // to field aliases. (Steal: https://supermemory.ai/docs/concepts/container-tags)
139
+ const fromTag = decodeContainerTag(
140
+ input.containerTag || input.container_tag || input.metadata?.containerTag
141
+ );
142
+ if (fromTag && missingScopeFields(fromTag).length === 0) {
143
+ return fromTag;
144
+ }
145
+
136
146
  const scope = {};
137
147
  for (const field of REQUIRED_SCOPE_FIELDS) {
138
148
  let resolved = null;
@@ -460,15 +470,304 @@ function buildMemoriStyleBenchmarkRecords() {
460
470
  ];
461
471
  }
462
472
 
473
+
474
+ // ---------------------------------------------------------------------------
475
+ // Supermemory process steal (NOT a product clone)
476
+ // Docs: Memory vs RAG, container tags, profiles, dreaming modes.
477
+ // ---------------------------------------------------------------------------
478
+
479
+ const CONTAINER_TAG_PATTERN = /^[a-zA-Z0-9_:-]+$/;
480
+ const DREAMING_MODES = Object.freeze(['dynamic', 'instant']);
481
+
482
+ /**
483
+ * Encode four-field ThumbGate scope as a Supermemory-valid containerTag.
484
+ * Format: entity:<id>:project:<id>:process:<id>:session:<id>
485
+ */
486
+ function encodeContainerTag(scopeInput = {}) {
487
+ const scope = normalizeScope(scopeInput);
488
+ const missing = missingScopeFields(scope);
489
+ if (missing.length > 0) {
490
+ return {
491
+ ok: false,
492
+ containerTag: null,
493
+ missingFields: missing,
494
+ reason: `incomplete scope: ${missing.join(', ')}`,
495
+ };
496
+ }
497
+ const values = [scope.entityId, scope.projectId, scope.processId, scope.sessionId];
498
+ if (values.some((value) => String(value).includes(':'))) {
499
+ return {
500
+ ok: false,
501
+ containerTag: null,
502
+ missingFields: [],
503
+ reason: 'scope field values must not contain ":" (reserved for containerTag encoding)',
504
+ };
505
+ }
506
+ const tag = [
507
+ `entity:${scope.entityId}`,
508
+ `project:${scope.projectId}`,
509
+ `process:${scope.processId}`,
510
+ `session:${scope.sessionId}`,
511
+ ].join(':');
512
+ if (!CONTAINER_TAG_PATTERN.test(tag) || tag.length > 100) {
513
+ return {
514
+ ok: false,
515
+ containerTag: null,
516
+ missingFields: [],
517
+ reason: 'encoded tag fails supermemory charset/length rules',
518
+ };
519
+ }
520
+ return { ok: true, containerTag: tag, missingFields: [], scope };
521
+ }
522
+
523
+ /**
524
+ * Decode containerTag produced by encodeContainerTag (or null if opaque).
525
+ */
526
+ function decodeContainerTag(tag) {
527
+ const text = normalizeId(tag);
528
+ if (!text || !CONTAINER_TAG_PATTERN.test(text) || text.length > 100) return null;
529
+ const parts = text.split(':');
530
+ if (parts.length !== 8) return null;
531
+ const scope = {};
532
+ for (let i = 0; i < 8; i += 2) {
533
+ const key = parts[i];
534
+ const value = parts[i + 1];
535
+ if (key === 'entity') scope.entityId = value;
536
+ else if (key === 'project') scope.projectId = value;
537
+ else if (key === 'process') scope.processId = value;
538
+ else if (key === 'session') scope.sessionId = value;
539
+ else return null;
540
+ }
541
+ if (missingScopeFields(scope).length > 0) return null;
542
+ return scope;
543
+ }
544
+
545
+ /**
546
+ * Route a question to RAG (stateless knowledge) vs MEMORY (scoped temporal).
547
+ * Steal: https://supermemory.ai/docs/concepts/memory-vs-rag
548
+ * RAG answers "what do I know?"; Memory answers "what do I remember about you?"
549
+ */
550
+ function routeMemoryVsRag(query = '', options = {}) {
551
+ const text = String(query || '').trim();
552
+ const scope = normalizeScope(options.scope || options);
553
+ const missing = missingScopeFields(scope);
554
+ const lower = text.toLowerCase();
555
+
556
+ const memoryCues = [
557
+ 'i prefer', 'my preference', 'last time', 'we decided', 'remember that',
558
+ 'what did we', 'what do i usually', 'my usual', 'for this user', 'for this project',
559
+ 'previous mistake', 'lesson about', 'prevention rule', 'thumbs down', 'what went wrong',
560
+ 'session', 'our agent', 'ceo said', 'standing order',
561
+ ];
562
+ const ragCues = [
563
+ 'how does', 'where is', 'which file', 'architecture', 'implements',
564
+ 'api endpoint', 'readme', 'documentation', 'source of', 'call graph',
565
+ 'pretooluse', 'gate-check', 'package.json', 'what connects',
566
+ ];
567
+
568
+ let memoryHits = memoryCues.filter((c) => lower.includes(c)).length;
569
+ let ragHits = ragCues.filter((c) => lower.includes(c)).length;
570
+
571
+ // Explicit override — reject typos instead of falling through to heuristics.
572
+ if (options.forceRail != null && options.forceRail !== '') {
573
+ if (options.forceRail !== 'memory' && options.forceRail !== 'rag' && options.forceRail !== 'hybrid') {
574
+ return {
575
+ ok: false,
576
+ rail: null,
577
+ query: text,
578
+ scope,
579
+ missingFields: missing,
580
+ containerTag: null,
581
+ rails: {},
582
+ recommended: [],
583
+ reason: 'invalid forceRail (expected rag|memory|hybrid)',
584
+ error: 'invalid_force_rail',
585
+ memoryHits,
586
+ ragHits,
587
+ };
588
+ }
589
+ return finalizeRoute(options.forceRail, text, scope, missing, {
590
+ memoryHits,
591
+ ragHits,
592
+ reason: `forced:${options.forceRail}`,
593
+ });
594
+ }
595
+
596
+ let rail = 'hybrid';
597
+ let reason = 'default hybrid: prefer graphify/docs for code, lesson-store only with complete scope';
598
+ if (memoryHits > ragHits && memoryHits > 0) {
599
+ rail = 'memory';
600
+ reason = 'memory cues dominate (preferences, prior decisions, lessons)';
601
+ } else if (ragHits > memoryHits && ragHits > 0) {
602
+ rail = 'rag';
603
+ reason = 'rag cues dominate (code/docs/architecture)';
604
+ } else if (!text) {
605
+ rail = 'rag';
606
+ reason = 'empty query defaults to rag/code-map';
607
+ }
608
+
609
+ return finalizeRoute(rail, text, scope, missing, { memoryHits, ragHits, reason });
610
+ }
611
+
612
+ function finalizeRoute(rail, text, scope, missing, meta) {
613
+ const container = encodeContainerTag(scope);
614
+ // Memory rail requires a complete, encodable scope (fail closed on bad tags).
615
+ const ok = rail !== 'memory'
616
+ ? true
617
+ : (missing.length === 0 && container.ok);
618
+ return {
619
+ ok,
620
+ rail,
621
+ query: text,
622
+ scope,
623
+ missingFields: missing,
624
+ containerTag: container.ok ? container.containerTag : null,
625
+ rails: {
626
+ rag: {
627
+ tools: [
628
+ 'graphify query/path/explain (when graphify-out/graph.json exists)',
629
+ 'grepai / codebase search',
630
+ 'docs/ and public/ HTML',
631
+ ],
632
+ note: 'Stateless knowledge — same answer for every user',
633
+ },
634
+ memory: {
635
+ tools: [
636
+ 'lesson-retrieval.js (requires complete four-field scope)',
637
+ 'memory-scope-readiness.selectRecordsForScope',
638
+ 'temporal-decay-weighting.js',
639
+ ],
640
+ note: 'Stateful scoped memory — fails closed without entity/project/process/session',
641
+ },
642
+ },
643
+ recommended: rail === 'rag'
644
+ ? ['graphify', 'docs']
645
+ : rail === 'memory'
646
+ ? ['lesson-retrieval', 'profile']
647
+ : ['graphify', 'lesson-retrieval+scope', 'profile'],
648
+ reason: meta.reason,
649
+ memoryHits: meta.memoryHits,
650
+ ragHits: meta.ragHits,
651
+ error: ok
652
+ ? null
653
+ : (!container.ok
654
+ ? (container.reason || 'memory rail requires encodable containerTag')
655
+ : `memory rail requires complete scope; missing: ${missing.join(', ')}`),
656
+ };
657
+ }
658
+
659
+ /**
660
+ * Dreaming mode for feedback→memory promotion (Supermemory process steal).
661
+ * dynamic = batch related feedback into coherent units (default, cheaper/higher quality)
662
+ * instant = promote this signal alone immediately (demo / crisis)
663
+ */
664
+ function resolveDreamingMode(input = {}) {
665
+ const raw = String(input.dreaming || input.mode || 'dynamic').toLowerCase().trim();
666
+ const mode = DREAMING_MODES.includes(raw) ? raw : 'dynamic';
667
+ return {
668
+ mode,
669
+ promoteImmediately: mode === 'instant',
670
+ batchRelated: mode === 'dynamic',
671
+ reason: mode === 'instant'
672
+ ? 'instant dreaming: promote this document/feedback alone now'
673
+ : 'dynamic dreaming: group related feedback before promotion (default)',
674
+ };
675
+ }
676
+
677
+ /**
678
+ * Build a static+dynamic profile from lesson-like records for one container/scope.
679
+ * Steal: https://supermemory.ai/docs/concepts/user-profiles
680
+ * Profile rides along every turn; search stays for query-specific recall.
681
+ */
682
+ function buildLessonProfile(records = [], scopeInput = {}, options = {}) {
683
+ const scope = normalizeScope(scopeInput);
684
+ const missing = missingScopeFields(scope);
685
+ if (missing.length > 0) {
686
+ return {
687
+ ok: false,
688
+ missingFields: missing,
689
+ profile: { static: [], dynamic: [] },
690
+ reason: `incomplete scope: ${missing.join(', ')}`,
691
+ };
692
+ }
693
+
694
+ const selected = selectRecordsForScope(records, scope, options);
695
+ const now = options.now ? new Date(options.now).getTime() : Date.now();
696
+ const dynamicWindowMs = Number.isFinite(options.dynamicWindowMs)
697
+ ? options.dynamicWindowMs
698
+ : 14 * 24 * 60 * 60 * 1000;
699
+
700
+ const staticFacts = [];
701
+ const dynamicFacts = [];
702
+
703
+ for (const record of selected.allowed) {
704
+ const content = String(
705
+ record.content || record.title || record.whatWorked || record.whatToChange || ''
706
+ ).trim();
707
+ if (!content) continue;
708
+
709
+ const ts = Date.parse(record.updatedAt || record.createdAt || record.timestamp || '');
710
+ const ageOk = Number.isFinite(ts) ? (now - ts) <= dynamicWindowMs : false;
711
+ const isPreference = /prefer|always|never|standing|ceo|mandate|policy/i.test(content)
712
+ || record.type === 'fact'
713
+ || record.visibility === 'shared'
714
+ || record.importance === 'high';
715
+
716
+ const entry = {
717
+ id: record.id || null,
718
+ content: content.slice(0, 280),
719
+ type: record.type || null,
720
+ };
721
+
722
+ if (isPreference && !ageOk) {
723
+ staticFacts.push(entry);
724
+ } else if (ageOk) {
725
+ dynamicFacts.push(entry);
726
+ } else if (isPreference) {
727
+ staticFacts.push(entry);
728
+ } else {
729
+ // Older non-preference lessons stay out of always-on profile
730
+ // (search/lesson-retrieval still covers them).
731
+ }
732
+ }
733
+
734
+ const encoded = encodeContainerTag(scope);
735
+ return {
736
+ ok: true,
737
+ missingFields: [],
738
+ containerTag: encoded.containerTag,
739
+ scope,
740
+ profile: {
741
+ static: staticFacts.slice(0, options.staticLimit || 12),
742
+ dynamic: dynamicFacts.slice(0, options.dynamicLimit || 12),
743
+ },
744
+ counts: {
745
+ allowed: selected.allowed.length,
746
+ blocked: selected.blocked.length,
747
+ static: Math.min(staticFacts.length, options.staticLimit || 12),
748
+ dynamic: Math.min(dynamicFacts.length, options.dynamicLimit || 12),
749
+ },
750
+ reason: 'profile synthesizes always-on facts; use search/lesson-retrieval for query-specific recall',
751
+ };
752
+ }
753
+
754
+
463
755
  module.exports = {
464
756
  MEMORY_OS_LAYERS,
465
757
  REQUIRED_SCOPE_FIELDS,
758
+ DREAMING_MODES,
759
+ CONTAINER_TAG_PATTERN,
760
+ buildLessonProfile,
466
761
  buildMemoriStyleBenchmarkRecords,
467
762
  buildMemoryOsLayerReport,
468
763
  buildMemoryScopeReadinessReport,
764
+ decodeContainerTag,
765
+ encodeContainerTag,
469
766
  isSharedMemory,
470
767
  memoryScopeKey,
471
768
  missingScopeFields,
472
769
  normalizeScope,
770
+ resolveDreamingMode,
771
+ routeMemoryVsRag,
473
772
  selectRecordsForScope,
474
773
  };
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * memory-vs-rag-route.js — Supermemory process steal (NOT a product clone).
6
+ *
7
+ * RAG answers "what do I know?" (graphify/docs/code).
8
+ * Memory answers "what do I remember about you?" (scoped lesson store).
9
+ *
10
+ * Usage:
11
+ * node scripts/memory-vs-rag-route.js --query "how does PreToolUse work?"
12
+ * node scripts/memory-vs-rag-route.js --query "what did we decide about checkout?" \
13
+ * --entity alice --project thumbgate --process coder --session s1 --json
14
+ * node scripts/memory-vs-rag-route.js --profile --entity ... --json < records.json
15
+ * node scripts/memory-vs-rag-route.js --dreaming instant --json
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const {
21
+ buildLessonProfile,
22
+ encodeContainerTag,
23
+ resolveDreamingMode,
24
+ routeMemoryVsRag,
25
+ } = require('./memory-scope-readiness');
26
+
27
+ function takeValue(flag, next) {
28
+ if (next == null || next.startsWith('-')) {
29
+ throw new Error(`Incomplete argument: ${flag} requires a value`);
30
+ }
31
+ return next;
32
+ }
33
+
34
+ function parseArgs(argv) {
35
+ const out = {
36
+ query: '',
37
+ json: false,
38
+ profile: false,
39
+ dreaming: null,
40
+ forceRail: null,
41
+ help: false,
42
+ entityId: null,
43
+ projectId: null,
44
+ processId: null,
45
+ sessionId: null,
46
+ containerTag: null,
47
+ };
48
+ for (let i = 0; i < argv.length; i += 1) {
49
+ const arg = argv[i];
50
+ const next = argv[i + 1];
51
+ if (arg === '--help' || arg === '-h') out.help = true;
52
+ else if (arg === '--json') out.json = true;
53
+ else if (arg === '--profile') out.profile = true;
54
+ else if (arg === '--query') { out.query = takeValue(arg, next); i += 1; }
55
+ else if (arg === '--dreaming') { out.dreaming = takeValue(arg, next); i += 1; }
56
+ else if (arg === '--force-rail') { out.forceRail = takeValue(arg, next); i += 1; }
57
+ else if (arg === '--entity') { out.entityId = takeValue(arg, next); i += 1; }
58
+ else if (arg === '--project') { out.projectId = takeValue(arg, next); i += 1; }
59
+ else if (arg === '--process') { out.processId = takeValue(arg, next); i += 1; }
60
+ else if (arg === '--session') { out.sessionId = takeValue(arg, next); i += 1; }
61
+ else if (arg === '--container-tag') { out.containerTag = takeValue(arg, next); i += 1; }
62
+ else if (!arg.startsWith('-') && !out.query) out.query = arg;
63
+ else throw new Error(`Unknown or incomplete argument: ${arg}`);
64
+ }
65
+ return out;
66
+ }
67
+
68
+ const MAX_STDIN_BYTES = 2 * 1024 * 1024;
69
+
70
+ function readStdinJson() {
71
+ if (process.stdin.isTTY) return null;
72
+ const raw = fs.readFileSync(0, 'utf8');
73
+ if (Buffer.byteLength(raw, 'utf8') > MAX_STDIN_BYTES) {
74
+ throw new Error(`stdin exceeds ${MAX_STDIN_BYTES} bytes`);
75
+ }
76
+ const trimmed = raw.trim();
77
+ if (!trimmed) return null;
78
+ return JSON.parse(trimmed);
79
+ }
80
+
81
+ function printHelp() {
82
+ console.log(`Usage: node scripts/memory-vs-rag-route.js [options]
83
+
84
+ Options:
85
+ --query <text> Question to route (rag | memory | hybrid)
86
+ --entity/--project/--process/--session
87
+ --container-tag <tag> Four-field encoded tag from encodeContainerTag
88
+ --force-rail rag|memory|hybrid
89
+ --dreaming dynamic|instant
90
+ --profile Build static+dynamic lesson profile (stdin JSON array)
91
+ --json
92
+ --help
93
+ `);
94
+ }
95
+
96
+ function main() {
97
+ const args = parseArgs(process.argv.slice(2));
98
+ if (args.help) {
99
+ printHelp();
100
+ return;
101
+ }
102
+
103
+ const scope = {
104
+ entityId: args.entityId,
105
+ projectId: args.projectId,
106
+ processId: args.processId,
107
+ sessionId: args.sessionId,
108
+ containerTag: args.containerTag,
109
+ };
110
+
111
+ if (args.dreaming && !args.query && !args.profile) {
112
+ const result = resolveDreamingMode({ dreaming: args.dreaming });
113
+ if (args.json) console.log(JSON.stringify(result, null, 2));
114
+ else console.log(`dreaming=${result.mode} immediate=${result.promoteImmediately}`);
115
+ process.exitCode = 0;
116
+ return;
117
+ }
118
+
119
+ if (args.profile) {
120
+ const stdin = readStdinJson();
121
+ const records = Array.isArray(stdin) ? stdin : (stdin?.records || []);
122
+ const result = buildLessonProfile(records, scope);
123
+ if (args.json) console.log(JSON.stringify(result, null, 2));
124
+ else {
125
+ console.log(result.ok ? 'profile: OK' : `profile: FAIL ${result.reason}`);
126
+ console.log(`static=${result.profile.static.length} dynamic=${result.profile.dynamic.length}`);
127
+ }
128
+ process.exitCode = result.ok ? 0 : 1;
129
+ return;
130
+ }
131
+
132
+ const route = routeMemoryVsRag(args.query, { ...scope, forceRail: args.forceRail });
133
+ const dreaming = resolveDreamingMode({ dreaming: args.dreaming || 'dynamic' });
134
+ const encoded = encodeContainerTag(scope);
135
+ const result = { ...route, dreaming, encodedContainerTag: encoded };
136
+
137
+ if (args.json) {
138
+ console.log(JSON.stringify(result, null, 2));
139
+ } else {
140
+ console.log(`rail=${result.rail} ok=${result.ok}`);
141
+ console.log(`reason=${result.reason}`);
142
+ if (result.error) console.log(`error=${result.error}`);
143
+ console.log(`recommended=${result.recommended.join(', ')}`);
144
+ if (result.containerTag) console.log(`containerTag=${result.containerTag}`);
145
+ }
146
+ process.exitCode = result.ok ? 0 : 2;
147
+ }
148
+
149
+ if (path.resolve(process.argv[1] || '') === path.resolve(__filename)) {
150
+ try {
151
+ main();
152
+ } catch (err) {
153
+ console.error(String(err && err.message ? err.message : err));
154
+ process.exitCode = 1;
155
+ }
156
+ }
157
+
158
+ module.exports = {
159
+ parseArgs,
160
+ main,
161
+ };