@remnic/core 9.3.741 → 9.3.742

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 (72) hide show
  1. package/dist/access-boundary.d.ts +2 -2
  2. package/dist/access-boundary.js +2 -2
  3. package/dist/access-cli.js +8 -8
  4. package/dist/access-http.d.ts +2 -2
  5. package/dist/access-http.js +6 -6
  6. package/dist/access-mcp.d.ts +2 -2
  7. package/dist/access-mcp.js +5 -5
  8. package/dist/access-operations-batch.js +3 -3
  9. package/dist/access-operations.d.ts +2 -2
  10. package/dist/access-operations.js +4 -4
  11. package/dist/{access-service-Cbk-Kv_A.d.ts → access-service-K8n12dXu.d.ts} +1 -1
  12. package/dist/access-service.d.ts +2 -2
  13. package/dist/access-service.js +1 -1
  14. package/dist/access-surface-catalog.d.ts +2 -2
  15. package/dist/{auto-sync-G3V5AN4C.js → auto-sync-XHSKKQAH.js} +2 -2
  16. package/dist/bootstrap.d.ts +1 -1
  17. package/dist/{chunk-DBVBV2RF.js → chunk-7ADHJ6SU.js} +3 -3
  18. package/dist/{chunk-VC7QELR6.js → chunk-7HUGLMSW.js} +346 -305
  19. package/dist/chunk-7HUGLMSW.js.map +1 -0
  20. package/dist/{chunk-QCO2QFLZ.js → chunk-AM7YKD7V.js} +3 -2
  21. package/dist/{chunk-QCO2QFLZ.js.map → chunk-AM7YKD7V.js.map} +1 -1
  22. package/dist/{chunk-OZECYVGI.js → chunk-C7LDPZJJ.js} +6 -4
  23. package/dist/{chunk-OZECYVGI.js.map → chunk-C7LDPZJJ.js.map} +1 -1
  24. package/dist/{chunk-4J53ZVPQ.js → chunk-EZZUNKMJ.js} +3 -3
  25. package/dist/{chunk-H3WSZWHK.js → chunk-GFJ5ABKS.js} +7 -6
  26. package/dist/chunk-GFJ5ABKS.js.map +1 -0
  27. package/dist/{chunk-LD6A7XG5.js → chunk-JMMSVRF4.js} +4 -4
  28. package/dist/{chunk-7Y5KKMIP.js → chunk-NHJYUIII.js} +3 -2
  29. package/dist/{chunk-7Y5KKMIP.js.map → chunk-NHJYUIII.js.map} +1 -1
  30. package/dist/{chunk-DGVM5SFL.js → chunk-PHRLJFWZ.js} +3 -2
  31. package/dist/{chunk-DGVM5SFL.js.map → chunk-PHRLJFWZ.js.map} +1 -1
  32. package/dist/{chunk-M5KLYHHI.js → chunk-QUNH5UZH.js} +2 -2
  33. package/dist/{chunk-3TUDKDTQ.js → chunk-UEIF7RWQ.js} +3 -3
  34. package/dist/{chunk-773DPI6T.js → chunk-V5RVMULT.js} +11 -8
  35. package/dist/chunk-V5RVMULT.js.map +1 -0
  36. package/dist/{chunk-XPOOY35N.js → chunk-W4H5A3TJ.js} +3 -2
  37. package/dist/chunk-W4H5A3TJ.js.map +1 -0
  38. package/dist/{cli-BQW2xKdb.d.ts → cli-BEOLy1xo.d.ts} +2 -2
  39. package/dist/cli.d.ts +3 -3
  40. package/dist/cli.js +8 -8
  41. package/dist/explicit-capture.d.ts +1 -1
  42. package/dist/graph-recall.js +1 -1
  43. package/dist/index.d.ts +4 -4
  44. package/dist/index.js +13 -13
  45. package/dist/lcm/engine.js +2 -2
  46. package/dist/lcm/index.js +2 -2
  47. package/dist/lcm/summarizer.js +1 -1
  48. package/dist/mcp-memory-inspector-app.d.ts +2 -2
  49. package/dist/operator-toolkit.js +1 -1
  50. package/dist/{orchestrator-BOAFN_Yr.d.ts → orchestrator-Dr-1v5EL.d.ts} +58 -0
  51. package/dist/orchestrator.d.ts +1 -1
  52. package/dist/orchestrator.js +4 -4
  53. package/package.json +2 -2
  54. package/src/cli.ts +2 -1
  55. package/src/graph-recall.ts +2 -1
  56. package/src/lcm/engine.ts +9 -6
  57. package/src/lcm/summarizer.ts +2 -1
  58. package/src/operator-toolkit.ts +2 -1
  59. package/src/orchestration/qmd-result-resolver.ts +377 -0
  60. package/src/orchestrator.ts +14 -279
  61. package/src/procedural/procedure-stats.ts +2 -1
  62. package/src/wearables/pipeline.ts +5 -3
  63. package/dist/chunk-773DPI6T.js.map +0 -1
  64. package/dist/chunk-H3WSZWHK.js.map +0 -1
  65. package/dist/chunk-VC7QELR6.js.map +0 -1
  66. package/dist/chunk-XPOOY35N.js.map +0 -1
  67. /package/dist/{auto-sync-G3V5AN4C.js.map → auto-sync-XHSKKQAH.js.map} +0 -0
  68. /package/dist/{chunk-DBVBV2RF.js.map → chunk-7ADHJ6SU.js.map} +0 -0
  69. /package/dist/{chunk-4J53ZVPQ.js.map → chunk-EZZUNKMJ.js.map} +0 -0
  70. /package/dist/{chunk-LD6A7XG5.js.map → chunk-JMMSVRF4.js.map} +0 -0
  71. /package/dist/{chunk-M5KLYHHI.js.map → chunk-QUNH5UZH.js.map} +0 -0
  72. /package/dist/{chunk-3TUDKDTQ.js.map → chunk-UEIF7RWQ.js.map} +0 -0
@@ -104,6 +104,7 @@ import { EntitySynthesisCoordinator } from "./orchestration/entity-synthesis-coo
104
104
  import { RecallResultFormatter } from "./orchestration/recall-result-formatter.js";
105
105
  import { ConversationIndexCoordinator } from "./orchestration/conversation-index-coordinator.js";
106
106
  import { RecallRerankCoordinator } from "./orchestration/recall-rerank-coordinator.js";
107
+ import { QmdResultResolver, qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
107
108
  export { hasIdentityRecoveryIntent, resolveEffectiveIdentityInjectionMode } from "./orchestration/recall-result-formatter.js";
108
109
  import {
109
110
  runLiveConnectorsOnce,
@@ -738,52 +739,6 @@ async function raceRecallAbort<T>(
738
739
  }
739
740
  }
740
741
 
741
- function qmdCollectionPathParts(resultPath: string): {
742
- collection: string;
743
- relativePath: string;
744
- } | null {
745
- if (!resultPath || path.isAbsolute(resultPath)) return null;
746
- const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
747
- const slashIndex = normalized.indexOf("/");
748
- if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
749
- const collection = normalized.slice(0, slashIndex);
750
- if (/^\d{4}-\d{2}-\d{2}$/.test(collection)) return null;
751
- return {
752
- collection,
753
- relativePath: normalized.slice(slashIndex + 1),
754
- };
755
- }
756
-
757
- function qmdResultPathCandidates(
758
- storageDir: string,
759
- resultPath: string,
760
- ): string[] {
761
- const candidates = new Set<string>();
762
- const storageRoot = path.resolve(storageDir);
763
- const addCandidate = (candidate: string) => {
764
- const resolved = path.resolve(candidate);
765
- if (isPathInsideStorageRoot(storageRoot, resolved)) {
766
- candidates.add(resolved);
767
- }
768
- };
769
- const addRelativeCandidates = (relativePath: string) => {
770
- const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
771
- if (!normalized) return;
772
- addCandidate(path.join(storageRoot, normalized));
773
- if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
774
- addCandidate(path.join(storageRoot, "facts", normalized));
775
- }
776
- };
777
-
778
- if (path.isAbsolute(resultPath)) {
779
- addCandidate(resultPath);
780
- } else {
781
- addRelativeCandidates(resultPath);
782
- }
783
-
784
- return [...candidates];
785
- }
786
-
787
742
  /** Maximum age (ms) before a compaction-reset signal file is considered stale and removed. */
788
743
  const COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
789
744
  const DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS = 10_000;
