@remnic/core 9.3.603 → 9.3.604

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.
@@ -313,13 +313,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
313
313
  peerProfiles: boolean;
314
314
  }>;
315
315
  }, "strip", z.ZodTypeAny, {
316
+ schemaVersion: string;
316
317
  includes: {
317
318
  procedural: boolean;
318
319
  taxonomy: boolean;
319
320
  identityAnchors: boolean;
320
321
  peerProfiles: boolean;
321
322
  };
322
- schemaVersion: string;
323
323
  id: string;
324
324
  description: string;
325
325
  version: string;
@@ -334,13 +334,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
334
334
  directAnswerEnabled: boolean;
335
335
  };
336
336
  }, {
337
+ schemaVersion: string;
337
338
  includes: {
338
339
  procedural: boolean;
339
340
  taxonomy: boolean;
340
341
  identityAnchors: boolean;
341
342
  peerProfiles: boolean;
342
343
  };
343
- schemaVersion: string;
344
344
  id: string;
345
345
  description: string;
346
346
  version: string;
@@ -464,13 +464,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
464
464
  peerProfiles: boolean;
465
465
  }>;
466
466
  }, "strip", z.ZodTypeAny, {
467
+ schemaVersion: string;
467
468
  includes: {
468
469
  procedural: boolean;
469
470
  taxonomy: boolean;
470
471
  identityAnchors: boolean;
471
472
  peerProfiles: boolean;
472
473
  };
473
- schemaVersion: string;
474
474
  id: string;
475
475
  description: string;
476
476
  version: string;
@@ -485,13 +485,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
485
485
  directAnswerEnabled: boolean;
486
486
  };