@@ -1885,6 +1840,7 @@ export class Orchestrator {
1885
1840
  */
1886
1841
  readonly conversationIndexCoordinator: ConversationIndexCoordinator;
1887
1842
  readonly recallRerankCoordinator: RecallRerankCoordinator;
1843
+ readonly qmdResultResolver: QmdResultResolver;
1888
1844
  private heartbeatObserverChains = new Map<string, Promise<void>>();
1889
1845
  private recentExtractionFingerprints = new Map<string, number>();
1890
1846
  private readonly consolidationObservers = new Set<
@@ -2913,6 +2869,13 @@ export class Orchestrator {
2913
2869
  fastChatCompletion: (messages, options) =>
2914
2870
  this.fastChatCompletion(messages, options),
2915
2871
  });
2872
+ this.qmdResultResolver = new QmdResultResolver({
2873
+ getConfig: () => this.config,
2874
+ storageFor: (namespace) => this.storageRouter.storageFor(namespace),
2875
+ storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
2876
+ qmdCollectionNamespaceFromPrefix: (prefix) => this.qmdCollectionNamespaceFromPrefix(prefix),
2877
+ namespaceFromPath: (p) => this.namespaceFromPath(p),
2878
+ });
2916
2879
 
2917
2880
  this.sessionObserver = new SessionObserverState({
2918
2881
  memoryDir: config.memoryDir,
@@ -16898,143 +16861,14 @@ export class Orchestrator {
16898
16861
  return null;
16899
16862
  }
16900
16863
 
16864
+ // Issue #1526 seam 11: QMD result-resolution methods moved to QmdResultResolver.
16865
+ // Thin delegation keeps the private API stable for callers + tests.
16901
16866
  private async readQmdResultMemory(
16902
16867
  resultPath: string,
16903
16868
  fallbackStorage: StorageManager,
16904
16869
  recallNamespaces: readonly string[] = [],
16905
16870
  ): Promise<MemoryFile | null> {
16906
- const parts = qmdCollectionPathParts(resultPath);
16907
- const storageDirFor = (storage: StorageManager): string | null =>
16908
- typeof (storage as { dir?: unknown }).dir === "string" &&
16909
- (storage as { dir?: string }).dir
16910
- ? (storage as { dir: string }).dir
16911
- : null;
16912
- const fallbackStorageDir = storageDirFor(fallbackStorage);
16913
- const coldCollection = this.config.qmdColdCollection ?? "openclaw-engram-cold";
16914
- if (parts && parts.collection === coldCollection) {
16915
- const storages: StorageManager[] = [];
16916
- const seenStorageDirs = new Set<string>();
16917
- const addStorage = (storage: StorageManager): void => {
16918
- const storageDir = storageDirFor(storage);
16919
- const storageKey = storageDir
16920
- ? path.resolve(storageDir)
16921
- : `storage-without-dir-${storages.length}`;
16922
- if (seenStorageDirs.has(storageKey)) return;
16923
- seenStorageDirs.add(storageKey);
16924
- storages.push(storage);
16925
- };
16926
-
16927
- const fallbackNamespace =
16928
- fallbackStorageDir !== null
16929
- ? this.storageDirNamespace(fallbackStorageDir)
16930
- : this.config.defaultNamespace;
16931
- if (
16932
- recallNamespaces.length === 0 ||
16933
- !resolveNamespaceCapabilities(this.config).namespaces ||
16934
- recallNamespaces.includes(fallbackNamespace)
16935
- ) {
16936
- addStorage(fallbackStorage);
16937
- }
16938
-
16939
- if (recallNamespaces.length > 0) {
16940
- for (const namespace of recallNamespaces) {
16941
- try {
16942
- addStorage(await this.storageRouter.storageFor(namespace));
16943
- } catch (err) {
16944
- log.debug("qmd cold result namespace storage lookup skipped", {
16945
- path: resultPath,
16946
- namespace,
16947
- error: (err as Error).message,
16948
- });
16949
- }
16950
- }
16951
- }
16952
-
16953
- for (const storage of storages) {
16954
- const storageDir = storageDirFor(storage);
16955
- if (!storageDir) {
16956
- const memory = await storage.readMemoryByPath(resultPath);
16957
- if (memory) return memory;
16958
- continue;
16959
- }
16960
- try {
16961
- const coldRoot = path.join(storageDir, "cold");
16962
- for (const candidate of qmdResultPathCandidates(
16963
- coldRoot,
16964
- parts.relativePath,
16965
- )) {
16966
- const memory = await storage.readMemoryByPath(candidate);
16967
- if (memory) return memory;
16968
- }
16969
- } catch (err) {
16970
- if (err instanceof SecureStoreLockedError) throw err;
16971
- log.debug("qmd cold result path lookup failed open", {
16972
- path: resultPath,
16973
- collection: coldCollection,
16974
- error: (err as Error).message,
16975
- });
16976
- }
16977
- }
16978
- return null;
16979
- }
16980
- const collectionNamespace = parts
16981
- ? this.qmdCollectionNamespaceFromPrefix(parts.collection)
16982
- : null;
16983
-
16984
- if (parts && collectionNamespace) {
16985
- try {
16986
- const collectionStorage =
16987
- await this.storageRouter.storageFor(collectionNamespace);
16988
- for (const candidate of qmdResultPathCandidates(
16989
- collectionStorage.dir,
16990
- parts.relativePath,
16991
- )) {
16992
- const memory = await collectionStorage.readMemoryByPath(candidate);
16993
- if (memory) return memory;
16994
- }
16995
- return null;
16996
- } catch (err) {
16997
- if (err instanceof SecureStoreLockedError) throw err;
16998
- log.debug("qmd result namespace path lookup failed open", {
16999
- path: resultPath,
17000
- namespace: collectionNamespace,
17001
- error: (err as Error).message,
17002
- });
17003
- return null;
17004
- }
17005
- }
17006
-
17007
- if (path.isAbsolute(resultPath)) {
17008
- if (!fallbackStorageDir) {
17009
- return await fallbackStorage.readMemoryByPath(resultPath);
17010
- }
17011
- const ownerStorage = await this.storageForAbsoluteQmdResultPath(
17012
- resultPath,
17013
- fallbackStorage,
17014
- recallNamespaces,
17015
- );
17016
- if (!ownerStorage) return null;
17017
- for (const candidate of qmdResultPathCandidates(
17018
- ownerStorage.dir,
17019
- resultPath,
17020
- )) {
17021
- const memory = await ownerStorage.storage.readMemoryByPath(candidate);
17022
- if (memory) return memory;
17023
- }
17024
- return null;
17025
- }
17026
-
17027
- if (!fallbackStorageDir) {
17028
- return await fallbackStorage.readMemoryByPath(resultPath);
17029
- }
17030
- for (const candidate of qmdResultPathCandidates(
17031
- fallbackStorageDir,
17032
- resultPath,
17033
- )) {
17034
- const memory = await fallbackStorage.readMemoryByPath(candidate);
17035
- if (memory) return memory;
17036
- }
17037
- return null;
16871
+ return this.qmdResultResolver.readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces);
17038
16872
  }
17039
16873
 
17040
16874
  private async resolveColdQmdResultForRecall(
@@ -17042,40 +16876,7 @@ export class Orchestrator {
17042
16876
  fallbackStorage: StorageManager,
17043
16877
  recallNamespaces: readonly string[] = [],
17044
16878
  ): Promise<{ namespace: string; result: QmdSearchResult } | null> {
17045
- const memory = await this.readQmdResultMemory(
17046
- result.path,
17047
- fallbackStorage,
17048
- recallNamespaces,
17049
- );
17050
- if (!memory) return null;
17051
-
17052
- let ownerNamespace: string | null = null;
17053
- if (path.isAbsolute(memory.path)) {
17054
- const ownerStorage = await this.storageForAbsoluteQmdResultPath(
17055
- memory.path,
17056
- fallbackStorage,
17057
- recallNamespaces,
17058
- );
17059
- ownerNamespace = ownerStorage?.namespace ?? null;
17060
- if (!ownerNamespace && resolveNamespaceCapabilities(this.config).namespaces) return null;
17061
- }
17062
- ownerNamespace ??= this.namespaceFromPath(memory.path);
17063
- if (
17064
- recallNamespaces.length > 0 &&
17065
- !recallNamespaces.includes(ownerNamespace)
17066
- ) {
17067
- return null;
17068
- }
17069
-
17070
- return {
17071
- namespace: ownerNamespace,
17072
- result: {
17073
- ...result,
17074
- docid: result.docid || memory.frontmatter.id,
17075
- path: memory.path,
17076
- snippet: result.snippet || memory.content.slice(0, 400),
17077
- },
17078
- };
16879
+ return this.qmdResultResolver.resolveColdQmdResultForRecall(result, fallbackStorage, recallNamespaces);
17079
16880
  }
17080
16881
 
17081
16882
  private async storageForAbsoluteQmdResultPath(
@@ -17083,73 +16884,7 @@ export class Orchestrator {
17083
16884
  fallbackStorage: StorageManager,
17084
16885
  recallNamespaces: readonly string[] = [],
17085
16886
  ): Promise<{ storage: StorageManager; dir: string; namespace: string } | null> {
17086
- const resolvedPath = path.resolve(resultPath);
17087
- const memoryRoot = path.resolve(this.config.memoryDir);
17088
- const namespacesRoot = path.join(memoryRoot, "namespaces");
17089
- const fallbackStorageDir =
17090
- typeof (fallbackStorage as { dir?: unknown }).dir === "string" &&
17091
- (fallbackStorage as { dir?: string }).dir
17092
- ? (fallbackStorage as { dir: string }).dir
17093
- : null;
17094
- const matches: Array<{ storage: StorageManager; dir: string; namespace: string }> = [];
17095
- const seenDirs = new Set<string>();
17096
-
17097
- const maybeAddStorage = (storage: StorageManager, namespace: string) => {
17098
- const storageDir =
17099
- typeof (storage as { dir?: unknown }).dir === "string" &&
17100
- (storage as { dir?: string }).dir
17101
- ? (storage as { dir: string }).dir
17102
- : null;
17103
- if (!storageDir) return;
17104
- const candidateRoot = path.resolve(storageDir);
17105
- if (seenDirs.has(candidateRoot)) return;
17106
- if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
17107
- if (
17108
- candidateRoot === memoryRoot &&
17109
- isPathInsideStorageRoot(namespacesRoot, resolvedPath)
17110
- ) {
17111
- return;
17112
- }
17113
- seenDirs.add(candidateRoot);
17114
- matches.push({ storage, dir: candidateRoot, namespace });
17115
- };
17116
-
17117
- const fallbackNamespace =
17118
- fallbackStorageDir !== null
17119
- ? this.storageDirNamespace(fallbackStorageDir)
17120
- : this.config.defaultNamespace;
17121
- maybeAddStorage(fallbackStorage, fallbackNamespace);
17122
-
17123
- const candidateNamespaces = new Set<string>();
17124
- candidateNamespaces.add(this.config.defaultNamespace);
17125
- candidateNamespaces.add(this.config.sharedNamespace);
17126
- for (const ns of recallNamespaces) {
17127
- candidateNamespaces.add(ns);
17128
- }
17129
- if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
17130
- const relativeToNamespaces = path.relative(namespacesRoot, resolvedPath);
17131
- const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
17132
- if (namespaceSegment) {
17133
- candidateNamespaces.add(
17134
- namespaceIdentityFromToken(namespaceSegment) ?? namespaceSegment,
17135
- );
17136
- }
17137
- }
17138
- for (const policy of this.config.namespacePolicies ?? []) {
17139
- candidateNamespaces.add(policy.name);
17140
- }
17141
-
17142
- for (const ns of candidateNamespaces) {
17143
- if (!ns) continue;
17144
- try {
17145
- maybeAddStorage(await this.storageRouter.storageFor(ns), ns);
17146
- } catch {
17147
- continue;
17148
- }
17149
- }
17150
-
17151
- matches.sort((a, b) => b.dir.length - a.dir.length);
17152
- return matches[0] ?? null;
16887
+ return this.qmdResultResolver.storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces);
17153
16888
  }
17154
16889
 
17155
16890
  // Issue #1526: recall-rerank methods moved to RecallRerankCoordinator.
@@ -180,6 +180,7 @@ export async function computeProcedureStats(options: {
180
180
  */
181
181
  export function formatProcedureStatsText(report: ProcedureStatsReport): string {
182
182
  const { counts, recent, config } = report;
183
+ const { autoPromoteEnabled } = config;
183
184
  const lines: string[] = [];
184
185
  lines.push(`Procedural memory stats (schema v${report.schemaVersion})`);
185
186
  lines.push(` generated: ${report.generatedAt}`);
@@ -189,7 +190,7 @@ export function formatProcedureStatsText(report: ProcedureStatsReport): string {
189
190
  lines.push(` minOccurrences: ${config.minOccurrences}`);
190
191
  lines.push(` successFloor: ${config.successFloor}`);
191
192
  lines.push(` autoPromoteOccurrences: ${config.autoPromoteOccurrences}`);
192
- lines.push(` autoPromoteEnabled: ${config.autoPromoteEnabled}`);
193
+ lines.push(` autoPromoteEnabled: ${autoPromoteEnabled}`);
193
194
  lines.push(` lookbackDays: ${config.lookbackDays}`);
194
195
  lines.push(` recallMaxProcedures: ${config.recallMaxProcedures}`);
195
196
  lines.push("");
@@ -260,6 +260,7 @@ function cleanDay(
260
260
  userRedaction: RegExp[],
261
261
  correctionRules: CompiledCorrectionRule[],
262
262
  ): CleanedDay {
263
+ const { offTheRecordEnabled, redactionEnabled } = config;
263
264
  const out: CleanedDay = {
264
265
  conversations: [],
265
266
  segmentsKept: 0,
@@ -269,7 +270,7 @@ function cleanDay(
269
270
  };
270
271
  for (const conversation of raw) {
271
272
  let current = conversation;
272
- if (config.offTheRecordEnabled) {
273
+ if (offTheRecordEnabled) {
273
274
  const otr = applyOffTheRecord(current);
274
275
  current = otr.conversation;
275
276
  out.segmentsDropped += otr.droppedSegments;
@@ -280,7 +281,7 @@ function cleanDay(
280
281
 
281
282
  const segments = current.segments.map((segment) => {
282
283
  let text = segment.text;
283
- if (config.redactionEnabled) {
284
+ if (redactionEnabled) {
284
285
  const redacted = redactText(text, userRedaction);
285
286
  text = redacted.text;
286
287
  out.redactions += redacted.redactions;
@@ -483,7 +484,8 @@ export async function syncWearableSource(
483
484
  // the day to re-run on the next sync (Cursor review on PR
484
485
  // #1462).
485
486
  passClean = generated.completed;
486
- if (config.digestEnabled) {
487
+ const { digestEnabled } = config;
488
+ if (digestEnabled) {
487
489
  const wrote = await writeDailyDigestMemory(
488
490
  connector.id,
489
491
  date,
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lcm/engine.ts"],"sourcesContent":["import type Database from \"better-sqlite3\";\nimport { openLcmDatabase, ensureLcmStateDir } from \"./schema.js\";\nimport { LcmArchive, type LcmStructuredRecallMatch } from \"./archive.js\";\nimport { LcmDag } from \"./dag.js\";\nimport { LcmSummarizer, type SummarizeFn } from \"./summarizer.js\";\nimport { assembleCompressedHistory, type LcmRecallConfig } from \"./recall.js\";\nimport { LcmWorkQueue, type LcmObserveMessage } from \"./queue.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { log } from \"../logger.js\";\n\nexport interface LcmEngineConfig {\n enabled: boolean;\n leafBatchSize: number;\n rollupFanIn: number;\n freshTailTurns: number;\n maxDepth: number;\n deterministicMaxTokens: number;\n archiveRetentionDays: number;\n recallBudgetShare: number;\n observeConcurrency: number;\n telemetryPrefilterEnabled: boolean;\n messagePartsEnabled: boolean;\n messagePartsRecallMaxResults: number;\n}\n\nfunction positiveInteger(value: unknown, fallback: number, min = 1): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n return Math.max(min, Math.floor(value));\n}\n\nexport function extractLcmConfig(cfg: PluginConfig): LcmEngineConfig {\n return {\n enabled: (cfg as any).lcmEnabled === true,\n leafBatchSize: positiveInteger((cfg as any).lcmLeafBatchSize, 8),\n rollupFanIn: positiveInteger((cfg as any).lcmRollupFanIn, 4, 2),\n freshTailTurns: positiveInteger((cfg as any).lcmFreshTailTurns, 16),\n maxDepth: positiveInteger((cfg as any).lcmMaxDepth, 5),\n deterministicMaxTokens: positiveInteger((cfg as any).lcmDeterministicMaxTokens, 512),\n archiveRetentionDays: positiveInteger((cfg as any).lcmArchiveRetentionDays, 90, 0),\n recallBudgetShare: (cfg as any).lcmRecallBudgetShare ?? 0.15,\n observeConcurrency:\n typeof (cfg as any).lcmObserveConcurrency === \"number\" &&\n Number.isFinite((cfg as any).lcmObserveConcurrency)\n ? Math.max(1, Math.floor((cfg as any).lcmObserveConcurrency))\n : 1,\n telemetryPrefilterEnabled: (cfg as any).lcmTelemetryPrefilterEnabled !== false,\n messagePartsEnabled: (cfg as any).messagePartsEnabled === true,\n messagePartsRecallMaxResults:\n typeof (cfg as any).messagePartsRecallMaxResults === \"number\"\n ? Math.max(0, Math.floor((cfg as any).messagePartsRecallMaxResults))\n : 6,\n };\n}\n\nfunction normalizeLcmSessionId(sessionId: string): string {\n return sessionId.trim();\n}\n\nfunction normalizeOptionalLcmSessionId(sessionId?: string): string | undefined {\n if (sessionId === undefined) {\n return undefined;\n }\n const normalized = normalizeLcmSessionId(sessionId);\n return normalized.length > 0 ? normalized : undefined;\n}\n\nconst EMPTY_LCM_STATS = {\n totalMessages: 0,\n totalSummaryNodes: 0,\n maxDepth: -1,\n} as const;\n\nexport class LcmEngine {\n private db: Database.Database | null = null;\n private archive: LcmArchive | null = null;\n private dag: LcmDag | null = null;\n private summarizer: LcmSummarizer | null = null;\n private observeQueue: LcmWorkQueue | null = null;\n private closed = false;\n private readonly config: LcmEngineConfig;\n private readonly memoryDir: string;\n private initPromise: Promise<void> | null = null;\n private readonly pendingObserveInitCounts = new Map<string, number>();\n private readonly pendingObserveInitWaiters = new Map<string, Array<() => void>>();\n private readonly pendingObserveInitIdleWaiters: Array<() => void> = [];\n\n constructor(\n pluginConfig: PluginConfig,\n private readonly summarizeFn: SummarizeFn,\n ) {\n this.config = extractLcmConfig(pluginConfig);\n this.memoryDir = pluginConfig.memoryDir;\n }\n\n get enabled(): boolean {\n return this.config.enabled;\n }\n\n /** Lazy init — open database on first use. */\n async ensureInitialized(): Promise<void> {\n if (this.closed) return;\n if (this.db) return;\n if (this.initPromise) {\n await this.initPromise;\n return;\n }\n this.initPromise = this.doInit().catch((err) => {\n // Reset so next call retries instead of caching the failure\n this.initPromise = null;\n throw err;\n });\n await this.initPromise;\n }\n\n private async doInit(): Promise<void> {\n await ensureLcmStateDir(this.memoryDir);\n const db = openLcmDatabase(this.memoryDir);\n const archive = new LcmArchive(db);\n const dag = new LcmDag(db);\n const summarizer = new LcmSummarizer(\n archive,\n dag,\n this.summarizeFn,\n {\n leafBatchSize: this.config.leafBatchSize,\n rollupFanIn: this.config.rollupFanIn,\n maxDepth: this.config.maxDepth,\n deterministicMaxTokens: this.config.deterministicMaxTokens,\n telemetryPrefilterEnabled: this.config.telemetryPrefilterEnabled,\n },\n );\n const observeQueue = new LcmWorkQueue({\n concurrency: this.config.observeConcurrency,\n worker: async (sessionId, messages) => {\n await this.processObserveMessages(sessionId, messages);\n },\n hooks: {\n onJobStart: ({ sessionId, depth, inFlight, waitMs }) => {\n log.debug(\n `LCM observe queue start: session=${sessionId}, depth=${depth}, inFlight=${inFlight}, wait=${waitMs}ms`,\n );\n },\n onJobFinish: ({\n sessionId,\n depth,\n inFlight,\n waitMs,\n runMs,\n totalMs,\n error,\n }) => {\n if (error) {\n log.error(\n `LCM observe queue failure: session=${sessionId}, depth=${depth}, inFlight=${inFlight}, wait=${waitMs}ms, run=${runMs}ms, total=${totalMs}ms, error=${error}`,\n );\n return;\n }\n\n log.debug(\n `LCM observe queue finish: session=${sessionId}, depth=${depth}, inFlight=${inFlight}, wait=${waitMs}ms, run=${runMs}ms, total=${totalMs}ms`,\n );\n },\n },\n });\n\n if (this.closed) {\n db.close();\n return;\n }\n\n this.db = db;\n this.archive = archive;\n this.dag = dag;\n this.summarizer = summarizer;\n this.observeQueue = observeQueue;\n log.info(\"LCM engine initialized\");\n }\n\n /**\n * Enqueue messages from agent_end hook.\n * The queue worker performs the archive append and incremental summarization.\n */\n async observeMessages(\n sessionId: string,\n messages: LcmObserveMessage[],\n ): Promise<void> {\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n this.enqueueObserveMessages(normalizedSessionId, messages);\n }\n\n /** Enqueue an observe job without waiting for worker completion. */\n enqueueObserveMessages(\n sessionId: string,\n messages: LcmObserveMessage[],\n ): void {\n if (!this.config.enabled || this.closed) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n if (messages.length === 0) return;\n\n this.reservePendingObserveInit(normalizedSessionId);\n\n void this.ensureInitialized()\n .then(() => {\n if (this.closed || !this.observeQueue) return;\n this.observeQueue.enqueue(normalizedSessionId, messages);\n log.debug(\n `LCM observe enqueued: session=${normalizedSessionId}, depth=${this.observeQueue.depth}, inFlight=${this.observeQueue.inFlightCount}`,\n );\n })\n .catch((err) => {\n if (this.closed) return;\n log.error(`LCM observe enqueue initialization error: ${err}`);\n })\n .finally(() => {\n this.releasePendingObserveInit(normalizedSessionId);\n });\n }\n\n private async processObserveMessages(\n sessionId: string,\n messages: LcmObserveMessage[],\n ): Promise<void> {\n if (this.closed) return;\n await this.ensureInitialized();\n if (this.closed || !this.archive || !this.summarizer) return;\n\n if (messages.length === 0) return;\n\n const currentMax = this.archive.getMaxTurnIndex(sessionId);\n const newMessages = messages.map((m, i) => ({\n turnIndex: currentMax + 1 + i,\n role: m.role,\n content: m.content,\n parts: this.config.messagePartsEnabled ? m.parts : undefined,\n rawContent: this.config.messagePartsEnabled ? m.rawContent : undefined,\n sourceFormat: this.config.messagePartsEnabled ? m.sourceFormat : undefined,\n }));\n\n this.archive.appendMessages(sessionId, newMessages, {\n messagePartsEnabled: this.config.messagePartsEnabled,\n });\n\n // Trigger incremental summarization inside the worker, after append.\n try {\n await this.summarizer.summarizeIncremental(sessionId);\n } catch (err) {\n log.debug(`LCM incremental summarization error: ${err}`);\n }\n }\n\n get observeQueueDepth(): number {\n return this.observeQueue?.depth ?? 0;\n }\n\n get observeQueueInFlightCount(): number {\n return this.observeQueue?.inFlightCount ?? 0;\n }\n\n async waitForObserveQueueIdle(): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n await this.ensureInitialized();\n if (this.closed) return;\n await this.waitForPendingObserveInitIdle();\n await this.observeQueue?.whenIdle();\n }\n\n async waitForSessionObserveIdle(sessionId: string): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n if (this.closed) return;\n await this.waitForPendingObserveInitIdle(normalizedSessionId);\n await this.observeQueue?.whenSessionIdle(normalizedSessionId);\n }\n\n private reservePendingObserveInit(sessionId: string): void {\n const count = this.pendingObserveInitCounts.get(sessionId) ?? 0;\n this.pendingObserveInitCounts.set(sessionId, count + 1);\n }\n\n private releasePendingObserveInit(sessionId: string): void {\n const count = this.pendingObserveInitCounts.get(sessionId);\n if (!count) return;\n\n if (count === 1) {\n this.pendingObserveInitCounts.delete(sessionId);\n const waiters = this.pendingObserveInitWaiters.get(sessionId) ?? [];\n this.pendingObserveInitWaiters.delete(sessionId);\n for (const resolve of waiters) resolve();\n if (this.pendingObserveInitCounts.size === 0) {\n const idleWaiters = this.pendingObserveInitIdleWaiters.splice(\n 0,\n this.pendingObserveInitIdleWaiters.length,\n );\n for (const resolve of idleWaiters) resolve();\n }\n return;\n }\n\n this.pendingObserveInitCounts.set(sessionId, count - 1);\n }\n\n private async waitForPendingObserveInitIdle(sessionId?: string): Promise<void> {\n if (sessionId) {\n if (!this.pendingObserveInitCounts.has(sessionId)) return;\n await new Promise<void>((resolve) => {\n const waiters = this.pendingObserveInitWaiters.get(sessionId) ?? [];\n waiters.push(resolve);\n this.pendingObserveInitWaiters.set(sessionId, waiters);\n });\n return;\n }\n\n if (this.pendingObserveInitCounts.size === 0) return;\n await new Promise<void>((resolve) => {\n this.pendingObserveInitIdleWaiters.push(resolve);\n });\n }\n\n /** Build the compressed history recall section for a session. */\n async assembleRecall(\n sessionId: string,\n budgetChars: number,\n ): Promise<string> {\n if (!this.config.enabled) return \"\";\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return \"\";\n await this.ensureInitialized();\n\n const effectiveBudget = Math.ceil(\n budgetChars * this.config.recallBudgetShare,\n );\n if (effectiveBudget <= 0) return \"\";\n\n return assembleCompressedHistory(this.dag!, this.archive!, normalizedSessionId, {\n freshTailTurns: this.config.freshTailTurns,\n budgetChars: effectiveBudget,\n });\n }\n\n async searchStructuredParts(\n sessionId: string,\n query: string,\n limit = this.config.messagePartsRecallMaxResults,\n ): Promise<LcmStructuredRecallMatch[]> {\n if (!this.config.enabled || !this.config.messagePartsEnabled) return [];\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return [];\n await this.ensureInitialized();\n if (!this.archive) return [];\n return this.archive.searchStructuredParts(query, limit, normalizedSessionId);\n }\n\n formatStructuredRecall(\n matches: LcmStructuredRecallMatch[],\n budgetChars: number,\n ): string {\n if (matches.length === 0 || budgetChars <= 0) return \"\";\n const lines: string[] = [];\n let used = \"## Structured Session Matches\\n\\n\".length;\n for (const match of matches) {\n const label = match.file_path\n ? `${match.kind} ${match.file_path}`\n : match.tool_name\n ? `${match.kind} ${match.tool_name}`\n : match.kind;\n const excerpt = match.content.replace(/\\s+/g, \" \").slice(0, 220);\n const line = `- turn ${match.turn_index} (${match.role}): ${label} — ${excerpt}`;\n if (used + line.length + 1 > budgetChars) break;\n lines.push(line);\n used += line.length + 1;\n }\n return lines.length > 0 ? `## Structured Session Matches\\n\\n${lines.join(\"\\n\")}` : \"\";\n }\n\n /** Flush pending summaries before compaction (called from before_compaction hook). */\n async preCompactionFlush(sessionId: string): Promise<void> {\n if (!this.config.enabled) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n await this.waitForSessionObserveIdle(normalizedSessionId);\n\n try {\n await this.summarizer!.summarizeIncremental(normalizedSessionId);\n } catch (err) {\n log.debug(`LCM pre-compaction flush error: ${err}`);\n }\n }\n\n /** Record a compaction event with real token counts (called from after_compaction hook). */\n async recordCompaction(\n sessionId: string,\n tokensBefore: number,\n tokensAfter: number,\n ): Promise<void> {\n if (!this.config.enabled) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n await this.waitForSessionObserveIdle(normalizedSessionId);\n\n const maxTurn = this.archive!.getMaxTurnIndex(normalizedSessionId);\n\n this.dag!.recordCompaction(normalizedSessionId, maxTurn, tokensBefore, tokensAfter);\n log.info(\n `LCM compaction recorded: session=${normalizedSessionId}, turn=${maxTurn}, tokens ${tokensBefore}→${tokensAfter}`,\n );\n }\n\n /** Verify archive coverage after compaction. */\n async verifyPostCompaction(sessionId: string): Promise<void> {\n if (!this.config.enabled) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n\n const msgCount = this.archive!.getMessageCount(normalizedSessionId);\n const nodeCount = this.dag!.getNodeCount(normalizedSessionId);\n log.debug(\n `LCM post-compaction verify: session=${normalizedSessionId}, messages=${msgCount}, summaryNodes=${nodeCount}`,\n );\n }\n\n // ── MCP Tool implementations ──\n\n /** Search across all conversation history via FTS (snippet mode). */\n async searchContext(\n query: string,\n limit: number,\n sessionId?: string,\n sessionPrefix?: string,\n ): Promise<\n Array<{\n turn_index: number;\n role: string;\n snippet: string;\n session_id: string;\n }>\n > {\n if (!this.config.enabled) return [];\n const normalizedSessionId = normalizeOptionalLcmSessionId(sessionId);\n if (sessionId !== undefined && !normalizedSessionId) return [];\n const normalizedSessionPrefix = normalizeOptionalLcmSessionId(sessionPrefix);\n if (sessionPrefix !== undefined && !normalizedSessionPrefix) return [];\n await this.ensureInitialized();\n return this.archive!.search(query, limit, normalizedSessionId, normalizedSessionPrefix);\n }\n\n /** Search via FTS returning full message content (not snippets). */\n async searchContextFull(\n query: string,\n limit: number,\n sessionId?: string,\n sessionPrefix?: string,\n ): Promise<\n Array<{\n id: number;\n turn_index: number;\n role: string;\n content: string;\n session_id: string;\n score: number;\n }>\n > {\n if (!this.config.enabled) return [];\n const normalizedSessionId = normalizeOptionalLcmSessionId(sessionId);\n if (sessionId !== undefined && !normalizedSessionId) return [];\n const normalizedSessionPrefix = normalizeOptionalLcmSessionId(sessionPrefix);\n if (sessionPrefix !== undefined && !normalizedSessionPrefix) return [];\n await this.ensureInitialized();\n return this.archive!.searchWithContent(\n query,\n limit,\n normalizedSessionId,\n 1000,\n normalizedSessionPrefix,\n );\n }\n\n /** Get a compressed summary of a turn range. */\n async describeContext(\n sessionId: string,\n fromTurn: number,\n toTurn: number,\n ): Promise<{ summary: string; turn_count: number; depth: number } | null> {\n if (!this.config.enabled) return null;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return null;\n await this.ensureInitialized();\n\n const nodes = this.dag!.getCoveringNodes(normalizedSessionId, fromTurn, toTurn);\n if (nodes.length === 0) {\n // No summary exists — build a description from raw messages\n const messages = this.archive!.getMessages(normalizedSessionId, fromTurn, toTurn);\n if (messages.length === 0) return null;\n const preview = messages\n .slice(0, 5)\n .map((m) => `[${m.role}] ${m.content.slice(0, 100)}`)\n .join(\"\\n\");\n return {\n summary: `No summary available for this range. Preview of ${messages.length} messages:\\n${preview}`,\n turn_count: messages.length,\n depth: -1,\n };\n }\n\n // Use the deepest covering node\n const best = nodes[0]; // Already sorted by depth DESC\n return {\n summary: best.summary_text,\n turn_count: best.msg_end - best.msg_start + 1,\n depth: best.depth,\n };\n }\n\n /** Retrieve raw messages for a turn range (lossless expansion). */\n async expandContext(\n sessionId: string,\n fromTurn: number,\n toTurn: number,\n maxTokens: number,\n ): Promise<Array<{ turn_index: number; role: string; content: string }>> {\n if (!this.config.enabled) return [];\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return [];\n await this.ensureInitialized();\n\n const messages = this.archive!.getMessages(normalizedSessionId, fromTurn, toTurn);\n if (messages.length === 0) return [];\n\n // Enforce token budget — keep first and last, truncate middle\n const maxChars = maxTokens * 4;\n let totalChars = 0;\n for (const m of messages) totalChars += m.content.length;\n\n if (totalChars <= maxChars) {\n return messages.map((m) => ({\n turn_index: m.turn_index,\n role: m.role,\n content: m.content,\n }));\n }\n\n // Keep first and last messages, truncate from middle\n const result: Array<{ turn_index: number; role: string; content: string }> =\n [];\n let budget = maxChars;\n\n // Reserve space for the last message\n const lastMsg = messages[messages.length - 1];\n const lastMsgChars = Math.min(\n lastMsg.content.length,\n Math.floor(maxChars * 0.3),\n );\n budget -= lastMsgChars;\n\n // Add messages from the beginning\n for (let i = 0; i < messages.length - 1; i++) {\n if (budget <= 0) break;\n const m = messages[i];\n const truncated = m.content.slice(0, budget);\n result.push({\n turn_index: m.turn_index,\n role: m.role,\n content: truncated,\n });\n budget -= truncated.length;\n }\n\n // Always append the last message\n result.push({\n turn_index: lastMsg.turn_index,\n role: lastMsg.role,\n content: lastMsg.content.slice(0, lastMsgChars + Math.max(0, budget)),\n });\n\n return result;\n }\n\n /** Get statistics about the LCM archive. */\n async getStats(sessionId?: string): Promise<{\n totalMessages: number;\n totalSummaryNodes: number;\n maxDepth: number;\n maxTurnIndex?: number;\n }> {\n if (!this.config.enabled)\n return { totalMessages: 0, totalSummaryNodes: 0, maxDepth: -1 };\n const normalizedSessionId = normalizeOptionalLcmSessionId(sessionId);\n if (sessionId !== undefined && !normalizedSessionId) {\n return { ...EMPTY_LCM_STATS };\n }\n await this.ensureInitialized();\n\n if (normalizedSessionId) {\n return {\n totalMessages: this.archive!.getMessageCount(normalizedSessionId),\n totalSummaryNodes: this.dag!.getNodeCount(normalizedSessionId),\n maxDepth: this.dag!.getMaxDepth(normalizedSessionId),\n maxTurnIndex: this.archive!.getMaxTurnIndex(normalizedSessionId),\n };\n }\n\n return {\n totalMessages: this.archive!.getTotalMessageCount(),\n totalSummaryNodes: 0, // Would need a global count query\n maxDepth: -1,\n };\n }\n\n /** Clear all LCM archive and summary state for one session. */\n async clearSession(sessionId: string): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n if (this.closed || !this.archive || !this.dag) return;\n await this.waitForSessionObserveIdle(normalizedSessionId);\n this.dag.deleteSession(normalizedSessionId);\n this.archive.deleteSession(normalizedSessionId);\n }\n\n /** Clear all LCM archive and summary state without closing the engine. */\n async clearAll(): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n await this.ensureInitialized();\n if (this.closed || !this.archive || !this.dag) return;\n await this.waitForObserveQueueIdle();\n this.dag.deleteAll();\n this.archive.deleteAll();\n }\n\n /** Prune old data beyond retention period. */\n async prune(): Promise<{ messagesPruned: number; nodesPruned: number }> {\n if (!this.config.enabled) return { messagesPruned: 0, nodesPruned: 0 };\n await this.ensureInitialized();\n\n const messagesPruned = this.archive!.pruneOldMessages(\n this.config.archiveRetentionDays,\n );\n const nodesPruned = this.dag!.pruneOldNodes(\n this.config.archiveRetentionDays,\n );\n return { messagesPruned, nodesPruned };\n }\n\n /** Close the database connection. */\n close(): void {\n this.closed = true;\n if (this.db) {\n this.db.close();\n }\n this.db = null;\n this.archive = null;\n this.dag = null;\n this.summarizer = null;\n this.observeQueue = null;\n this.initPromise = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAS,gBAAgB,OAAgB,UAAkB,MAAM,GAAW;AAC1E,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,CAAC;AACxC;AAEO,SAAS,iBAAiB,KAAoC;AACnE,SAAO;AAAA,IACL,SAAU,IAAY,eAAe;AAAA,IACrC,eAAe,gBAAiB,IAAY,kBAAkB,CAAC;AAAA,IAC/D,aAAa,gBAAiB,IAAY,gBAAgB,GAAG,CAAC;AAAA,IAC9D,gBAAgB,gBAAiB,IAAY,mBAAmB,EAAE;AAAA,IAClE,UAAU,gBAAiB,IAAY,aAAa,CAAC;AAAA,IACrD,wBAAwB,gBAAiB,IAAY,2BAA2B,GAAG;AAAA,IACnF,sBAAsB,gBAAiB,IAAY,yBAAyB,IAAI,CAAC;AAAA,IACjF,mBAAoB,IAAY,wBAAwB;AAAA,IACxD,oBACE,OAAQ,IAAY,0BAA0B,YAC5C,OAAO,SAAU,IAAY,qBAAqB,IAChD,KAAK,IAAI,GAAG,KAAK,MAAO,IAAY,qBAAqB,CAAC,IAC1D;AAAA,IACN,2BAA4B,IAAY,iCAAiC;AAAA,IACzE,qBAAsB,IAAY,wBAAwB;AAAA,IAC1D,8BACE,OAAQ,IAAY,iCAAiC,WACjD,KAAK,IAAI,GAAG,KAAK,MAAO,IAAY,4BAA4B,CAAC,IACjE;AAAA,EACR;AACF;AAEA,SAAS,sBAAsB,WAA2B;AACxD,SAAO,UAAU,KAAK;AACxB;AAEA,SAAS,8BAA8B,WAAwC;AAC7E,MAAI,cAAc,QAAW;AAC3B,WAAO;AAAA,EACT;AACA,QAAM,aAAa,sBAAsB,SAAS;AAClD,SAAO,WAAW,SAAS,IAAI,aAAa;AAC9C;AAEA,IAAM,kBAAkB;AAAA,EACtB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,UAAU;AACZ;AAEO,IAAM,YAAN,MAAgB;AAAA,EAcrB,YACE,cACiB,aACjB;AADiB;AAEjB,SAAK,SAAS,iBAAiB,YAAY;AAC3C,SAAK,YAAY,aAAa;AAAA,EAChC;AAAA,EAJmB;AAAA,EAfX,KAA+B;AAAA,EAC/B,UAA6B;AAAA,EAC7B,MAAqB;AAAA,EACrB,aAAmC;AAAA,EACnC,eAAoC;AAAA,EACpC,SAAS;AAAA,EACA;AAAA,EACA;AAAA,EACT,cAAoC;AAAA,EAC3B,2BAA2B,oBAAI,IAAoB;AAAA,EACnD,4BAA4B,oBAAI,IAA+B;AAAA,EAC/D,gCAAmD,CAAC;AAAA,EAUrE,IAAI,UAAmB;AACrB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA,EAGA,MAAM,oBAAmC;AACvC,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,GAAI;AACb,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK;AACX;AAAA,IACF;AACA,SAAK,cAAc,KAAK,OAAO,EAAE,MAAM,CAAC,QAAQ;AAE9C,WAAK,cAAc;AACnB,YAAM;AAAA,IACR,CAAC;AACD,UAAM,KAAK;AAAA,EACb;AAAA,EAEA,MAAc,SAAwB;AACpC,UAAM,kBAAkB,KAAK,SAAS;AACtC,UAAM,KAAK,gBAAgB,KAAK,SAAS;AACzC,UAAM,UAAU,IAAI,WAAW,EAAE;AACjC,UAAM,MAAM,IAAI,OAAO,EAAE;AACzB,UAAM,aAAa,IAAI;AAAA,MACrB;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL;AAAA,QACE,eAAe,KAAK,OAAO;AAAA,QAC3B,aAAa,KAAK,OAAO;AAAA,QACzB,UAAU,KAAK,OAAO;AAAA,QACtB,wBAAwB,KAAK,OAAO;AAAA,QACpC,2BAA2B,KAAK,OAAO;AAAA,MACzC;AAAA,IACF;AACA,UAAM,eAAe,IAAI,aAAa;AAAA,MACpC,aAAa,KAAK,OAAO;AAAA,MACzB,QAAQ,OAAO,WAAW,aAAa;AACrC,cAAM,KAAK,uBAAuB,WAAW,QAAQ;AAAA,MACvD;AAAA,MACA,OAAO;AAAA,QACL,YAAY,CAAC,EAAE,WAAW,OAAO,UAAU,OAAO,MAAM;AACtD,cAAI;AAAA,YACF,oCAAoC,SAAS,WAAW,KAAK,cAAc,QAAQ,UAAU,MAAM;AAAA,UACrG;AAAA,QACF;AAAA,QACA,aAAa,CAAC;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,MAAM;AACJ,cAAI,OAAO;AACT,gBAAI;AAAA,cACF,sCAAsC,SAAS,WAAW,KAAK,cAAc,QAAQ,UAAU,MAAM,WAAW,KAAK,aAAa,OAAO,aAAa,KAAK;AAAA,YAC7J;AACA;AAAA,UACF;AAEA,cAAI;AAAA,YACF,qCAAqC,SAAS,WAAW,KAAK,cAAc,QAAQ,UAAU,MAAM,WAAW,KAAK,aAAa,OAAO;AAAA,UAC1I;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,KAAK,QAAQ;AACf,SAAG,MAAM;AACT;AAAA,IACF;AAEA,SAAK,KAAK;AACV,SAAK,UAAU;AACf,SAAK,MAAM;AACX,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,QAAI,KAAK,wBAAwB;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBACJ,WACA,UACe;AACf,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,SAAK,uBAAuB,qBAAqB,QAAQ;AAAA,EAC3D;AAAA;AAAA,EAGA,uBACE,WACA,UACM;AACN,QAAI,CAAC,KAAK,OAAO,WAAW,KAAK,OAAQ;AACzC,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,QAAI,SAAS,WAAW,EAAG;AAE3B,SAAK,0BAA0B,mBAAmB;AAElD,SAAK,KAAK,kBAAkB,EACzB,KAAK,MAAM;AACV,UAAI,KAAK,UAAU,CAAC,KAAK,aAAc;AACvC,WAAK,aAAa,QAAQ,qBAAqB,QAAQ;AACvD,UAAI;AAAA,QACF,iCAAiC,mBAAmB,WAAW,KAAK,aAAa,KAAK,cAAc,KAAK,aAAa,aAAa;AAAA,MACrI;AAAA,IACF,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAI,KAAK,OAAQ;AACjB,UAAI,MAAM,6CAA6C,GAAG,EAAE;AAAA,IAC9D,CAAC,EACA,QAAQ,MAAM;AACb,WAAK,0BAA0B,mBAAmB;AAAA,IACpD,CAAC;AAAA,EACL;AAAA,EAEA,MAAc,uBACZ,WACA,UACe;AACf,QAAI,KAAK,OAAQ;AACjB,UAAM,KAAK,kBAAkB;AAC7B,QAAI,KAAK,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,WAAY;AAEtD,QAAI,SAAS,WAAW,EAAG;AAE3B,UAAM,aAAa,KAAK,QAAQ,gBAAgB,SAAS;AACzD,UAAM,cAAc,SAAS,IAAI,CAAC,GAAG,OAAO;AAAA,MAC1C,WAAW,aAAa,IAAI;AAAA,MAC5B,MAAM,EAAE;AAAA,MACR,SAAS,EAAE;AAAA,MACX,OAAO,KAAK,OAAO,sBAAsB,EAAE,QAAQ;AAAA,MACnD,YAAY,KAAK,OAAO,sBAAsB,EAAE,aAAa;AAAA,MAC7D,cAAc,KAAK,OAAO,sBAAsB,EAAE,eAAe;AAAA,IACnE,EAAE;AAEF,SAAK,QAAQ,eAAe,WAAW,aAAa;AAAA,MAClD,qBAAqB,KAAK,OAAO;AAAA,IACnC,CAAC;AAGD,QAAI;AACF,YAAM,KAAK,WAAW,qBAAqB,SAAS;AAAA,IACtD,SAAS,KAAK;AACZ,UAAI,MAAM,wCAAwC,GAAG,EAAE;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,IAAI,oBAA4B;AAC9B,WAAO,KAAK,cAAc,SAAS;AAAA,EACrC;AAAA,EAEA,IAAI,4BAAoC;AACtC,WAAO,KAAK,cAAc,iBAAiB;AAAA,EAC7C;AAAA,EAEA,MAAM,0BAAyC;AAC7C,QAAI,CAAC,KAAK,OAAO,WAAW,KAAK,OAAQ;AACzC,UAAM,KAAK,kBAAkB;AAC7B,QAAI,KAAK,OAAQ;AACjB,UAAM,KAAK,8BAA8B;AACzC,UAAM,KAAK,cAAc,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,0BAA0B,WAAkC;AAChE,QAAI,CAAC,KAAK,OAAO,WAAW,KAAK,OAAQ;AACzC,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,UAAM,KAAK,kBAAkB;AAC7B,QAAI,KAAK,OAAQ;AACjB,UAAM,KAAK,8BAA8B,mBAAmB;AAC5D,UAAM,KAAK,cAAc,gBAAgB,mBAAmB;AAAA,EAC9D;AAAA,EAEQ,0BAA0B,WAAyB;AACzD,UAAM,QAAQ,KAAK,yBAAyB,IAAI,SAAS,KAAK;AAC9D,SAAK,yBAAyB,IAAI,WAAW,QAAQ,CAAC;AAAA,EACxD;AAAA,EAEQ,0BAA0B,WAAyB;AACzD,UAAM,QAAQ,KAAK,yBAAyB,IAAI,SAAS;AACzD,QAAI,CAAC,MAAO;AAEZ,QAAI,UAAU,GAAG;AACf,WAAK,yBAAyB,OAAO,SAAS;AAC9C,YAAM,UAAU,KAAK,0BAA0B,IAAI,SAAS,KAAK,CAAC;AAClE,WAAK,0BAA0B,OAAO,SAAS;AAC/C,iBAAW,WAAW,QAAS,SAAQ;AACvC,UAAI,KAAK,yBAAyB,SAAS,GAAG;AAC5C,cAAM,cAAc,KAAK,8BAA8B;AAAA,UACrD;AAAA,UACA,KAAK,8BAA8B;AAAA,QACrC;AACA,mBAAW,WAAW,YAAa,SAAQ;AAAA,MAC7C;AACA;AAAA,IACF;AAEA,SAAK,yBAAyB,IAAI,WAAW,QAAQ,CAAC;AAAA,EACxD;AAAA,EAEA,MAAc,8BAA8B,WAAmC;AAC7E,QAAI,WAAW;AACb,UAAI,CAAC,KAAK,yBAAyB,IAAI,SAAS,EAAG;AACnD,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,cAAM,UAAU,KAAK,0BAA0B,IAAI,SAAS,KAAK,CAAC;AAClE,gBAAQ,KAAK,OAAO;AACpB,aAAK,0BAA0B,IAAI,WAAW,OAAO;AAAA,MACvD,CAAC;AACD;AAAA,IACF;AAEA,QAAI,KAAK,yBAAyB,SAAS,EAAG;AAC9C,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,WAAK,8BAA8B,KAAK,OAAO;AAAA,IACjD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,eACJ,WACA,aACiB;AACjB,QAAI,CAAC,KAAK,OAAO,QAAS,QAAO;AACjC,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB,QAAO;AACjC,UAAM,KAAK,kBAAkB;AAE7B,UAAM,kBAAkB,KAAK;AAAA,MAC3B,cAAc,KAAK,OAAO;AAAA,IAC5B;AACA,QAAI,mBAAmB,EAAG,QAAO;AAEjC,WAAO,0BAA0B,KAAK,KAAM,KAAK,SAAU,qBAAqB;AAAA,MAC9E,gBAAgB,KAAK,OAAO;AAAA,MAC5B,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,sBACJ,WACA,OACA,QAAQ,KAAK,OAAO,8BACiB;AACrC,QAAI,CAAC,KAAK,OAAO,WAAW,CAAC,KAAK,OAAO,oBAAqB,QAAO,CAAC;AACtE,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB,QAAO,CAAC;AAClC,UAAM,KAAK,kBAAkB;AAC7B,QAAI,CAAC,KAAK,QAAS,QAAO,CAAC;AAC3B,WAAO,KAAK,QAAQ,sBAAsB,OAAO,OAAO,mBAAmB;AAAA,EAC7E;AAAA,EAEA,uBACE,SACA,aACQ;AACR,QAAI,QAAQ,WAAW,KAAK,eAAe,EAAG,QAAO;AACrD,UAAM,QAAkB,CAAC;AACzB,QAAI,OAAO,oCAAoC;AAC/C,eAAW,SAAS,SAAS;AAC3B,YAAM,QAAQ,MAAM,YAChB,GAAG,MAAM,IAAI,IAAI,MAAM,SAAS,KAChC,MAAM,YACJ,GAAG,MAAM,IAAI,IAAI,MAAM,SAAS,KAChC,MAAM;AACZ,YAAM,UAAU,MAAM,QAAQ,QAAQ,QAAQ,GAAG,EAAE,MAAM,GAAG,GAAG;AAC/D,YAAM,OAAO,UAAU,MAAM,UAAU,KAAK,MAAM,IAAI,MAAM,KAAK,WAAM,OAAO;AAC9E,UAAI,OAAO,KAAK,SAAS,IAAI,YAAa;AAC1C,YAAM,KAAK,IAAI;AACf,cAAQ,KAAK,SAAS;AAAA,IACxB;AACA,WAAO,MAAM,SAAS,IAAI;AAAA;AAAA,EAAoC,MAAM,KAAK,IAAI,CAAC,KAAK;AAAA,EACrF;AAAA;AAAA,EAGA,MAAM,mBAAmB,WAAkC;AACzD,QAAI,CAAC,KAAK,OAAO,QAAS;AAC1B,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,UAAM,KAAK,kBAAkB;AAC7B,UAAM,KAAK,0BAA0B,mBAAmB;AAExD,QAAI;AACF,YAAM,KAAK,WAAY,qBAAqB,mBAAmB;AAAA,IACjE,SAAS,KAAK;AACZ,UAAI,MAAM,mCAAmC,GAAG,EAAE;AAAA,IACpD;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,iBACJ,WACA,cACA,aACe;AACf,QAAI,CAAC,KAAK,OAAO,QAAS;AAC1B,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,UAAM,KAAK,kBAAkB;AAC7B,UAAM,KAAK,0BAA0B,mBAAmB;AAExD,UAAM,UAAU,KAAK,QAAS,gBAAgB,mBAAmB;AAEjE,SAAK,IAAK,iBAAiB,qBAAqB,SAAS,cAAc,WAAW;AAClF,QAAI;AAAA,MACF,oCAAoC,mBAAmB,UAAU,OAAO,YAAY,YAAY,SAAI,WAAW;AAAA,IACjH;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,qBAAqB,WAAkC;AAC3D,QAAI,CAAC,KAAK,OAAO,QAAS;AAC1B,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,UAAM,KAAK,kBAAkB;AAE7B,UAAM,WAAW,KAAK,QAAS,gBAAgB,mBAAmB;AAClE,UAAM,YAAY,KAAK,IAAK,aAAa,mBAAmB;AAC5D,QAAI;AAAA,MACF,uCAAuC,mBAAmB,cAAc,QAAQ,kBAAkB,SAAS;AAAA,IAC7G;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,OACA,OACA,WACA,eAQA;AACA,QAAI,CAAC,KAAK,OAAO,QAAS,QAAO,CAAC;AAClC,UAAM,sBAAsB,8BAA8B,SAAS;AACnE,QAAI,cAAc,UAAa,CAAC,oBAAqB,QAAO,CAAC;AAC7D,UAAM,0BAA0B,8BAA8B,aAAa;AAC3E,QAAI,kBAAkB,UAAa,CAAC,wBAAyB,QAAO,CAAC;AACrE,UAAM,KAAK,kBAAkB;AAC7B,WAAO,KAAK,QAAS,OAAO,OAAO,OAAO,qBAAqB,uBAAuB;AAAA,EACxF;AAAA;AAAA,EAGA,MAAM,kBACJ,OACA,OACA,WACA,eAUA;AACA,QAAI,CAAC,KAAK,OAAO,QAAS,QAAO,CAAC;AAClC,UAAM,sBAAsB,8BAA8B,SAAS;AACnE,QAAI,cAAc,UAAa,CAAC,oBAAqB,QAAO,CAAC;AAC7D,UAAM,0BAA0B,8BAA8B,aAAa;AAC3E,QAAI,kBAAkB,UAAa,CAAC,wBAAyB,QAAO,CAAC;AACrE,UAAM,KAAK,kBAAkB;AAC7B,WAAO,KAAK,QAAS;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,gBACJ,WACA,UACA,QACwE;AACxE,QAAI,CAAC,KAAK,OAAO,QAAS,QAAO;AACjC,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB,QAAO;AACjC,UAAM,KAAK,kBAAkB;AAE7B,UAAM,QAAQ,KAAK,IAAK,iBAAiB,qBAAqB,UAAU,MAAM;AAC9E,QAAI,MAAM,WAAW,GAAG;AAEtB,YAAM,WAAW,KAAK,QAAS,YAAY,qBAAqB,UAAU,MAAM;AAChF,UAAI,SAAS,WAAW,EAAG,QAAO;AAClC,YAAM,UAAU,SACb,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,IAAI,EAAE,IAAI,KAAK,EAAE,QAAQ,MAAM,GAAG,GAAG,CAAC,EAAE,EACnD,KAAK,IAAI;AACZ,aAAO;AAAA,QACL,SAAS,mDAAmD,SAAS,MAAM;AAAA,EAAe,OAAO;AAAA,QACjG,YAAY,SAAS;AAAA,QACrB,OAAO;AAAA,MACT;AAAA,IACF;AAGA,UAAM,OAAO,MAAM,CAAC;AACpB,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,YAAY,KAAK,UAAU,KAAK,YAAY;AAAA,MAC5C,OAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,cACJ,WACA,UACA,QACA,WACuE;AACvE,QAAI,CAAC,KAAK,OAAO,QAAS,QAAO,CAAC;AAClC,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB,QAAO,CAAC;AAClC,UAAM,KAAK,kBAAkB;AAE7B,UAAM,WAAW,KAAK,QAAS,YAAY,qBAAqB,UAAU,MAAM;AAChF,QAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAGnC,UAAM,WAAW,YAAY;AAC7B,QAAI,aAAa;AACjB,eAAW,KAAK,SAAU,eAAc,EAAE,QAAQ;AAElD,QAAI,cAAc,UAAU;AAC1B,aAAO,SAAS,IAAI,CAAC,OAAO;AAAA,QAC1B,YAAY,EAAE;AAAA,QACd,MAAM,EAAE;AAAA,QACR,SAAS,EAAE;AAAA,MACb,EAAE;AAAA,IACJ;AAGA,UAAM,SACJ,CAAC;AACH,QAAI,SAAS;AAGb,UAAM,UAAU,SAAS,SAAS,SAAS,CAAC;AAC5C,UAAM,eAAe,KAAK;AAAA,MACxB,QAAQ,QAAQ;AAAA,MAChB,KAAK,MAAM,WAAW,GAAG;AAAA,IAC3B;AACA,cAAU;AAGV,aAAS,IAAI,GAAG,IAAI,SAAS,SAAS,GAAG,KAAK;AAC5C,UAAI,UAAU,EAAG;AACjB,YAAM,IAAI,SAAS,CAAC;AACpB,YAAM,YAAY,EAAE,QAAQ,MAAM,GAAG,MAAM;AAC3C,aAAO,KAAK;AAAA,QACV,YAAY,EAAE;AAAA,QACd,MAAM,EAAE;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AACD,gBAAU,UAAU;AAAA,IACtB;AAGA,WAAO,KAAK;AAAA,MACV,YAAY,QAAQ;AAAA,MACpB,MAAM,QAAQ;AAAA,MACd,SAAS,QAAQ,QAAQ,MAAM,GAAG,eAAe,KAAK,IAAI,GAAG,MAAM,CAAC;AAAA,IACtE,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SAAS,WAKZ;AACD,QAAI,CAAC,KAAK,OAAO;AACf,aAAO,EAAE,eAAe,GAAG,mBAAmB,GAAG,UAAU,GAAG;AAChE,UAAM,sBAAsB,8BAA8B,SAAS;AACnE,QAAI,cAAc,UAAa,CAAC,qBAAqB;AACnD,aAAO,EAAE,GAAG,gBAAgB;AAAA,IAC9B;AACA,UAAM,KAAK,kBAAkB;AAE7B,QAAI,qBAAqB;AACvB,aAAO;AAAA,QACL,eAAe,KAAK,QAAS,gBAAgB,mBAAmB;AAAA,QAChE,mBAAmB,KAAK,IAAK,aAAa,mBAAmB;AAAA,QAC7D,UAAU,KAAK,IAAK,YAAY,mBAAmB;AAAA,QACnD,cAAc,KAAK,QAAS,gBAAgB,mBAAmB;AAAA,MACjE;AAAA,IACF;AAEA,WAAO;AAAA,MACL,eAAe,KAAK,QAAS,qBAAqB;AAAA,MAClD,mBAAmB;AAAA;AAAA,MACnB,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,aAAa,WAAkC;AACnD,QAAI,CAAC,KAAK,OAAO,WAAW,KAAK,OAAQ;AACzC,UAAM,sBAAsB,sBAAsB,SAAS;AAC3D,QAAI,CAAC,oBAAqB;AAC1B,UAAM,KAAK,kBAAkB;AAC7B,QAAI,KAAK,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,IAAK;AAC/C,UAAM,KAAK,0BAA0B,mBAAmB;AACxD,SAAK,IAAI,cAAc,mBAAmB;AAC1C,SAAK,QAAQ,cAAc,mBAAmB;AAAA,EAChD;AAAA;AAAA,EAGA,MAAM,WAA0B;AAC9B,QAAI,CAAC,KAAK,OAAO,WAAW,KAAK,OAAQ;AACzC,UAAM,KAAK,kBAAkB;AAC7B,QAAI,KAAK,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,IAAK;AAC/C,UAAM,KAAK,wBAAwB;AACnC,SAAK,IAAI,UAAU;AACnB,SAAK,QAAQ,UAAU;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,QAAkE;AACtE,QAAI,CAAC,KAAK,OAAO,QAAS,QAAO,EAAE,gBAAgB,GAAG,aAAa,EAAE;AACrE,UAAM,KAAK,kBAAkB;AAE7B,UAAM,iBAAiB,KAAK,QAAS;AAAA,MACnC,KAAK,OAAO;AAAA,IACd;AACA,UAAM,cAAc,KAAK,IAAK;AAAA,MAC5B,KAAK,OAAO;AAAA,IACd;AACA,WAAO,EAAE,gBAAgB,YAAY;AAAA,EACvC;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,SAAS;AACd,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AAAA,IAChB;AACA,SAAK,KAAK;AACV,SAAK,UAAU;AACf,SAAK,MAAM;AACX,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,cAAc;AAAA,EACrB;AACF;","names":[]}