487
487
  }, {
488
+ schemaVersion: string;
488
489
  includes: {
489
490
  procedural: boolean;
490
491
  taxonomy: boolean;
491
492
  identityAnchors: boolean;
492
493
  peerProfiles: boolean;
493
494
  };
494
- schemaVersion: string;
495
495
  id: string;
496
496
  description: string;
497
497
  version: string;
@@ -518,13 +518,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
518
518
  pluginVersion: string;
519
519
  includesTranscripts: boolean;
520
520
  capsule: {
521
+ schemaVersion: string;
521
522
  includes: {
522
523
  procedural: boolean;
523
524
  taxonomy: boolean;
524
525
  identityAnchors: boolean;
525
526
  peerProfiles: boolean;
526
527
  };
527
- schemaVersion: string;
528
528
  id: string;
529
529
  description: string;
530
530
  version: string;
@@ -551,13 +551,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
551
551
  pluginVersion: string;
552
552
  includesTranscripts: boolean;
553
553
  capsule: {
554
+ schemaVersion: string;
554
555
  includes: {
555
556
  procedural: boolean;
556
557
  taxonomy: boolean;
557
558
  identityAnchors: boolean;
558
559
  peerProfiles: boolean;
559
560
  };
560
- schemaVersion: string;
561
561
  id: string;
562
562
  description: string;
563
563
  version: string;
@@ -683,13 +683,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
683
683
  peerProfiles: boolean;
684
684
  }>;
685
685
  }, "strip", z.ZodTypeAny, {
686
+ schemaVersion: string;
686
687
  includes: {
687
688
  procedural: boolean;
688
689
  taxonomy: boolean;
689
690
  identityAnchors: boolean;
690
691
  peerProfiles: boolean;
691
692
  };
692
- schemaVersion: string;
693
693
  id: string;
694
694
  description: string;
695
695
  version: string;
@@ -704,13 +704,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
704
704
  directAnswerEnabled: boolean;
705
705
  };
706
706
  }, {
707
+ schemaVersion: string;
707
708
  includes: {
708
709
  procedural: boolean;
709
710
  taxonomy: boolean;
710
711
  identityAnchors: boolean;
711
712
  peerProfiles: boolean;
712
713
  };
713
- schemaVersion: string;
714
714
  id: string;
715
715
  description: string;
716
716
  version: string;
@@ -737,13 +737,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
737
737
  pluginVersion: string;
738
738
  includesTranscripts: boolean;
739
739
  capsule: {
740
+ schemaVersion: string;
740
741
  includes: {
741
742
  procedural: boolean;
742
743
  taxonomy: boolean;
743
744
  identityAnchors: boolean;
744
745
  peerProfiles: boolean;
745
746
  };
746
- schemaVersion: string;
747
747
  id: string;
748
748
  description: string;
749
749
  version: string;
@@ -770,13 +770,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
770
770
  pluginVersion: string;
771
771
  includesTranscripts: boolean;
772
772
  capsule: {
773
+ schemaVersion: string;
773
774
  includes: {
774
775
  procedural: boolean;
775
776
  taxonomy: boolean;
776
777
  identityAnchors: boolean;
777
778
  peerProfiles: boolean;
778
779
  };
779
- schemaVersion: string;
780
780
  id: string;
781
781
  description: string;
782
782
  version: string;
@@ -815,13 +815,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
815
815
  pluginVersion: string;
816
816
  includesTranscripts: boolean;
817
817
  capsule: {
818
+ schemaVersion: string;
818
819
  includes: {
819
820
  procedural: boolean;
820
821
  taxonomy: boolean;
821
822
  identityAnchors: boolean;
822
823
  peerProfiles: boolean;
823
824
  };
824
- schemaVersion: string;
825
825
  id: string;
826
826
  description: string;
827
827
  version: string;
@@ -854,13 +854,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
854
854
  pluginVersion: string;
855
855
  includesTranscripts: boolean;
856
856
  capsule: {
857
+ schemaVersion: string;
857
858
  includes: {
858
859
  procedural: boolean;
859
860
  taxonomy: boolean;
860
861
  identityAnchors: boolean;
861
862
  peerProfiles: boolean;
862
863
  };
863
- schemaVersion: string;
864
864
  id: string;
865
865
  description: string;
866
866
  version: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.603",
3
+ "version": "9.3.604",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -953,7 +953,8 @@ function applyQueryAwareCandidateFilter(
953
953
  candidates: QmdSearchResult[],
954
954
  candidatePaths: Set<string> | null,
955
955
  ): QmdSearchResult[] {
956
- if (!candidatePaths || candidatePaths.size === 0) return candidates;
956
+ if (!candidatePaths) return candidates;
957
+ if (candidatePaths.size === 0) return [];
957
958
  const filtered = candidates.filter((candidate) =>
958
959
  candidatePaths.has(candidate.path),
959
960
  );
@@ -5316,7 +5317,7 @@ export class Orchestrator {
5316
5317
  paths: Set<string> | null,
5317
5318
  recallNamespaces: string[],
5318
5319
  ): Set<string> | null {
5319
- if (!paths || paths.size === 0) return null;
5320
+ if (!paths) return null;
5320
5321
  const scoped = new Set<string>();
5321
5322
  for (const memoryPath of paths) {
5322
5323
  if (!memoryPath || isArtifactMemoryPath(memoryPath)) continue;
@@ -5328,7 +5329,7 @@ export class Orchestrator {
5328
5329
  }
5329
5330
  scoped.add(memoryPath);
5330
5331
  }
5331
- return scoped.size > 0 ? scoped : null;
5332
+ return scoped;
5332
5333
  }
5333
5334
 
5334
5335
  private async buildQueryAwarePrefilter(
@@ -5376,7 +5377,14 @@ export class Orchestrator {
5376
5377
  let combination: QueryAwarePrefilter["combination"] = "none";
5377
5378
  let filteredToFullSearch = false;
5378
5379
 
5379
- if (temporalCandidates && tagCandidates) {
5380
+ if (
5381
+ tagSignals.matchedTags.length > 0 &&
5382
+ tagCandidates !== null &&
5383
+ tagCandidates.size === 0
5384
+ ) {
5385
+ candidatePaths = tagCandidates;
5386
+ combination = "tag";
5387
+ } else if (temporalCandidates !== null && tagCandidates !== null) {
5380
5388
  const intersection = new Set(
5381
5389
  Array.from(temporalCandidates).filter((memoryPath) =>
5382
5390
  tagCandidates.has(memoryPath),
@@ -5389,10 +5397,10 @@ export class Orchestrator {
5389
5397
  candidatePaths = new Set([...temporalCandidates, ...tagCandidates]);
5390
5398
  combination = "union";
5391
5399
  }
5392
- } else if (temporalCandidates) {
5400
+ } else if (temporalCandidates !== null) {
5393
5401
  candidatePaths = temporalCandidates;
5394
5402
  combination = "temporal";
5395
- } else if (tagCandidates) {
5403
+ } else if (tagCandidates !== null) {
5396
5404
  candidatePaths = tagCandidates;
5397
5405
  combination = "tag";
5398
5406
  }
@@ -5565,6 +5573,10 @@ export class Orchestrator {
5565
5573
  })),
5566
5574
  });
5567
5575
  };
5576
+ if (queryAwarePrefilter.candidatePaths?.size === 0) {
5577
+ await emitDebugSnapshot([], fetchLimit);
5578
+ return [];
5579
+ }
5568
5580
 
5569
5581
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
5570
5582
  throwIfRecallAborted(options.abortSignal);
@@ -7778,6 +7790,15 @@ export class Orchestrator {
7778
7790
  );
7779
7791
  const staleQmdFallback =
7780
7792
  cachedQmd?.source === "stale" ? cachedQmd : null;
7793
+ const queryAwarePrefilter = await queryAwarePrefilterPromise;
7794
+ const queryAwarePrefilterIsEmpty =
7795
+ queryAwarePrefilter.candidatePaths?.size === 0;
7796
+ const emptyQueryAwareQmdResult: Exclude<QmdPhaseResult, null> = {
7797
+ memoryResultsLists: [[]],
7798
+ globalResults: [],
7799
+ preAugmentTopScore: 0,
7800
+ maxSpecializedScore: 0,
7801
+ };
7781
7802
  if (cachedQmd?.source === "fresh") {
7782
7803
  recordRecallSectionMetric({
7783
7804
  section: "qmd",
@@ -7788,6 +7809,9 @@ export class Orchestrator {
7788
7809
  success: true,
7789
7810
  timing: `${Math.max(0, Math.round(cachedQmd.ageMs))}ms-cache`,
7790
7811
  });
7812
+ if (queryAwarePrefilterIsEmpty) {
7813
+ return emptyQueryAwareQmdResult;
7814
+ }
7791
7815
  return cachedQmd.value;
7792
7816
  }
7793
7817
 
@@ -7808,6 +7832,9 @@ export class Orchestrator {
7808
7832
  success: true,
7809
7833
  timing: `stale-cache(reprobe-cooldown:${Math.max(0, Math.round(staleQmdFallback.ageMs))}ms)`,
7810
7834
  });
7835
+ if (queryAwarePrefilterIsEmpty) {
7836
+ return emptyQueryAwareQmdResult;
7837
+ }
7811
7838
  return staleQmdFallback.value;
7812
7839
  }
7813
7840
  recordRecallSectionMetric({
@@ -7834,6 +7861,9 @@ export class Orchestrator {
7834
7861
  success: true,
7835
7862
  timing: `stale-cache(reprobe-failed:${Math.max(0, Math.round(staleQmdFallback.ageMs))}ms)`,
7836
7863
  });
7864
+ if (queryAwarePrefilterIsEmpty) {
7865
+ return emptyQueryAwareQmdResult;
7866
+ }
7837
7867
  return staleQmdFallback.value;
7838
7868
  }
7839
7869
  recordRecallSectionMetric({
@@ -7853,11 +7883,11 @@ export class Orchestrator {
7853
7883
  log.info(`QMD re-probe succeeded: ${this.qmd.debugStatus()}`);
7854
7884
  }
7855
7885
 
7856
- const queryAwarePrefilter = await queryAwarePrefilterPromise;
7857
7886
  const maxPerAgent = this.config.parallelMaxResultsPerAgent;
7858
7887
  const specializedAgentPromise: Promise<
7859
7888
  [ParallelSearchResult[], ParallelSearchResult[]]
7860
7889
  > | null =
7890
+ !queryAwarePrefilterIsEmpty &&
7861
7891
  this.config.parallelRetrievalEnabled && maxPerAgent > 0
7862
7892
  ? Promise.all([
7863
7893
  shouldRunAgent("direct", retrievalQuery, 0)
@@ -7981,6 +8011,9 @@ export class Orchestrator {
7981
8011
  success: true,
7982
8012
  timing: `stale-cache(${err instanceof Error ? err.message : String(err)})`,
7983
8013
  });
8014
+ if (queryAwarePrefilterIsEmpty) {
8015
+ return emptyQueryAwareQmdResult;
8016
+ }
7984
8017
  return staleQmdFallback.value;
7985
8018
  }
7986
8019
  throw err;
@@ -9462,6 +9495,7 @@ export class Orchestrator {
9462
9495
  // When the gate rejects, all recall pathways are skipped to prevent
9463
9496
  // low-relevance results from polluting context.
9464
9497
  const queryAwarePrefilter = await queryAwarePrefilterPromise;
9498
+ if (queryAwarePrefilter.candidatePaths?.size !== 0) {
9465
9499
  const embeddingResults = await this.searchEmbeddingFallback(
9466
9500
  retrievalQuery,
9467
9501
  embeddingFetchLimit,
@@ -9566,6 +9600,7 @@ export class Orchestrator {
9566
9600
  impressionRecorded = true;
9567
9601
  }
9568
9602
  }
9603
+ }
9569
9604
  }
9570
9605
 
9571
9606
  if (globalResults.length > 0) {
@@ -9607,6 +9642,7 @@ export class Orchestrator {
9607
9642
  } else if (recallResultLimit > 0 && !this.qmd.isAvailable()) {
9608
9643
  // Fallback: embeddings first, then recency-only.
9609
9644
  const queryAwarePrefilter = await queryAwarePrefilterPromise;
9645
+ if (queryAwarePrefilter.candidatePaths?.size !== 0) {
9610
9646
  const embeddingResults = await this.searchEmbeddingFallback(
9611
9647
  retrievalQuery,
9612
9648
  embeddingFetchLimit,
@@ -9922,6 +9958,7 @@ export class Orchestrator {
9922
9958
  }
9923
9959
  }
9924
9960
  }
9961
+ }
9925
9962
 
9926
9963
  if (isDisagreementPrompt(prompt)) {
9927
9964
  this.appendRecallSection(
@@ -15553,6 +15590,7 @@ export class Orchestrator {
15553
15590
  throwIfRecallAborted(abortSignal);
15554
15591
  const cappedLimit = Math.max(0, limit);
15555
15592
  if (cappedLimit === 0) return [];
15593
+ if (queryAwarePrefilter?.candidatePaths?.size === 0) return [];
15556
15594
 
15557
15595
  const scopedSeedResults = queryAwarePrefilter?.candidatePaths?.size
15558
15596
  ? await this.searchScopedMemoryCandidates(
@@ -15639,6 +15677,11 @@ export class Orchestrator {
15639
15677
  /** Issue #681 — when true, bypass graphTraversalConfidenceFloor. */
15640
15678
  includeLowConfidence?: boolean;
15641
15679
  }): Promise<QmdSearchResult[]> {
15680
+ if (options.queryAwarePrefilter?.candidatePaths?.size === 0) {
15681
+ if (options.xrayPoolSizeSink) options.xrayPoolSizeSink.size = 0;
15682
+ return [];
15683
+ }
15684
+
15642
15685
  const coldQmdEnabled = this.config.qmdColdTierEnabled === true;
15643
15686
  const coldCollection =
15644
15687
  this.config.qmdColdCollection ?? "openclaw-engram-cold";
@@ -15953,7 +15996,7 @@ export class Orchestrator {
15953
15996
  // Intersect with result paths first so out-of-scope paths don't exhaust the budget
15954
15997
  const scoped = new Set(Array.from(s).filter((p) => resultPaths.has(p)));
15955
15998
  if (maxCandidates === 0 || scoped.size <= maxCandidates)
15956
- return scoped.size > 0 ? scoped : null;
15999
+ return scoped;
15957
16000
  return new Set(Array.from(scoped).slice(0, maxCandidates));
15958
16001
  };
15959
16002
  if (temporalFromDate !== null) {
@@ -6,7 +6,12 @@ import { join } from "node:path";
6
6
  import test from "node:test";
7
7
  import { setTimeout as delay } from "node:timers/promises";
8
8
 
9
- import { queryByDateRangeAsync, queryByTagsAsync } from "./temporal-index.js";
9
+ import {
10
+ indexMemory,
11
+ queryByDateRangeAsync,
12
+ queryByTagsAsync,
13
+ resolvePromptTagPrefilterAsync,
14
+ } from "./temporal-index.js";
10
15
 
11
16
  async function runIndexWorker(moduleUrl: string, memoryDir: string, workerId: number, count: number): Promise<void> {
12
17
  const workerSource = `
@@ -189,3 +194,27 @@ test("temporal index writers fail open on symlink lock blockers", async () => {
189
194
  assert.equal(dateMatches, null);
190
195
  assert.deepEqual(tagMatches, new Set(["/tmp/remnic-temporal-worker-101-memory-0.md"]));
191
196
  });
197
+
198
+ test("tag queries distinguish missing index from valid no-match results", async () => {
199
+ const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-tag-miss-"));
200
+
201
+ const unavailableMatches = await queryByTagsAsync(memoryDir, ["beta"]);
202
+ assert.equal(unavailableMatches, null);
203
+
204
+ indexMemory(memoryDir, "/tmp/remnic-temporal-alpha-memory.md", "2026-03-11T12:00:00.000Z", ["alpha"]);
205
+
206
+ const matchingPaths = await queryByTagsAsync(memoryDir, ["alpha"]);
207
+ assert.deepEqual(matchingPaths, new Set(["/tmp/remnic-temporal-alpha-memory.md"]));
208
+
209
+ const noMatchPaths = await queryByTagsAsync(memoryDir, ["beta"]);
210
+ assert.deepEqual(noMatchPaths, new Set());
211
+
212
+ const promptPrefilter = await resolvePromptTagPrefilterAsync(memoryDir, "find #beta notes");
213
+ assert.deepEqual(promptPrefilter.matchedTags, ["beta"]);
214
+ assert.deepEqual(promptPrefilter.paths, new Set());
215
+
216
+ const noFilterPrefilter = await resolvePromptTagPrefilterAsync(memoryDir, "find project notes");
217
+ assert.deepEqual(noFilterPrefilter.matchedTags, []);
218
+ assert.deepEqual(noFilterPrefilter.expandedTags, []);
219
+ assert.equal(noFilterPrefilter.paths, null);
220
+ });
@@ -732,7 +732,7 @@ export async function queryByTagsAsync(memoryDir: string, tags: string[]): Promi
732
732
  try {
733
733
  gIndex = normalizeTagIndex(JSON.parse(raw) as TagIndex);
734
734
  } catch {
735
- gIndex = { version: TAG_INDEX_VERSION, tags: {}, aliases: {} };
735
+ return null;
736
736
  }
737
737
 
738
738
  return queryByTagsFromIndex(gIndex, tags);
@@ -741,7 +741,7 @@ export async function queryByTagsAsync(memoryDir: string, tags: string[]): Promi
741
741
  }
742
742
  }
743
743
 
744
- function queryByTagsFromIndex(index: TagIndex, tags: string[]): Set<string> | null {
744
+ function queryByTagsFromIndex(index: TagIndex, tags: string[]): Set<string> {
745
745
  const expandedTags = expandCanonicalTags(index, tags);
746
746
  const results = new Set<string>();
747
747
  for (const canonical of expandedTags) {
@@ -751,7 +751,7 @@ function queryByTagsFromIndex(index: TagIndex, tags: string[]): Set<string> | nu
751
751
  results.add(pathValue);
752
752
  }
753
753
  }
754
- return results.size > 0 ? results : null;
754
+ return results;
755
755
  }
756
756
 
757
757
  /**
@@ -797,6 +797,13 @@ export async function resolvePromptTagPrefilterAsync(
797
797
  matched.add(canonical);
798
798
  }
799
799
  }
800
+ if (matched.size === 0) {
801
+ return {
802
+ matchedTags: [],
803
+ expandedTags: [],
804
+ paths: null,
805
+ };
806
+ }
800
807
 
801
808
  const expandedTags = expandCanonicalTags(tagIndex, Array.from(matched));
802
809
  const paths = queryByTagsFromIndex(tagIndex, expandedTags);
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/temporal-index.ts"],"sourcesContent":["/**\n * Temporal and Tag Indexes (v8.1 — SwiftMem-inspired)\n *\n * Maintains two fast on-disk lookup structures in `state/`:\n * index_time.json — maps YYYY-MM-DD date buckets to memory file paths\n * index_tags.json — maps tag strings to memory file paths\n *\n * Used as an optional prefilter in the retrieval pipeline:\n * given a time range or a set of tags, narrow the candidate set\n * before the QMD hybrid search so we can pass a smaller pool to scoring.\n *\n * Design constraints:\n * - Must be fail-open (any error returns empty / unfiltered)\n * - Reads/writes are batched per extraction run\n * - Both indexes are plain JSON; no external dependencies\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport interface TemporalIndex {\n /** version bumped when schema changes */\n version: number;\n /** Last full rebuild timestamp (ISO string) */\n lastRebuildAt?: string;\n /** Map from YYYY-MM-DD → array of memory paths */\n dates: Record<string, string[]>;\n}\n\nexport interface TagIndex {\n version: number;\n lastRebuildAt?: string;\n /** Map from canonical tag string → node metadata */\n tags: Record<string, TagNode | string[]>;\n /** Map from alias string → canonical tags */\n aliases?: Record<string, string[]>;\n}\n\nexport interface TagNode {\n paths: string[];\n aliases?: string[];\n parents?: string[];\n}\n\nconst INDEX_VERSION = 1;\nconst TEMPORAL_INDEX_FILE = \"index_time.json\";\nconst TAG_INDEX_FILE = \"index_tags.json\";\nconst TAG_INDEX_VERSION = 2;\nconst INDEX_LOCK_STALE_MS = 60_000;\nconst INDEX_LOCK_POLL_MS = 10;\nconst INDEX_PROCESS_START_TOLERANCE_MS = 2_000;\nconst INDEX_LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(4));\nconst INDEX_PROCESS_STARTED_AT_MS = Date.now() - process.uptime() * 1000;\n\ninterface IndexLockOwner {\n pid: number;\n createdAt?: string;\n processStartedAtMs?: number;\n}\n\ntype IndexLockCleanupResult = \"removed\" | \"wait\" | \"blocked\";\n\nfunction stateDir(memoryDir: string): string {\n return path.join(memoryDir, \"state\");\n}\n\nfunction temporalIndexPath(memoryDir: string): string {\n return path.join(stateDir(memoryDir), TEMPORAL_INDEX_FILE);\n}\n\nfunction tagIndexPath(memoryDir: string): string {\n return path.join(stateDir(memoryDir), TAG_INDEX_FILE);\n}\n\nfunction ensureStateDir(memoryDir: string): void {\n const dir = stateDir(memoryDir);\n fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction readJsonSafe<T>(filePath: string, fallback: T): T {\n try {\n const raw = fs.readFileSync(filePath, \"utf8\");\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction writeJsonSafe(filePath: string, data: unknown): void {\n try {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), \"utf8\");\n } catch {\n // Fail silently — indexes are advisory only\n }\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(INDEX_LOCK_SLEEP, 0, 0, ms);\n}\n\nfunction uniqueTempPath(filePath: string): string {\n const dir = path.dirname(filePath);\n const base = path.basename(filePath);\n const nonce = crypto.randomBytes(6).toString(\"hex\");\n return path.join(dir, `.${base}.${process.pid}.${Date.now()}.${nonce}.tmp`);\n}\n\nfunction lockOwnerPath(lockDir: string): string {\n return path.join(lockDir, \"owner.json\");\n}\n\nfunction writeIndexLockOwner(lockDir: string): void {\n try {\n fs.writeFileSync(\n lockOwnerPath(lockDir),\n JSON.stringify({\n pid: process.pid,\n createdAt: new Date().toISOString(),\n processStartedAtMs: INDEX_PROCESS_STARTED_AT_MS,\n }),\n {\n encoding: \"utf8\",\n flag: \"wx\",\n }\n );\n } catch {\n // Fail silently — the directory lock is still the serialization primitive.\n }\n}\n\nfunction readIndexLockOwner(lockDir: string): IndexLockOwner | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockOwnerPath(lockDir), \"utf8\")) as { pid?: unknown };\n if (!(typeof parsed.pid === \"number\" && Number.isInteger(parsed.pid) && parsed.pid > 0)) return null;\n const owner: IndexLockOwner = { pid: parsed.pid };\n if (\n \"createdAt\" in parsed &&\n typeof (parsed as { createdAt?: unknown }).createdAt === \"string\" &&\n (parsed as { createdAt: string }).createdAt.length > 0\n ) {\n owner.createdAt = (parsed as { createdAt: string }).createdAt;\n }\n const processStartedAtMs = (parsed as { processStartedAtMs?: unknown }).processStartedAtMs;\n if (typeof processStartedAtMs === \"number\" && Number.isFinite(processStartedAtMs) && processStartedAtMs > 0) {\n owner.processStartedAtMs = processStartedAtMs;\n }\n return owner;\n } catch {\n return null;\n }\n}\n\nfunction processIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code;\n return code === \"EPERM\";\n }\n}\n\nfunction readProcessStartedAtMs(pid: number): number | null {\n try {\n const output = execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"lstart=\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 1_000,\n }).trim();\n if (!output) return null;\n const startedAtMs = Date.parse(output);\n return Number.isFinite(startedAtMs) ? startedAtMs : null;\n } catch {\n return null;\n }\n}\n\nfunction lockOwnerIsRunning(owner: IndexLockOwner): boolean {\n if (!processIsAlive(owner.pid)) return false;\n if (owner.processStartedAtMs === undefined) return true;\n const runningStartedAtMs = readProcessStartedAtMs(owner.pid);\n if (runningStartedAtMs === null) return true;\n return runningStartedAtMs <= owner.processStartedAtMs + INDEX_PROCESS_START_TOLERANCE_MS;\n}\n\nfunction lockIsFresh(lockInfo: fs.Stats, owner: IndexLockOwner | null): boolean {\n const ownerCreatedAtMs =\n typeof owner?.createdAt === \"string\" && owner.createdAt.length > 0 ? Date.parse(owner.createdAt) : Number.NaN;\n const referenceMs = Number.isFinite(ownerCreatedAtMs) ? ownerCreatedAtMs : lockInfo.mtimeMs;\n return Date.now() - referenceMs < INDEX_LOCK_STALE_MS;\n}\n\nfunction removeAbandonedIndexLock(lockDir: string): IndexLockCleanupResult {\n try {\n const info = fs.lstatSync(lockDir);\n if (info.isSymbolicLink()) return \"blocked\";\n if (!info.isDirectory()) {\n fs.rmSync(lockDir, { force: true });\n return \"removed\";\n }\n const owner = readIndexLockOwner(lockDir);\n if (owner !== null) {\n if (lockOwnerIsRunning(owner)) return \"wait\";\n }\n if (owner === null && lockIsFresh(info, null)) return \"wait\";\n fs.rmSync(lockDir, { recursive: true, force: true });\n return \"removed\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") return \"removed\";\n // Fail silently — indexes are advisory only\n return \"blocked\";\n }\n}\n\nfunction withIndexFileLock(filePath: string, update: () => void): void {\n const lockDir = `${filePath}.lock.d`;\n let acquired = false;\n\n while (!acquired) {\n try {\n fs.mkdirSync(lockDir);\n writeIndexLockOwner(lockDir);\n acquired = true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") {\n try {\n fs.mkdirSync(path.dirname(lockDir), { recursive: true });\n } catch {\n return;\n }\n sleepSync(INDEX_LOCK_POLL_MS);\n continue;\n }\n if (code !== \"EEXIST\") return;\n const cleanupResult = removeAbandonedIndexLock(lockDir);\n if (cleanupResult === \"blocked\") return;\n sleepSync(INDEX_LOCK_POLL_MS);\n }\n }\n\n try {\n update();\n } finally {\n try {\n fs.rmSync(lockDir, { recursive: true, force: true });\n } catch {\n // Fail silently — indexes are advisory only\n }\n }\n}\n\n/**\n * Atomic write: write to a unique `.tmp` sibling then rename so readers never\n * observe a partially-written file.\n */\nfunction writeJsonAtomic(filePath: string, data: unknown): void {\n const payload = JSON.stringify(data, null, 2);\n for (let attempt = 0; attempt < 3; attempt += 1) {\n const tmp = uniqueTempPath(filePath);\n try {\n fs.writeFileSync(tmp, payload, \"utf8\");\n fs.renameSync(tmp, filePath);\n return;\n } catch {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // Fail silently — indexes are advisory only\n }\n sleepSync(INDEX_LOCK_POLL_MS);\n }\n }\n}\n\nfunction updateTemporalIndex(memoryDir: string, update: (index: TemporalIndex) => void): void {\n const indexPath = temporalIndexPath(memoryDir);\n withIndexFileLock(indexPath, () => {\n const index = readJsonSafe<TemporalIndex>(indexPath, { version: INDEX_VERSION, dates: {} });\n update(index);\n writeJsonAtomic(indexPath, index);\n });\n}\n\nfunction updateTagIndex(memoryDir: string, update: (index: TagIndex) => void): void {\n const indexPath = tagIndexPath(memoryDir);\n withIndexFileLock(indexPath, () => {\n const index = normalizeTagIndex(\n readJsonSafe<TagIndex>(indexPath, { version: TAG_INDEX_VERSION, tags: {}, aliases: {} })\n );\n update(index);\n writeJsonAtomic(indexPath, index);\n });\n}\n\nfunction isoDateFromTimestamp(isoString: string): string {\n if (typeof isoString !== \"string\" || isoString.length < 10) {\n // Malformed frontmatter — fall back to today so the memory is still indexed.\n // Log a warning to surface data-quality issues without aborting the write.\n console.warn(`[engram] temporal-index: malformed timestamp \"${isoString}\", falling back to today`);\n return new Date().toISOString().slice(0, 10);\n }\n return isoString.slice(0, 10); // YYYY-MM-DD\n}\n\nfunction addPathToSet(record: Record<string, string[]>, key: string, p: string): void {\n if (!record[key]) {\n record[key] = [];\n }\n if (!record[key].includes(p)) {\n record[key].push(p);\n }\n}\n\nfunction removePathFromSet(record: Record<string, string[]>, key: string, p: string): void {\n if (!record[key]) return;\n record[key] = record[key].filter((x) => x !== p);\n if (record[key].length === 0) {\n delete record[key];\n }\n}\n\nfunction normalizeTagSegment(segment: string): string {\n return segment\n .trim()\n .toLowerCase()\n .replace(/[_\\s]+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction normalizeCanonicalTag(tag: string): string {\n return tag\n .trim()\n .toLowerCase()\n .replace(/\\s*[>:|.]+\\s*/g, \"/\")\n .replace(/\\s*\\/\\s*/g, \"/\")\n .split(\"/\")\n .map(normalizeTagSegment)\n .filter(Boolean)\n .join(\"/\");\n}\n\nfunction normalizeAliasKey(tag: string): string {\n return tag\n .trim()\n .toLowerCase()\n .replace(/[\\/_.:-]+/g, \" \")\n .replace(/[-]+/g, \" \")\n .replace(/[^a-z0-9\\s]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction singularizeAlias(alias: string): string | null {\n if (!alias.endsWith(\"s\") || alias.length <= 3) return null;\n return alias.slice(0, -1);\n}\n\nfunction deriveParentTags(canonical: string): string[] {\n const parts = canonical.split(\"/\").filter(Boolean);\n const parents: string[] = [];\n for (let i = parts.length - 1; i > 0; i -= 1) {\n parents.push(parts.slice(0, i).join(\"/\"));\n }\n return parents;\n}\n\nfunction deriveTagAliases(rawTag: string, canonical: string): string[] {\n const aliases = new Set<string>();\n const canonicalAlias = normalizeAliasKey(canonical);\n const rawAlias = normalizeAliasKey(rawTag);\n const leaf = canonical.split(\"/\").at(-1) ?? canonical;\n const leafAlias = normalizeAliasKey(leaf);\n\n for (const candidate of [canonicalAlias, rawAlias, leafAlias]) {\n if (!candidate) continue;\n aliases.add(candidate);\n const singular = singularizeAlias(candidate);\n if (singular) aliases.add(singular);\n }\n\n return Array.from(aliases);\n}\n\nfunction normalizeTagIndex(raw: TagIndex | null | undefined): TagIndex {\n const normalized: TagIndex = {\n version: TAG_INDEX_VERSION,\n tags: {},\n aliases: {},\n };\n\n if (!raw || typeof raw !== \"object\") {\n return normalized;\n }\n\n const sourceTags = raw.tags ?? {};\n for (const [rawCanonical, nodeOrPaths] of Object.entries(sourceTags)) {\n const canonical = normalizeCanonicalTag(rawCanonical);\n if (!canonical) continue;\n const node: TagNode = Array.isArray(nodeOrPaths)\n ? { paths: [...new Set(nodeOrPaths)] }\n : {\n paths: Array.isArray(nodeOrPaths?.paths) ? [...new Set(nodeOrPaths.paths)] : [],\n aliases: Array.isArray(nodeOrPaths?.aliases) ? [...new Set(nodeOrPaths.aliases)] : [],\n parents: Array.isArray(nodeOrPaths?.parents) ? [...new Set(nodeOrPaths.parents)] : [],\n };\n const existingNode = normalized.tags[canonical];\n if (existingNode && !Array.isArray(existingNode)) {\n existingNode.paths = [...new Set([...existingNode.paths, ...node.paths])];\n existingNode.aliases = [...new Set([...(existingNode.aliases ?? []), ...(node.aliases ?? [])])];\n existingNode.parents = [...new Set([...(existingNode.parents ?? []), ...(node.parents ?? [])])];\n } else if (Array.isArray(existingNode)) {\n normalized.tags[canonical] = {\n paths: [...new Set([...existingNode, ...node.paths])],\n aliases: [...new Set(node.aliases ?? [])],\n parents: [...new Set(node.parents ?? deriveParentTags(canonical))],\n };\n } else {\n normalized.tags[canonical] = node;\n }\n for (const alias of deriveTagAliases(canonical, canonical)) {\n const list = normalized.aliases![alias] ?? [];\n if (!list.includes(canonical)) list.push(canonical);\n normalized.aliases![alias] = list;\n }\n for (const alias of node.aliases ?? []) {\n const aliasKey = normalizeAliasKey(alias);\n if (!aliasKey) continue;\n const list = normalized.aliases![aliasKey] ?? [];\n if (!list.includes(canonical)) list.push(canonical);\n normalized.aliases![aliasKey] = list;\n }\n const mergedNode = normalized.tags[canonical];\n if (mergedNode && !Array.isArray(mergedNode)) {\n mergedNode.parents = [...new Set(mergedNode.parents ?? deriveParentTags(canonical))];\n }\n }\n\n for (const [alias, canonicals] of Object.entries(raw.aliases ?? {})) {\n const aliasKey = normalizeAliasKey(alias);\n if (!aliasKey) continue;\n const list = normalized.aliases![aliasKey] ?? [];\n for (const canonical of canonicals ?? []) {\n const normalizedCanonical = normalizeCanonicalTag(canonical);\n if (normalizedCanonical && !list.includes(normalizedCanonical)) {\n list.push(normalizedCanonical);\n }\n }\n normalized.aliases![aliasKey] = list;\n }\n\n return normalized;\n}\n\nfunction ensureTagNode(index: TagIndex, canonical: string): TagNode {\n const existing = index.tags[canonical];\n if (existing && !Array.isArray(existing)) {\n return existing;\n }\n const created: TagNode = {\n paths: Array.isArray(existing) ? [...new Set(existing)] : [],\n aliases: [],\n parents: deriveParentTags(canonical),\n };\n index.tags[canonical] = created;\n return created;\n}\n\nfunction addTagGraphEntry(index: TagIndex, rawTag: string, memoryPath: string): void {\n const canonical = normalizeCanonicalTag(rawTag);\n if (!canonical) return;\n const node = ensureTagNode(index, canonical);\n if (!node.paths.includes(memoryPath)) {\n node.paths.push(memoryPath);\n }\n\n for (const alias of deriveTagAliases(rawTag, canonical)) {\n const aliasKey = normalizeAliasKey(alias);\n if (!aliasKey) continue;\n if (!node.aliases?.includes(aliasKey)) {\n node.aliases = [...new Set([...(node.aliases ?? []), aliasKey])];\n }\n const list = index.aliases?.[aliasKey] ?? [];\n if (!list.includes(canonical)) {\n index.aliases![aliasKey] = [...list, canonical];\n }\n }\n}\n\nfunction removeTagGraphEntry(index: TagIndex, rawTag: string, memoryPath: string): void {\n const canonical = normalizeCanonicalTag(rawTag);\n if (!canonical) return;\n const node = index.tags[canonical];\n if (!node || Array.isArray(node)) return;\n node.paths = node.paths.filter((value) => value !== memoryPath);\n if (node.paths.length === 0) {\n delete index.tags[canonical];\n }\n}\n\nfunction expandCanonicalTags(index: TagIndex, rawTags: string[]): string[] {\n const canonicals = new Set<string>();\n for (const rawTag of rawTags) {\n const canonical = normalizeCanonicalTag(rawTag);\n if (canonical && index.tags[canonical]) {\n canonicals.add(canonical);\n }\n const aliasKey = normalizeAliasKey(rawTag);\n for (const resolved of index.aliases?.[aliasKey] ?? []) {\n canonicals.add(resolved);\n }\n }\n\n const expanded = new Set<string>();\n for (const canonical of canonicals) {\n expanded.add(canonical);\n const node = index.tags[canonical];\n if (node && !Array.isArray(node)) {\n for (const parent of node.parents ?? []) {\n expanded.add(parent);\n }\n }\n }\n return Array.from(expanded);\n}\n\nfunction aliasPhrase(alias: string): string {\n return alias.replace(/\\//g, \" \").replace(/-/g, \" \").replace(/\\s+/g, \" \").trim();\n}\n\nfunction promptContainsAlias(prompt: string, alias: string): boolean {\n const phrase = aliasPhrase(alias);\n if (!phrase) return false;\n const normalizedPrompt = ` ${normalizeAliasKey(prompt)} `;\n return normalizedPrompt.includes(` ${phrase} `);\n}\n\n// ─── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Add (or update) a memory file in both indexes.\n *\n * @param memoryDir Root memory directory\n * @param memoryPath Absolute path to the memory file\n * @param createdAt ISO timestamp of the memory's creation date\n * @param tags Array of tag strings from the memory's frontmatter\n */\nexport function indexMemory(memoryDir: string, memoryPath: string, createdAt: string, tags: string[]): void {\n try {\n ensureStateDir(memoryDir);\n\n const dateKey = isoDateFromTimestamp(createdAt);\n updateTemporalIndex(memoryDir, (index) => {\n addPathToSet(index.dates, dateKey, memoryPath);\n });\n\n updateTagIndex(memoryDir, (index) => {\n for (const tag of tags) {\n if (tag && typeof tag === \"string\") {\n addTagGraphEntry(index, tag, memoryPath);\n }\n }\n });\n } catch {\n // Fail silently\n }\n}\n\n/**\n * Remove a memory file from both indexes (called on deletion/archival).\n */\nexport function deindexMemory(memoryDir: string, memoryPath: string, createdAt: string, tags: string[]): void {\n try {\n ensureStateDir(memoryDir);\n\n const dateKey = isoDateFromTimestamp(createdAt);\n updateTemporalIndex(memoryDir, (index) => {\n removePathFromSet(index.dates, dateKey, memoryPath);\n });\n\n updateTagIndex(memoryDir, (index) => {\n for (const tag of tags) {\n if (tag && typeof tag === \"string\") {\n removeTagGraphEntry(index, tag, memoryPath);\n }\n }\n });\n } catch {\n // Fail silently\n }\n}\n\n/**\n * Reset both index files to empty state.\n * Called before a full-corpus rebuild so stale paths in any surviving index\n * file do not persist after the rebuild completes.\n */\nexport function clearIndexes(memoryDir: string): void {\n try {\n ensureStateDir(memoryDir);\n updateTemporalIndex(memoryDir, (index) => {\n index.version = INDEX_VERSION;\n index.lastRebuildAt = undefined;\n index.dates = {};\n });\n updateTagIndex(memoryDir, (index) => {\n index.version = TAG_INDEX_VERSION;\n index.lastRebuildAt = undefined;\n index.tags = {};\n index.aliases = {};\n });\n } catch {\n // Fail silently — indexes are advisory only\n }\n}\n\n/**\n * Returns true when both index files exist on disk.\n * Used to detect first-time enablement so callers can trigger a full rebuild.\n */\nexport function indexesExist(memoryDir: string): boolean {\n try {\n return fs.existsSync(temporalIndexPath(memoryDir)) && fs.existsSync(tagIndexPath(memoryDir));\n } catch {\n return false;\n }\n}\n\n/**\n * Batch-add multiple memories to both indexes in a single read-modify-write cycle.\n * More efficient than calling indexMemory() per file when adding many at once.\n */\nexport function indexMemoriesBatch(\n memoryDir: string,\n entries: Array<{ path: string; createdAt: string; tags: string[] }>\n): void {\n if (entries.length === 0) return;\n try {\n ensureStateDir(memoryDir);\n\n updateTemporalIndex(memoryDir, (index) => {\n for (const entry of entries) {\n const dateKey = isoDateFromTimestamp(entry.createdAt);\n addPathToSet(index.dates, dateKey, entry.path);\n }\n });\n\n updateTagIndex(memoryDir, (index) => {\n for (const entry of entries) {\n for (const tag of entry.tags) {\n if (tag && typeof tag === \"string\") {\n addTagGraphEntry(index, tag, entry.path);\n }\n }\n }\n });\n } catch {\n // Fail silently\n }\n}\n\n/**\n * Return the set of memory paths whose index date falls in [fromDate, toDate).\n *\n * Boundary semantics (exclusive upper bound):\n * - `fromDate` is INCLUSIVE — entries on this date ARE returned.\n * - `toDate` is EXCLUSIVE — entries on this date are NOT returned.\n * Pass `recencyWindowBoundsFromPrompt(query).toDate` to get the correct\n * exclusive boundary; do NOT add +1 day yourself.\n * - Default `toDate` = tomorrow, so omitting it includes all of today.\n *\n * @param memoryDir - root memory directory (contains state/index_time.json)\n * @param fromDate - inclusive start date, YYYY-MM-DD\n * @param toDate - exclusive end date, YYYY-MM-DD (default: tomorrow)\n * @returns Set of matching file paths, or null if the index is unavailable.\n */\nexport async function queryByDateRangeAsync(\n memoryDir: string,\n fromDate: string,\n toDate?: string\n): Promise<Set<string> | null> {\n try {\n const tPath = temporalIndexPath(memoryDir);\n let raw: string;\n try {\n raw = await fs.promises.readFile(tPath, \"utf8\");\n } catch {\n return null; // File missing or unreadable\n }\n let tIndex: TemporalIndex;\n try {\n tIndex = JSON.parse(raw) as TemporalIndex;\n } catch {\n tIndex = { version: INDEX_VERSION, dates: {} };\n }\n // toDate is exclusive (first day NOT included). Default: tomorrow so \"all of today\" is included.\n const end = toDate ?? new Date(Date.now() + 86_400_000).toISOString().slice(0, 10);\n\n const results = new Set<string>();\n for (const [date, paths] of Object.entries(tIndex.dates)) {\n if (date >= fromDate && date < end) {\n for (const p of paths) {\n results.add(p);\n }\n }\n }\n return results;\n } catch {\n return null;\n }\n}\n\n/**\n * Async version of queryByTags — uses non-blocking fs.promises.readFile\n * to avoid blocking the Node.js event loop.\n */\nexport async function queryByTagsAsync(memoryDir: string, tags: string[]): Promise<Set<string> | null> {\n if (tags.length === 0) return null;\n try {\n const gPath = tagIndexPath(memoryDir);\n let raw: string;\n try {\n raw = await fs.promises.readFile(gPath, \"utf8\");\n } catch {\n return null; // File missing or unreadable\n }\n let gIndex: TagIndex;\n try {\n gIndex = normalizeTagIndex(JSON.parse(raw) as TagIndex);\n } catch {\n gIndex = { version: TAG_INDEX_VERSION, tags: {}, aliases: {} };\n }\n\n return queryByTagsFromIndex(gIndex, tags);\n } catch {\n return null;\n }\n}\n\nfunction queryByTagsFromIndex(index: TagIndex, tags: string[]): Set<string> | null {\n const expandedTags = expandCanonicalTags(index, tags);\n const results = new Set<string>();\n for (const canonical of expandedTags) {\n const nodeOrPaths = index.tags[canonical];\n const paths = Array.isArray(nodeOrPaths) ? nodeOrPaths : (nodeOrPaths?.paths ?? []);\n for (const pathValue of paths) {\n results.add(pathValue);\n }\n }\n return results.size > 0 ? results : null;\n}\n\n/**\n * Extract tags from a prompt for tag-based prefiltering.\n * Looks for hashtag-style tokens (#foo).\n * Returns lowercase, deduplicated list.\n */\nexport function extractTagsFromPrompt(prompt: string): string[] {\n const found = new Set<string>();\n\n // Match #tag style tokens\n const hashMatches = prompt.matchAll(/#([a-zA-Z][\\w-]{1,30})/g);\n for (const m of hashMatches) {\n const canonical = normalizeCanonicalTag(m[1]);\n if (canonical) found.add(canonical);\n }\n\n return Array.from(found);\n}\n\nexport async function resolvePromptTagPrefilterAsync(\n memoryDir: string,\n prompt: string\n): Promise<{\n matchedTags: string[];\n expandedTags: string[];\n paths: Set<string> | null;\n}> {\n const explicitTags = extractTagsFromPrompt(prompt);\n try {\n const raw = await fs.promises.readFile(tagIndexPath(memoryDir), \"utf8\");\n const tagIndex = normalizeTagIndex(JSON.parse(raw) as TagIndex);\n const matched = new Set<string>(explicitTags);\n\n for (const canonical of Object.keys(tagIndex.tags)) {\n if (promptContainsAlias(prompt, canonical)) {\n matched.add(canonical);\n }\n }\n for (const [alias, canonicals] of Object.entries(tagIndex.aliases ?? {})) {\n if (!promptContainsAlias(prompt, alias)) continue;\n for (const canonical of canonicals) {\n matched.add(canonical);\n }\n }\n\n const expandedTags = expandCanonicalTags(tagIndex, Array.from(matched));\n const paths = queryByTagsFromIndex(tagIndex, expandedTags);\n return {\n matchedTags: Array.from(matched),\n expandedTags,\n paths,\n };\n } catch {\n return { matchedTags: explicitTags, expandedTags: explicitTags, paths: null };\n }\n}\n\n/**\n * Detect if a prompt is time-sensitive (mentions specific time references).\n * Used to decide whether to activate the temporal prefilter.\n */\nexport function isTemporalQuery(prompt: string): boolean {\n return /\\b(today|yesterday|this week|last week|this month|last month|recent(?:ly)?|lately|just now|earlier today|this morning|last night|last year|this year|\\d+ days? ago|\\d+ hours? ago|\\d+ weeks? ago|\\d+ months? ago|(?:in |on |during |since |before |after )?(?:january|february|march|april|may|june|july|august|september|october|november|december)(?:\\s+\\d{1,4})?|\\d{4}-\\d{2}-\\d{2}|\\d{1,2}\\/\\d{1,2}\\/\\d{2,4}|(?:spring|summer|fall|autumn|winter)\\s+\\d{4}|on the \\d{1,2}(?:st|nd|rd|th)?|last (?:monday|tuesday|wednesday|thursday|friday|saturday|sunday))\\b/i.test(\n prompt\n );\n}\n\n/**\n * Compute a \"from date\" string (YYYY-MM-DD) for a recency-based temporal query.\n * For \"recent\" / \"lately\" returns 7 days ago; for today/yesterday the obvious window.\n */\nexport function recencyWindowFromPrompt(prompt: string, nowMs: number = Date.now()): string {\n const p = prompt.toLowerCase();\n const now = new Date(nowMs);\n let daysBack = 7; // default\n\n if (/\\btoday\\b/.test(p) || /\\bthis morning\\b/.test(p) || /\\bjust now\\b/.test(p) || /\\bearlier today\\b/.test(p)) {\n daysBack = 0; // fromDate = today → window [today, today]\n } else if (/\\byesterday\\b/.test(p) || /\\blast night\\b/.test(p)) {\n daysBack = 1; // fromDate = yesterday → window [yesterday, today]\n } else if (/\\bthis week\\b/.test(p)) {\n daysBack = 7;\n } else if (/\\blast week\\b/.test(p)) {\n daysBack = 14;\n } else if (/\\bthis month\\b/.test(p)) {\n daysBack = 31;\n } else if (/\\blast month\\b/.test(p)) {\n daysBack = 62;\n } else if (/\\bthis year\\b/.test(p)) {\n // From Jan 1 of current year\n const jan1 = new Date(now.getFullYear(), 0, 1);\n return jan1.toISOString().slice(0, 10);\n } else if (/\\blast year\\b/.test(p)) {\n const jan1LastYear = new Date(now.getFullYear() - 1, 0, 1);\n return jan1LastYear.toISOString().slice(0, 10);\n } else {\n // Try specific month references: \"in March\", \"during January\", \"since February\"\n const monthNames = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n ];\n const monthMatch = p.match(\n /\\b(january|february|march|april|may|june|july|august|september|october|november|december)(?:\\s+(\\d{4}))?\\b/\n );\n if (monthMatch) {\n const monthIdx = monthNames.indexOf(monthMatch[1]);\n const year = monthMatch[2] ? Number.parseInt(monthMatch[2], 10) : now.getFullYear();\n // \"before <month>\" means everything prior to that month: use 2-year lookback\n // as fromDate so the window isn't unbounded. toDate is set to the month start\n // in recencyWindowBoundsFromPrompt.\n // IMPORTANT: when an explicit year is given, anchor the lookback to the named\n // month start (not to today). \"before January 2024\" should produce\n // fromDate ≈ 2022-01 — not today-730 days, which could land after 2024-01 and\n // invert the window for any explicitly past month within the 730-day horizon.\n if (/\\bbefore\\b/.test(p)) {\n if (monthMatch[2]) {\n // Explicit year: anchor fromDate 730 days before the named month start.\n const monthStart = new Date(year, monthIdx, 1);\n return new Date(monthStart.getTime() - 730 * 86_400_000).toISOString().slice(0, 10);\n }\n daysBack = 730;\n } else {\n const monthStart = new Date(year, monthIdx, 1);\n return monthStart.toISOString().slice(0, 10);\n }\n }\n\n // Try \"N weeks ago\"\n const weekMatch = p.match(/(\\d{1,5})\\s*weeks?\\s*ago/);\n if (weekMatch) {\n daysBack = Math.min(365, Number.parseInt(weekMatch[1], 10) * 7);\n } else {\n // Try \"N months ago\"\n const monthsAgoMatch = p.match(/(\\d{1,5})\\s*months?\\s*ago/);\n if (monthsAgoMatch) {\n daysBack = Math.min(730, Number.parseInt(monthsAgoMatch[1], 10) * 31);\n } else {\n const numMatch = p.match(/(\\d{1,5})\\s*days?\\s*ago/);\n if (numMatch) {\n daysBack = Math.min(365, Number.parseInt(numMatch[1], 10)); // no off-by-one: \"3 days ago\" → 3\n } else {\n const hrMatch = p.match(/(\\d{1,5})\\s*hours?\\s*ago/);\n if (hrMatch) {\n // Convert hours to days (ceiling); at least 1 day window\n daysBack = Math.max(1, Math.ceil(Number.parseInt(hrMatch[1], 10) / 24));\n }\n }\n }\n }\n\n // Try explicit date patterns: YYYY-MM-DD or MM/DD/YYYY\n const isoMatch = p.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n if (isoMatch) {\n return `${isoMatch[1]}-${isoMatch[2]}-${isoMatch[3]}`;\n }\n const usMatch = p.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{2,4})/);\n if (usMatch) {\n const year = usMatch[3].length === 2 ? 2000 + Number.parseInt(usMatch[3], 10) : Number.parseInt(usMatch[3], 10);\n return `${year}-${usMatch[1].padStart(2, \"0\")}-${usMatch[2].padStart(2, \"0\")}`;\n }\n\n // Try \"last Monday/Tuesday/etc\"\n const dayOfWeekMatch = p.match(/\\blast\\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b/);\n if (dayOfWeekMatch) {\n const dayNames = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n const targetDay = dayNames.indexOf(dayOfWeekMatch[1]);\n const currentDay = now.getDay();\n daysBack = (currentDay - targetDay + 7) % 7 || 7; // at least 7 days back\n }\n }\n\n const from = new Date(nowMs - daysBack * 24 * 60 * 60 * 1000);\n return from.toISOString().slice(0, 10);\n}\n\n/**\n * Returns both the start and end of the temporal window implied by the prompt.\n *\n * `fromDate` is computed by delegating to `recencyWindowFromPrompt`, so the two\n * functions cannot diverge on the lower bound. `toDate` is computed independently\n * here using the same pattern-priority ordering, because the upper-bound arithmetic\n * differs (exclusive end vs. inclusive start) and would not be expressible as a\n * simple wrapper.\n *\n * Known divergence to be aware of: for \"N hours ago\", `recencyWindowFromPrompt`\n * uses `Math.max(1, Math.ceil(hours/24))` days back, so `fromDate` = yesterday for\n * sub-24h values. `toDate` here is always `tomorrow` when no explicit date is\n * specified (see the `toDaysBack === 0` branch), giving a 2-day window. This is\n * intentional — the window must cover both yesterday and today for recent hour-level\n * queries. Any fix to the hours formula in `recencyWindowFromPrompt` must be\n * manually reflected here.\n *\n * - `fromDate`: first day of the implied window (same as `recencyWindowFromPrompt`)\n * - `toDate`: first day NOT in the window (exclusive upper bound), so callers\n * should filter with `date >= fromDate && date < toDate`.\n */\nexport function recencyWindowBoundsFromPrompt(\n prompt: string,\n nowMs: number = Date.now()\n): { fromDate: string; toDate: string } {\n const fromDate = recencyWindowFromPrompt(prompt, nowMs);\n const p = prompt.toLowerCase();\n const now = new Date(nowMs);\n const today = now.toISOString().slice(0, 10);\n const tomorrow = new Date(nowMs + 86_400_000).toISOString().slice(0, 10);\n\n let toDate: string;\n\n if (/\\btoday\\b|\\bthis morning\\b|\\bjust now\\b|\\bearlier today\\b/.test(p)) {\n toDate = tomorrow; // exclusive: include all of today\n } else if (/\\byesterday\\b|\\blast night\\b/.test(p)) {\n toDate = today; // exclusive: include all of yesterday, stop before today\n } else if (/\\bthis week\\b|\\bthis month\\b|\\bthis year\\b/.test(p)) {\n toDate = tomorrow; // exclusive: include all of today\n } else if (/\\blast week\\b/.test(p)) {\n toDate = new Date(nowMs - 7 * 86_400_000).toISOString().slice(0, 10); // already exclusive\n } else if (/\\blast month\\b/.test(p)) {\n toDate = new Date(nowMs - 31 * 86_400_000).toISOString().slice(0, 10); // approximately exclusive\n } else if (/\\blast year\\b/.test(p)) {\n toDate = `${now.getFullYear()}-01-01`; // exclusive: stop at Jan 1 of current year\n } else {\n // Mirror the structure of recencyWindowFromPrompt's else branch:\n // month name → early set (highest priority), then ago patterns set a\n // working offset, then ISO/US/weekday patterns run AFTER ago patterns\n // and can override them — matching the priority ordering in recencyWindowFromPrompt.\n\n const monthNames = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n ];\n const monthMatch = p.match(\n /\\b(january|february|march|april|may|june|july|august|september|october|november|december)(?:\\s+(\\d{4}))?\\b/\n );\n if (monthMatch) {\n // \"since <month>\" / \"after <month>\" — open-ended: everything from that month to now.\n // \"before <month>\" — closed upper bound: everything before that month starts.\n // Plain \"<month>\" — just that calendar month.\n const monthIdx = monthNames.indexOf(monthMatch[1]);\n const year = monthMatch[2] ? Number.parseInt(monthMatch[2], 10) : now.getFullYear();\n const isSinceOrAfter = /\\b(since|after)\\b/.test(p);\n const isBefore = /\\bbefore\\b/.test(p);\n if (isSinceOrAfter) {\n toDate = tomorrow;\n } else if (isBefore) {\n // \"before <month>\" means everything prior to that month.\n // toDate = first day of that month (exclusive upper bound).\n toDate = new Date(year, monthIdx, 1).toISOString().slice(0, 10);\n } else {\n // Closed window: first day of the NEXT month is the exclusive upper bound.\n toDate = new Date(year, monthIdx + 1, 1).toISOString().slice(0, 10);\n }\n } else {\n // Ago patterns set a working offset (recent edge of the N-ago window).\n // Same nesting order as recencyWindowFromPrompt.\n // Sentinel -1 means \"no ago pattern matched\" → toDate falls back to tomorrow.\n let toDaysBack = -1;\n const weekMatch = p.match(/(\\d{1,5})\\s*weeks?\\s*ago/);\n if (weekMatch) {\n toDaysBack = Math.max(0, Math.min(52, Number.parseInt(weekMatch[1], 10)) - 1) * 7;\n } else {\n const monthsAgoMatch = p.match(/(\\d{1,5})\\s*months?\\s*ago/);\n if (monthsAgoMatch) {\n toDaysBack = Math.max(0, Math.min(24, Number.parseInt(monthsAgoMatch[1], 10)) - 1) * 31;\n } else {\n const numMatch = p.match(/(\\d{1,5})\\s*days?\\s*ago/);\n if (numMatch) {\n // (N-1) mirrors the weeks/months ago formula: \"3 days ago\" → window [today-3, today-2]\n // N=1 → toDaysBack=0 → toDate=today (exclusive) → window [yesterday, today) = 1 day. ✓\n toDaysBack = Math.max(0, Math.min(365, Number.parseInt(numMatch[1], 10)) - 1);\n } else {\n const hrMatch = p.match(/(\\d{1,5})\\s*hours?\\s*ago/);\n if (hrMatch) {\n // Sub-day precision: keep sentinel -1 so toDate falls back to tomorrow,\n // which includes today. fromDate = yesterday (recencyWindowFromPrompt uses ceiling).\n toDaysBack = -1;\n }\n }\n }\n }\n\n // Explicit date/weekday patterns run AFTER ago patterns (same as recencyWindowFromPrompt)\n // and override the ago-derived offset when present.\n const isoMatch = p.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n if (isoMatch) {\n // +1 day: exclusive upper bound includes the named date\n const d = new Date(`${isoMatch[1]}-${isoMatch[2]}-${isoMatch[3]}T00:00:00Z`);\n toDate = new Date(d.getTime() + 86_400_000).toISOString().slice(0, 10);\n } else {\n const usMatch = p.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{2,4})/);\n if (usMatch) {\n const year =\n usMatch[3].length === 2 ? 2000 + Number.parseInt(usMatch[3], 10) : Number.parseInt(usMatch[3], 10);\n // +1 day: exclusive upper bound includes the named date\n const d = new Date(`${year}-${usMatch[1].padStart(2, \"0\")}-${usMatch[2].padStart(2, \"0\")}T00:00:00Z`);\n toDate = new Date(d.getTime() + 86_400_000).toISOString().slice(0, 10);\n } else {\n const dayOfWeekMatch = p.match(/\\blast\\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b/);\n if (dayOfWeekMatch) {\n const dayNames = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n const targetDay = dayNames.indexOf(dayOfWeekMatch[1]);\n const currentDay = now.getDay();\n const daysBack = (currentDay - targetDay + 7) % 7 || 7;\n // +1 day: exclusive upper bound includes the named weekday\n toDate = new Date(nowMs - (daysBack - 1) * 86_400_000).toISOString().slice(0, 10);\n } else {\n // Use the ago-derived offset.\n // toDaysBack=-1 means no pattern matched (or hours-ago): use tomorrow so today\n // is included in the window. toDaysBack=0 means N=1 ago (e.g. \"1 day ago\"):\n // toDate = today (exclusive) correctly creates a 1-day window [yesterday, today).\n toDate = toDaysBack < 0 ? tomorrow : new Date(nowMs - toDaysBack * 86_400_000).toISOString().slice(0, 10);\n }\n }\n }\n }\n }\n\n // Guard: if toDate would precede fromDate (inverted window from conflicting keywords),\n // fall back to tomorrow (exclusive upper bound that covers today) so we never produce\n // an empty window. Using `today` is insufficient because `date < today` excludes today.\n if (toDate <= fromDate) toDate = tomorrow;\n\n return { fromDate, toDate };\n}\n"],"mappings":";AAiBA,SAAS,oBAAoB;AAC7B,YAAY,YAAY;AACxB,YAAY,QAAQ;AACpB,YAAY,UAAU;AA0BtB,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAC5B,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB;AAC3B,IAAM,mCAAmC;AACzC,IAAM,mBAAmB,IAAI,WAAW,IAAI,kBAAkB,CAAC,CAAC;AAChE,IAAM,8BAA8B,KAAK,IAAI,IAAI,QAAQ,OAAO,IAAI;AAUpE,SAAS,SAAS,WAA2B;AAC3C,SAAY,UAAK,WAAW,OAAO;AACrC;AAEA,SAAS,kBAAkB,WAA2B;AACpD,SAAY,UAAK,SAAS,SAAS,GAAG,mBAAmB;AAC3D;AAEA,SAAS,aAAa,WAA2B;AAC/C,SAAY,UAAK,SAAS,SAAS,GAAG,cAAc;AACtD;AAEA,SAAS,eAAe,WAAyB;AAC/C,QAAM,MAAM,SAAS,SAAS;AAC9B,EAAG,aAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC;AAEA,SAAS,aAAgB,UAAkB,UAAgB;AACzD,MAAI;AACF,UAAM,MAAS,gBAAa,UAAU,MAAM;AAC5C,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,SAAS,UAAU,IAAkB;AACnC,UAAQ,KAAK,kBAAkB,GAAG,GAAG,EAAE;AACzC;AAEA,SAAS,eAAe,UAA0B;AAChD,QAAM,MAAW,aAAQ,QAAQ;AACjC,QAAM,OAAY,cAAS,QAAQ;AACnC,QAAM,QAAe,mBAAY,CAAC,EAAE,SAAS,KAAK;AAClD,SAAY,UAAK,KAAK,IAAI,IAAI,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,MAAM;AAC5E;AAEA,SAAS,cAAc,SAAyB;AAC9C,SAAY,UAAK,SAAS,YAAY;AACxC;AAEA,SAAS,oBAAoB,SAAuB;AAClD,MAAI;AACF,IAAG;AAAA,MACD,cAAc,OAAO;AAAA,MACrB,KAAK,UAAU;AAAA,QACb,KAAK,QAAQ;AAAA,QACb,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,oBAAoB;AAAA,MACtB,CAAC;AAAA,MACD;AAAA,QACE,UAAU;AAAA,QACV,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,mBAAmB,SAAwC;AAClE,MAAI;AACF,UAAM,SAAS,KAAK,MAAS,gBAAa,cAAc,OAAO,GAAG,MAAM,CAAC;AACzE,QAAI,EAAE,OAAO,OAAO,QAAQ,YAAY,OAAO,UAAU,OAAO,GAAG,KAAK,OAAO,MAAM,GAAI,QAAO;AAChG,UAAM,QAAwB,EAAE,KAAK,OAAO,IAAI;AAChD,QACE,eAAe,UACf,OAAQ,OAAmC,cAAc,YACxD,OAAiC,UAAU,SAAS,GACrD;AACA,YAAM,YAAa,OAAiC;AAAA,IACtD;AACA,UAAM,qBAAsB,OAA4C;AACxE,QAAI,OAAO,uBAAuB,YAAY,OAAO,SAAS,kBAAkB,KAAK,qBAAqB,GAAG;AAC3G,YAAM,qBAAqB;AAAA,IAC7B;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,eAAe,KAAsB;AAC5C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAQ,OAAiC;AAC/C,WAAO,SAAS;AAAA,EAClB;AACF;AAEA,SAAS,uBAAuB,KAA4B;AAC1D,MAAI;AACF,UAAM,SAAS,aAAa,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,MAAM,SAAS,GAAG;AAAA,MACtE,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MAClC,SAAS;AAAA,IACX,CAAC,EAAE,KAAK;AACR,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,cAAc,KAAK,MAAM,MAAM;AACrC,WAAO,OAAO,SAAS,WAAW,IAAI,cAAc;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,OAAgC;AAC1D,MAAI,CAAC,eAAe,MAAM,GAAG,EAAG,QAAO;AACvC,MAAI,MAAM,uBAAuB,OAAW,QAAO;AACnD,QAAM,qBAAqB,uBAAuB,MAAM,GAAG;AAC3D,MAAI,uBAAuB,KAAM,QAAO;AACxC,SAAO,sBAAsB,MAAM,qBAAqB;AAC1D;AAEA,SAAS,YAAY,UAAoB,OAAuC;AAC9E,QAAM,mBACJ,OAAO,OAAO,cAAc,YAAY,MAAM,UAAU,SAAS,IAAI,KAAK,MAAM,MAAM,SAAS,IAAI,OAAO;AAC5G,QAAM,cAAc,OAAO,SAAS,gBAAgB,IAAI,mBAAmB,SAAS;AACpF,SAAO,KAAK,IAAI,IAAI,cAAc;AACpC;AAEA,SAAS,yBAAyB,SAAyC;AACzE,MAAI;AACF,UAAM,OAAU,aAAU,OAAO;AACjC,QAAI,KAAK,eAAe,EAAG,QAAO;AAClC,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,MAAG,UAAO,SAAS,EAAE,OAAO,KAAK,CAAC;AAClC,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,mBAAmB,OAAO;AACxC,QAAI,UAAU,MAAM;AAClB,UAAI,mBAAmB,KAAK,EAAG,QAAO;AAAA,IACxC;AACA,QAAI,UAAU,QAAQ,YAAY,MAAM,IAAI,EAAG,QAAO;AACtD,IAAG,UAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACnD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAK,OAAiC,SAAS,SAAU,QAAO;AAEhE,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,UAAkB,QAA0B;AACrE,QAAM,UAAU,GAAG,QAAQ;AAC3B,MAAI,WAAW;AAEf,SAAO,CAAC,UAAU;AAChB,QAAI;AACF,MAAG,aAAU,OAAO;AACpB,0BAAoB,OAAO;AAC3B,iBAAW;AAAA,IACb,SAAS,OAAO;AACd,YAAM,OAAQ,OAAiC;AAC/C,UAAI,SAAS,UAAU;AACrB,YAAI;AACF,UAAG,aAAe,aAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,QACzD,QAAQ;AACN;AAAA,QACF;AACA,kBAAU,kBAAkB;AAC5B;AAAA,MACF;AACA,UAAI,SAAS,SAAU;AACvB,YAAM,gBAAgB,yBAAyB,OAAO;AACtD,UAAI,kBAAkB,UAAW;AACjC,gBAAU,kBAAkB;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI;AACF,WAAO;AAAA,EACT,UAAE;AACA,QAAI;AACF,MAAG,UAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IACrD,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAMA,SAAS,gBAAgB,UAAkB,MAAqB;AAC9D,QAAM,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC;AAC5C,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,UAAM,MAAM,eAAe,QAAQ;AACnC,QAAI;AACF,MAAG,iBAAc,KAAK,SAAS,MAAM;AACrC,MAAG,cAAW,KAAK,QAAQ;AAC3B;AAAA,IACF,QAAQ;AACN,UAAI;AACF,QAAG,cAAW,GAAG;AAAA,MACnB,QAAQ;AAAA,MAER;AACA,gBAAU,kBAAkB;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB,WAAmB,QAA8C;AAC5F,QAAM,YAAY,kBAAkB,SAAS;AAC7C,oBAAkB,WAAW,MAAM;AACjC,UAAM,QAAQ,aAA4B,WAAW,EAAE,SAAS,eAAe,OAAO,CAAC,EAAE,CAAC;AAC1F,WAAO,KAAK;AACZ,oBAAgB,WAAW,KAAK;AAAA,EAClC,CAAC;AACH;AAEA,SAAS,eAAe,WAAmB,QAAyC;AAClF,QAAM,YAAY,aAAa,SAAS;AACxC,oBAAkB,WAAW,MAAM;AACjC,UAAM,QAAQ;AAAA,MACZ,aAAuB,WAAW,EAAE,SAAS,mBAAmB,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC;AAAA,IACzF;AACA,WAAO,KAAK;AACZ,oBAAgB,WAAW,KAAK;AAAA,EAClC,CAAC;AACH;AAEA,SAAS,qBAAqB,WAA2B;AACvD,MAAI,OAAO,cAAc,YAAY,UAAU,SAAS,IAAI;AAG1D,YAAQ,KAAK,iDAAiD,SAAS,0BAA0B;AACjG,YAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EAC7C;AACA,SAAO,UAAU,MAAM,GAAG,EAAE;AAC9B;AAEA,SAAS,aAAa,QAAkC,KAAa,GAAiB;AACpF,MAAI,CAAC,OAAO,GAAG,GAAG;AAChB,WAAO,GAAG,IAAI,CAAC;AAAA,EACjB;AACA,MAAI,CAAC,OAAO,GAAG,EAAE,SAAS,CAAC,GAAG;AAC5B,WAAO,GAAG,EAAE,KAAK,CAAC;AAAA,EACpB;AACF;AAEA,SAAS,kBAAkB,QAAkC,KAAa,GAAiB;AACzF,MAAI,CAAC,OAAO,GAAG,EAAG;AAClB,SAAO,GAAG,IAAI,OAAO,GAAG,EAAE,OAAO,CAAC,MAAM,MAAM,CAAC;AAC/C,MAAI,OAAO,GAAG,EAAE,WAAW,GAAG;AAC5B,WAAO,OAAO,GAAG;AAAA,EACnB;AACF;AAEA,SAAS,oBAAoB,SAAyB;AACpD,SAAO,QACJ,KAAK,EACL,YAAY,EACZ,QAAQ,WAAW,GAAG,EACtB,QAAQ,eAAe,GAAG,EAC1B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACzB;AAEA,SAAS,sBAAsB,KAAqB;AAClD,SAAO,IACJ,KAAK,EACL,YAAY,EACZ,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,aAAa,GAAG,EACxB,MAAM,GAAG,EACT,IAAI,mBAAmB,EACvB,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAEA,SAAS,kBAAkB,KAAqB;AAC9C,SAAO,IACJ,KAAK,EACL,YAAY,EACZ,QAAQ,cAAc,GAAG,EACzB,QAAQ,SAAS,GAAG,EACpB,QAAQ,iBAAiB,GAAG,EAC5B,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEA,SAAS,iBAAiB,OAA8B;AACtD,MAAI,CAAC,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,EAAG,QAAO;AACtD,SAAO,MAAM,MAAM,GAAG,EAAE;AAC1B;AAEA,SAAS,iBAAiB,WAA6B;AACrD,QAAM,QAAQ,UAAU,MAAM,GAAG,EAAE,OAAO,OAAO;AACjD,QAAM,UAAoB,CAAC;AAC3B,WAAS,IAAI,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,GAAG;AAC5C,YAAQ,KAAK,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAgB,WAA6B;AACrE,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,iBAAiB,kBAAkB,SAAS;AAClD,QAAM,WAAW,kBAAkB,MAAM;AACzC,QAAM,OAAO,UAAU,MAAM,GAAG,EAAE,GAAG,EAAE,KAAK;AAC5C,QAAM,YAAY,kBAAkB,IAAI;AAExC,aAAW,aAAa,CAAC,gBAAgB,UAAU,SAAS,GAAG;AAC7D,QAAI,CAAC,UAAW;AAChB,YAAQ,IAAI,SAAS;AACrB,UAAM,WAAW,iBAAiB,SAAS;AAC3C,QAAI,SAAU,SAAQ,IAAI,QAAQ;AAAA,EACpC;AAEA,SAAO,MAAM,KAAK,OAAO;AAC3B;AAEA,SAAS,kBAAkB,KAA4C;AACrE,QAAM,aAAuB;AAAA,IAC3B,SAAS;AAAA,IACT,MAAM,CAAC;AAAA,IACP,SAAS,CAAC;AAAA,EACZ;AAEA,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,IAAI,QAAQ,CAAC;AAChC,aAAW,CAAC,cAAc,WAAW,KAAK,OAAO,QAAQ,UAAU,GAAG;AACpE,UAAM,YAAY,sBAAsB,YAAY;AACpD,QAAI,CAAC,UAAW;AAChB,UAAM,OAAgB,MAAM,QAAQ,WAAW,IAC3C,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,WAAW,CAAC,EAAE,IACnC;AAAA,MACE,OAAO,MAAM,QAAQ,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,IAAI,YAAY,KAAK,CAAC,IAAI,CAAC;AAAA,MAC9E,SAAS,MAAM,QAAQ,aAAa,OAAO,IAAI,CAAC,GAAG,IAAI,IAAI,YAAY,OAAO,CAAC,IAAI,CAAC;AAAA,MACpF,SAAS,MAAM,QAAQ,aAAa,OAAO,IAAI,CAAC,GAAG,IAAI,IAAI,YAAY,OAAO,CAAC,IAAI,CAAC;AAAA,IACtF;AACJ,UAAM,eAAe,WAAW,KAAK,SAAS;AAC9C,QAAI,gBAAgB,CAAC,MAAM,QAAQ,YAAY,GAAG;AAChD,mBAAa,QAAQ,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,aAAa,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC;AACxE,mBAAa,UAAU,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAI,aAAa,WAAW,CAAC,GAAI,GAAI,KAAK,WAAW,CAAC,CAAE,CAAC,CAAC;AAC9F,mBAAa,UAAU,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAI,aAAa,WAAW,CAAC,GAAI,GAAI,KAAK,WAAW,CAAC,CAAE,CAAC,CAAC;AAAA,IAChG,WAAW,MAAM,QAAQ,YAAY,GAAG;AACtC,iBAAW,KAAK,SAAS,IAAI;AAAA,QAC3B,OAAO,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,cAAc,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,QACpD,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC;AAAA,QACxC,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,WAAW,iBAAiB,SAAS,CAAC,CAAC;AAAA,MACnE;AAAA,IACF,OAAO;AACL,iBAAW,KAAK,SAAS,IAAI;AAAA,IAC/B;AACA,eAAW,SAAS,iBAAiB,WAAW,SAAS,GAAG;AAC1D,YAAM,OAAO,WAAW,QAAS,KAAK,KAAK,CAAC;AAC5C,UAAI,CAAC,KAAK,SAAS,SAAS,EAAG,MAAK,KAAK,SAAS;AAClD,iBAAW,QAAS,KAAK,IAAI;AAAA,IAC/B;AACA,eAAW,SAAS,KAAK,WAAW,CAAC,GAAG;AACtC,YAAM,WAAW,kBAAkB,KAAK;AACxC,UAAI,CAAC,SAAU;AACf,YAAM,OAAO,WAAW,QAAS,QAAQ,KAAK,CAAC;AAC/C,UAAI,CAAC,KAAK,SAAS,SAAS,EAAG,MAAK,KAAK,SAAS;AAClD,iBAAW,QAAS,QAAQ,IAAI;AAAA,IAClC;AACA,UAAM,aAAa,WAAW,KAAK,SAAS;AAC5C,QAAI,cAAc,CAAC,MAAM,QAAQ,UAAU,GAAG;AAC5C,iBAAW,UAAU,CAAC,GAAG,IAAI,IAAI,WAAW,WAAW,iBAAiB,SAAS,CAAC,CAAC;AAAA,IACrF;AAAA,EACF;AAEA,aAAW,CAAC,OAAO,UAAU,KAAK,OAAO,QAAQ,IAAI,WAAW,CAAC,CAAC,GAAG;AACnE,UAAM,WAAW,kBAAkB,KAAK;AACxC,QAAI,CAAC,SAAU;AACf,UAAM,OAAO,WAAW,QAAS,QAAQ,KAAK,CAAC;AAC/C,eAAW,aAAa,cAAc,CAAC,GAAG;AACxC,YAAM,sBAAsB,sBAAsB,SAAS;AAC3D,UAAI,uBAAuB,CAAC,KAAK,SAAS,mBAAmB,GAAG;AAC9D,aAAK,KAAK,mBAAmB;AAAA,MAC/B;AAAA,IACF;AACA,eAAW,QAAS,QAAQ,IAAI;AAAA,EAClC;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,OAAiB,WAA4B;AAClE,QAAM,WAAW,MAAM,KAAK,SAAS;AACrC,MAAI,YAAY,CAAC,MAAM,QAAQ,QAAQ,GAAG;AACxC,WAAO;AAAA,EACT;AACA,QAAM,UAAmB;AAAA,IACvB,OAAO,MAAM,QAAQ,QAAQ,IAAI,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC;AAAA,IAC3D,SAAS,CAAC;AAAA,IACV,SAAS,iBAAiB,SAAS;AAAA,EACrC;AACA,QAAM,KAAK,SAAS,IAAI;AACxB,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAiB,QAAgB,YAA0B;AACnF,QAAM,YAAY,sBAAsB,MAAM;AAC9C,MAAI,CAAC,UAAW;AAChB,QAAM,OAAO,cAAc,OAAO,SAAS;AAC3C,MAAI,CAAC,KAAK,MAAM,SAAS,UAAU,GAAG;AACpC,SAAK,MAAM,KAAK,UAAU;AAAA,EAC5B;AAEA,aAAW,SAAS,iBAAiB,QAAQ,SAAS,GAAG;AACvD,UAAM,WAAW,kBAAkB,KAAK;AACxC,QAAI,CAAC,SAAU;AACf,QAAI,CAAC,KAAK,SAAS,SAAS,QAAQ,GAAG;AACrC,WAAK,UAAU,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAI,KAAK,WAAW,CAAC,GAAI,QAAQ,CAAC,CAAC;AAAA,IACjE;AACA,UAAM,OAAO,MAAM,UAAU,QAAQ,KAAK,CAAC;AAC3C,QAAI,CAAC,KAAK,SAAS,SAAS,GAAG;AAC7B,YAAM,QAAS,QAAQ,IAAI,CAAC,GAAG,MAAM,SAAS;AAAA,IAChD;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB,OAAiB,QAAgB,YAA0B;AACtF,QAAM,YAAY,sBAAsB,MAAM;AAC9C,MAAI,CAAC,UAAW;AAChB,QAAM,OAAO,MAAM,KAAK,SAAS;AACjC,MAAI,CAAC,QAAQ,MAAM,QAAQ,IAAI,EAAG;AAClC,OAAK,QAAQ,KAAK,MAAM,OAAO,CAAC,UAAU,UAAU,UAAU;AAC9D,MAAI,KAAK,MAAM,WAAW,GAAG;AAC3B,WAAO,MAAM,KAAK,SAAS;AAAA,EAC7B;AACF;AAEA,SAAS,oBAAoB,OAAiB,SAA6B;AACzE,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,UAAU,SAAS;AAC5B,UAAM,YAAY,sBAAsB,MAAM;AAC9C,QAAI,aAAa,MAAM,KAAK,SAAS,GAAG;AACtC,iBAAW,IAAI,SAAS;AAAA,IAC1B;AACA,UAAM,WAAW,kBAAkB,MAAM;AACzC,eAAW,YAAY,MAAM,UAAU,QAAQ,KAAK,CAAC,GAAG;AACtD,iBAAW,IAAI,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,aAAa,YAAY;AAClC,aAAS,IAAI,SAAS;AACtB,UAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,GAAG;AAChC,iBAAW,UAAU,KAAK,WAAW,CAAC,GAAG;AACvC,iBAAS,IAAI,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,QAAQ;AAC5B;AAEA,SAAS,YAAY,OAAuB;AAC1C,SAAO,MAAM,QAAQ,OAAO,GAAG,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAChF;AAEA,SAAS,oBAAoB,QAAgB,OAAwB;AACnE,QAAM,SAAS,YAAY,KAAK;AAChC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,mBAAmB,IAAI,kBAAkB,MAAM,CAAC;AACtD,SAAO,iBAAiB,SAAS,IAAI,MAAM,GAAG;AAChD;AAYO,SAAS,YAAY,WAAmB,YAAoB,WAAmB,MAAsB;AAC1G,MAAI;AACF,mBAAe,SAAS;AAExB,UAAM,UAAU,qBAAqB,SAAS;AAC9C,wBAAoB,WAAW,CAAC,UAAU;AACxC,mBAAa,MAAM,OAAO,SAAS,UAAU;AAAA,IAC/C,CAAC;AAED,mBAAe,WAAW,CAAC,UAAU;AACnC,iBAAW,OAAO,MAAM;AACtB,YAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,2BAAiB,OAAO,KAAK,UAAU;AAAA,QACzC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAKO,SAAS,cAAc,WAAmB,YAAoB,WAAmB,MAAsB;AAC5G,MAAI;AACF,mBAAe,SAAS;AAExB,UAAM,UAAU,qBAAqB,SAAS;AAC9C,wBAAoB,WAAW,CAAC,UAAU;AACxC,wBAAkB,MAAM,OAAO,SAAS,UAAU;AAAA,IACpD,CAAC;AAED,mBAAe,WAAW,CAAC,UAAU;AACnC,iBAAW,OAAO,MAAM;AACtB,YAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,8BAAoB,OAAO,KAAK,UAAU;AAAA,QAC5C;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAOO,SAAS,aAAa,WAAyB;AACpD,MAAI;AACF,mBAAe,SAAS;AACxB,wBAAoB,WAAW,CAAC,UAAU;AACxC,YAAM,UAAU;AAChB,YAAM,gBAAgB;AACtB,YAAM,QAAQ,CAAC;AAAA,IACjB,CAAC;AACD,mBAAe,WAAW,CAAC,UAAU;AACnC,YAAM,UAAU;AAChB,YAAM,gBAAgB;AACtB,YAAM,OAAO,CAAC;AACd,YAAM,UAAU,CAAC;AAAA,IACnB,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAMO,SAAS,aAAa,WAA4B;AACvD,MAAI;AACF,WAAU,cAAW,kBAAkB,SAAS,CAAC,KAAQ,cAAW,aAAa,SAAS,CAAC;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,mBACd,WACA,SACM;AACN,MAAI,QAAQ,WAAW,EAAG;AAC1B,MAAI;AACF,mBAAe,SAAS;AAExB,wBAAoB,WAAW,CAAC,UAAU;AACxC,iBAAW,SAAS,SAAS;AAC3B,cAAM,UAAU,qBAAqB,MAAM,SAAS;AACpD,qBAAa,MAAM,OAAO,SAAS,MAAM,IAAI;AAAA,MAC/C;AAAA,IACF,CAAC;AAED,mBAAe,WAAW,CAAC,UAAU;AACnC,iBAAW,SAAS,SAAS;AAC3B,mBAAW,OAAO,MAAM,MAAM;AAC5B,cAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,6BAAiB,OAAO,KAAK,MAAM,IAAI;AAAA,UACzC;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAiBA,eAAsB,sBACpB,WACA,UACA,QAC6B;AAC7B,MAAI;AACF,UAAM,QAAQ,kBAAkB,SAAS;AACzC,QAAI;AACJ,QAAI;AACF,YAAM,MAAS,YAAS,SAAS,OAAO,MAAM;AAAA,IAChD,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,GAAG;AAAA,IACzB,QAAQ;AACN,eAAS,EAAE,SAAS,eAAe,OAAO,CAAC,EAAE;AAAA,IAC/C;AAEA,UAAM,MAAM,UAAU,IAAI,KAAK,KAAK,IAAI,IAAI,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAEjF,UAAM,UAAU,oBAAI,IAAY;AAChC,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,KAAK,GAAG;AACxD,UAAI,QAAQ,YAAY,OAAO,KAAK;AAClC,mBAAW,KAAK,OAAO;AACrB,kBAAQ,IAAI,CAAC;AAAA,QACf;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,iBAAiB,WAAmB,MAA6C;AACrG,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,QAAQ,aAAa,SAAS;AACpC,QAAI;AACJ,QAAI;AACF,YAAM,MAAS,YAAS,SAAS,OAAO,MAAM;AAAA,IAChD,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AACF,eAAS,kBAAkB,KAAK,MAAM,GAAG,CAAa;AAAA,IACxD,QAAQ;AACN,eAAS,EAAE,SAAS,mBAAmB,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IAC/D;AAEA,WAAO,qBAAqB,QAAQ,IAAI;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qBAAqB,OAAiB,MAAoC;AACjF,QAAM,eAAe,oBAAoB,OAAO,IAAI;AACpD,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,aAAa,cAAc;AACpC,UAAM,cAAc,MAAM,KAAK,SAAS;AACxC,UAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,cAAe,aAAa,SAAS,CAAC;AACjF,eAAW,aAAa,OAAO;AAC7B,cAAQ,IAAI,SAAS;AAAA,IACvB;AAAA,EACF;AACA,SAAO,QAAQ,OAAO,IAAI,UAAU;AACtC;AAOO,SAAS,sBAAsB,QAA0B;AAC9D,QAAM,QAAQ,oBAAI,IAAY;AAG9B,QAAM,cAAc,OAAO,SAAS,yBAAyB;AAC7D,aAAW,KAAK,aAAa;AAC3B,UAAM,YAAY,sBAAsB,EAAE,CAAC,CAAC;AAC5C,QAAI,UAAW,OAAM,IAAI,SAAS;AAAA,EACpC;AAEA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,eAAsB,+BACpB,WACA,QAKC;AACD,QAAM,eAAe,sBAAsB,MAAM;AACjD,MAAI;AACF,UAAM,MAAM,MAAS,YAAS,SAAS,aAAa,SAAS,GAAG,MAAM;AACtE,UAAM,WAAW,kBAAkB,KAAK,MAAM,GAAG,CAAa;AAC9D,UAAM,UAAU,IAAI,IAAY,YAAY;AAE5C,eAAW,aAAa,OAAO,KAAK,SAAS,IAAI,GAAG;AAClD,UAAI,oBAAoB,QAAQ,SAAS,GAAG;AAC1C,gBAAQ,IAAI,SAAS;AAAA,MACvB;AAAA,IACF;AACA,eAAW,CAAC,OAAO,UAAU,KAAK,OAAO,QAAQ,SAAS,WAAW,CAAC,CAAC,GAAG;AACxE,UAAI,CAAC,oBAAoB,QAAQ,KAAK,EAAG;AACzC,iBAAW,aAAa,YAAY;AAClC,gBAAQ,IAAI,SAAS;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,eAAe,oBAAoB,UAAU,MAAM,KAAK,OAAO,CAAC;AACtE,UAAM,QAAQ,qBAAqB,UAAU,YAAY;AACzD,WAAO;AAAA,MACL,aAAa,MAAM,KAAK,OAAO;AAAA,MAC/B;AAAA,MACA;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,aAAa,cAAc,cAAc,cAAc,OAAO,KAAK;AAAA,EAC9E;AACF;AAMO,SAAS,gBAAgB,QAAyB;AACvD,SAAO,oiBAAoiB;AAAA,IACziB;AAAA,EACF;AACF;AAMO,SAAS,wBAAwB,QAAgB,QAAgB,KAAK,IAAI,GAAW;AAC1F,QAAM,IAAI,OAAO,YAAY;AAC7B,QAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,MAAI,WAAW;AAEf,MAAI,YAAY,KAAK,CAAC,KAAK,mBAAmB,KAAK,CAAC,KAAK,eAAe,KAAK,CAAC,KAAK,oBAAoB,KAAK,CAAC,GAAG;AAC9G,eAAW;AAAA,EACb,WAAW,gBAAgB,KAAK,CAAC,KAAK,iBAAiB,KAAK,CAAC,GAAG;AAC9D,eAAW;AAAA,EACb,WAAW,gBAAgB,KAAK,CAAC,GAAG;AAClC,eAAW;AAAA,EACb,WAAW,gBAAgB,KAAK,CAAC,GAAG;AAClC,eAAW;AAAA,EACb,WAAW,iBAAiB,KAAK,CAAC,GAAG;AACnC,eAAW;AAAA,EACb,WAAW,iBAAiB,KAAK,CAAC,GAAG;AACnC,eAAW;AAAA,EACb,WAAW,gBAAgB,KAAK,CAAC,GAAG;AAElC,UAAM,OAAO,IAAI,KAAK,IAAI,YAAY,GAAG,GAAG,CAAC;AAC7C,WAAO,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EACvC,WAAW,gBAAgB,KAAK,CAAC,GAAG;AAClC,UAAM,eAAe,IAAI,KAAK,IAAI,YAAY,IAAI,GAAG,GAAG,CAAC;AACzD,WAAO,aAAa,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EAC/C,OAAO;AAEL,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,aAAa,EAAE;AAAA,MACnB;AAAA,IACF;AACA,QAAI,YAAY;AACd,YAAM,WAAW,WAAW,QAAQ,WAAW,CAAC,CAAC;AACjD,YAAM,OAAO,WAAW,CAAC,IAAI,OAAO,SAAS,WAAW,CAAC,GAAG,EAAE,IAAI,IAAI,YAAY;AAQlF,UAAI,aAAa,KAAK,CAAC,GAAG;AACxB,YAAI,WAAW,CAAC,GAAG;AAEjB,gBAAM,aAAa,IAAI,KAAK,MAAM,UAAU,CAAC;AAC7C,iBAAO,IAAI,KAAK,WAAW,QAAQ,IAAI,MAAM,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,QACpF;AACA,mBAAW;AAAA,MACb,OAAO;AACL,cAAM,aAAa,IAAI,KAAK,MAAM,UAAU,CAAC;AAC7C,eAAO,WAAW,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,MAC7C;AAAA,IACF;AAGA,UAAM,YAAY,EAAE,MAAM,0BAA0B;AACpD,QAAI,WAAW;AACb,iBAAW,KAAK,IAAI,KAAK,OAAO,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;AAAA,IAChE,OAAO;AAEL,YAAM,iBAAiB,EAAE,MAAM,2BAA2B;AAC1D,UAAI,gBAAgB;AAClB,mBAAW,KAAK,IAAI,KAAK,OAAO,SAAS,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE;AAAA,MACtE,OAAO;AACL,cAAM,WAAW,EAAE,MAAM,yBAAyB;AAClD,YAAI,UAAU;AACZ,qBAAW,KAAK,IAAI,KAAK,OAAO,SAAS,SAAS,CAAC,GAAG,EAAE,CAAC;AAAA,QAC3D,OAAO;AACL,gBAAM,UAAU,EAAE,MAAM,0BAA0B;AAClD,cAAI,SAAS;AAEX,uBAAW,KAAK,IAAI,GAAG,KAAK,KAAK,OAAO,SAAS,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;AAAA,UACxE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,WAAW,EAAE,MAAM,yBAAyB;AAClD,QAAI,UAAU;AACZ,aAAO,GAAG,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;AAAA,IACrD;AACA,UAAM,UAAU,EAAE,MAAM,iCAAiC;AACzD,QAAI,SAAS;AACX,YAAM,OAAO,QAAQ,CAAC,EAAE,WAAW,IAAI,MAAO,OAAO,SAAS,QAAQ,CAAC,GAAG,EAAE,IAAI,OAAO,SAAS,QAAQ,CAAC,GAAG,EAAE;AAC9G,aAAO,GAAG,IAAI,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IAC9E;AAGA,UAAM,iBAAiB,EAAE,MAAM,uEAAuE;AACtG,QAAI,gBAAgB;AAClB,YAAM,WAAW,CAAC,UAAU,UAAU,WAAW,aAAa,YAAY,UAAU,UAAU;AAC9F,YAAM,YAAY,SAAS,QAAQ,eAAe,CAAC,CAAC;AACpD,YAAM,aAAa,IAAI,OAAO;AAC9B,kBAAY,aAAa,YAAY,KAAK,KAAK;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,KAAK,QAAQ,WAAW,KAAK,KAAK,KAAK,GAAI;AAC5D,SAAO,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AACvC;AAuBO,SAAS,8BACd,QACA,QAAgB,KAAK,IAAI,GACa;AACtC,QAAM,WAAW,wBAAwB,QAAQ,KAAK;AACtD,QAAM,IAAI,OAAO,YAAY;AAC7B,QAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,QAAM,QAAQ,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE;AAC3C,QAAM,WAAW,IAAI,KAAK,QAAQ,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAEvE,MAAI;AAEJ,MAAI,4DAA4D,KAAK,CAAC,GAAG;AACvE,aAAS;AAAA,EACX,WAAW,+BAA+B,KAAK,CAAC,GAAG;AACjD,aAAS;AAAA,EACX,WAAW,6CAA6C,KAAK,CAAC,GAAG;AAC/D,aAAS;AAAA,EACX,WAAW,gBAAgB,KAAK,CAAC,GAAG;AAClC,aAAS,IAAI,KAAK,QAAQ,IAAI,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EACrE,WAAW,iBAAiB,KAAK,CAAC,GAAG;AACnC,aAAS,IAAI,KAAK,QAAQ,KAAK,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EACtE,WAAW,gBAAgB,KAAK,CAAC,GAAG;AAClC,aAAS,GAAG,IAAI,YAAY,CAAC;AAAA,EAC/B,OAAO;AAML,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,aAAa,EAAE;AAAA,MACnB;AAAA,IACF;AACA,QAAI,YAAY;AAId,YAAM,WAAW,WAAW,QAAQ,WAAW,CAAC,CAAC;AACjD,YAAM,OAAO,WAAW,CAAC,IAAI,OAAO,SAAS,WAAW,CAAC,GAAG,EAAE,IAAI,IAAI,YAAY;AAClF,YAAM,iBAAiB,oBAAoB,KAAK,CAAC;AACjD,YAAM,WAAW,aAAa,KAAK,CAAC;AACpC,UAAI,gBAAgB;AAClB,iBAAS;AAAA,MACX,WAAW,UAAU;AAGnB,iBAAS,IAAI,KAAK,MAAM,UAAU,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,MAChE,OAAO;AAEL,iBAAS,IAAI,KAAK,MAAM,WAAW,GAAG,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,MACpE;AAAA,IACF,OAAO;AAIL,UAAI,aAAa;AACjB,YAAM,YAAY,EAAE,MAAM,0BAA0B;AACpD,UAAI,WAAW;AACb,qBAAa,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,SAAS,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI;AAAA,MAClF,OAAO;AACL,cAAM,iBAAiB,EAAE,MAAM,2BAA2B;AAC1D,YAAI,gBAAgB;AAClB,uBAAa,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,SAAS,eAAe,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI;AAAA,QACvF,OAAO;AACL,gBAAM,WAAW,EAAE,MAAM,yBAAyB;AAClD,cAAI,UAAU;AAGZ,yBAAa,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,OAAO,SAAS,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;AAAA,UAC9E,OAAO;AACL,kBAAM,UAAU,EAAE,MAAM,0BAA0B;AAClD,gBAAI,SAAS;AAGX,2BAAa;AAAA,YACf;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAIA,YAAM,WAAW,EAAE,MAAM,yBAAyB;AAClD,UAAI,UAAU;AAEZ,cAAM,IAAI,oBAAI,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,YAAY;AAC3E,iBAAS,IAAI,KAAK,EAAE,QAAQ,IAAI,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,MACvE,OAAO;AACL,cAAM,UAAU,EAAE,MAAM,iCAAiC;AACzD,YAAI,SAAS;AACX,gBAAM,OACJ,QAAQ,CAAC,EAAE,WAAW,IAAI,MAAO,OAAO,SAAS,QAAQ,CAAC,GAAG,EAAE,IAAI,OAAO,SAAS,QAAQ,CAAC,GAAG,EAAE;AAEnG,gBAAM,IAAI,oBAAI,KAAK,GAAG,IAAI,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,YAAY;AACpG,mBAAS,IAAI,KAAK,EAAE,QAAQ,IAAI,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,QACvE,OAAO;AACL,gBAAM,iBAAiB,EAAE,MAAM,uEAAuE;AACtG,cAAI,gBAAgB;AAClB,kBAAM,WAAW,CAAC,UAAU,UAAU,WAAW,aAAa,YAAY,UAAU,UAAU;AAC9F,kBAAM,YAAY,SAAS,QAAQ,eAAe,CAAC,CAAC;AACpD,kBAAM,aAAa,IAAI,OAAO;AAC9B,kBAAM,YAAY,aAAa,YAAY,KAAK,KAAK;AAErD,qBAAS,IAAI,KAAK,SAAS,WAAW,KAAK,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,UAClF,OAAO;AAKL,qBAAS,aAAa,IAAI,WAAW,IAAI,KAAK,QAAQ,aAAa,KAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,UAC1G;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAKA,MAAI,UAAU,SAAU,UAAS;AAEjC,SAAO,EAAE,UAAU,OAAO;AAC5B;","names":[]}