@pyxmate/memory 0.45.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,9 +11,9 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-DKNGLNN4.mjs";
15
- import "./chunk-UV2DFSKR.mjs";
16
- import "./chunk-KSTI4M52.mjs";
14
+ } from "./chunk-ZILXBWWH.mjs";
15
+ import "./chunk-U3U4MHWS.mjs";
16
+ import "./chunk-X6AYWXW7.mjs";
17
17
  export {
18
18
  DashboardClient,
19
19
  Poller,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StoreInput as StoreInput$1, MemoryEntry as MemoryEntry$1, MemorySearchParams as MemorySearchParams$1, MemorySearchResult as MemorySearchResult$1, MemoryType as MemoryType$1, PrincipalContext as PrincipalContext$1, MemoryStats as MemoryStats$1, LineageParams as LineageParams$1, LineageResult as LineageResult$1, ReinforceParams as ReinforceParams$1, ReinforceResult as ReinforceResult$1, WikiLintReport as WikiLintReport$1, GraphRepairResult as GraphRepairResult$1, ExtractedImageMeta as ExtractedImageMeta$1, IngestEntity as IngestEntity$1, IngestRelationship as IngestRelationship$1, EntityExtractionResult as EntityExtractionResult$1, Topology as Topology$1, IngestEvent as IngestEvent$1, GraphNode as GraphNode$1, GraphTraversalResult as GraphTraversalResult$1, CorrectionRecord as CorrectionRecord$1 } from '@pyx-memory/shared';
1
+ import { StoreInput as StoreInput$1, MemoryEntry as MemoryEntry$1, MemorySearchParams as MemorySearchParams$1, MemorySearchResult as MemorySearchResult$1, MemoryType as MemoryType$1, PrincipalContext as PrincipalContext$1, SensitivityLevel as SensitivityLevel$1, MemoryStats as MemoryStats$1, LineageParams as LineageParams$1, LineageResult as LineageResult$1, ReinforceParams as ReinforceParams$1, ReinforceResult as ReinforceResult$1, WikiLintReport as WikiLintReport$1, GraphRepairResult as GraphRepairResult$1, ExtractedImageMeta as ExtractedImageMeta$1, IngestEntity as IngestEntity$1, IngestRelationship as IngestRelationship$1, EntityExtractionResult as EntityExtractionResult$1, Topology as Topology$1, IngestEvent as IngestEvent$1, GraphNode as GraphNode$1, GraphTraversalResult as GraphTraversalResult$1, CorrectionRecord as CorrectionRecord$1 } from '@pyx-memory/shared';
2
2
 
3
3
  /** Parameters for paginated entry listing. */
4
4
  interface MemoryListParams {
@@ -19,6 +19,8 @@ interface MemoryListParams {
19
19
  * and get the legacy tenant-only scope.
20
20
  */
21
21
  principal?: PrincipalContext$1;
22
+ /** Maximum sensitivity level to include. Omitted preserves legacy behavior. */
23
+ maxSensitivity?: SensitivityLevel$1;
22
24
  /**
23
25
  * R1/R2 — opt-in list of `metadata._kind` values to include in the
24
26
  * response. When omitted or empty, the default-set returns only entries
@@ -42,6 +44,8 @@ interface TemporalQueryFilters {
42
44
  agentId?: string;
43
45
  source?: string;
44
46
  limit?: number;
47
+ /** Maximum sensitivity level to include. Omitted preserves legacy behavior. */
48
+ maxSensitivity?: SensitivityLevel$1;
45
49
  }
46
50
  /** Filters for the chronological feed (`log`, Karpathy `log.md` primitive). */
47
51
  interface MemoryLogFilters {
@@ -52,6 +56,8 @@ interface MemoryLogFilters {
52
56
  type?: MemoryType$1;
53
57
  agentId?: string;
54
58
  source?: string;
59
+ /** Maximum sensitivity level to include. Omitted preserves legacy behavior. */
60
+ maxSensitivity?: SensitivityLevel$1;
55
61
  }
56
62
  /** Options for scoping operations to a specific tenant. */
57
63
  interface TenantScopeOptions {
@@ -69,6 +75,8 @@ interface TenantScopeOptions {
69
75
  * legacy tenant-only scope.
70
76
  */
71
77
  principal?: PrincipalContext$1;
78
+ /** Maximum sensitivity level to include. Omitted preserves legacy behavior. */
79
+ maxSensitivity?: SensitivityLevel$1;
72
80
  }
73
81
  /** Abstract interface for memory systems (local or remote). */
74
82
  interface MemoryInterface {
@@ -141,6 +149,37 @@ interface ExtendedMemoryInterface extends MemoryInterface {
141
149
  getEntitySynthesis(name: string): Promise<MemoryEntry$1 | null>;
142
150
  }
143
151
 
152
+ /**
153
+ * No-op {@link MemoryInterface} for hosts that run without a memory backend
154
+ * (e.g. `MEMORY_URL` is unset). Every method returns a safe default, so the
155
+ * rest of the system functions without branching on `memory == null`.
156
+ *
157
+ * Canonical single source: this lives in the SAME package as `MemoryInterface`,
158
+ * so any method added to the interface fails THIS class's compile in the same
159
+ * build. The null-object can never silently drift behind the contract — the
160
+ * failure mode that left hand-rolled copies in downstream repos missing
161
+ * `lineage`/`reinforce` after the interface grew them.
162
+ *
163
+ * `initialize()` emits a single `console.warn`; pass nothing else — it is a
164
+ * pure null-object. Hosts wanting structured logging should log at the wiring
165
+ * site where they choose `DisabledMemory` over a real client.
166
+ */
167
+ declare class DisabledMemory implements MemoryInterface {
168
+ initialize(): Promise<void>;
169
+ store(entry: StoreInput$1): Promise<MemoryEntry$1>;
170
+ search(): Promise<MemorySearchResult$1>;
171
+ list(params?: MemoryListParams): Promise<MemoryListResult>;
172
+ get(): Promise<MemoryEntry$1 | null>;
173
+ delete(): Promise<boolean>;
174
+ clearSession(): Promise<number>;
175
+ stats(): Promise<MemoryStats$1>;
176
+ queryAsOf(): Promise<MemoryEntry$1[]>;
177
+ lineage(): Promise<LineageResult$1>;
178
+ reinforce(): Promise<ReinforceResult$1>;
179
+ queryByEventTime(): Promise<MemoryEntry$1[]>;
180
+ shutdown(): Promise<void>;
181
+ }
182
+
144
183
  /**
145
184
  * Callbacks for two-phase file enrichment. All callbacks are optional:
146
185
  * - Image-rich PDF + describeImage only → describes images, no entity extraction
@@ -149,7 +188,9 @@ interface ExtendedMemoryInterface extends MemoryInterface {
149
188
  * - Mixed file + both → describes images + extracts from both sources
150
189
  *
151
190
  * Without any callback, the ingest stream emits the server result with no
152
- * SDK-side enrichment caller opted out entirely.
191
+ * SDK-side enrichment. Server-side text/entity extraction may still run when
192
+ * configured; image understanding still requires caller-provided
193
+ * descriptions/hooks.
153
194
  */
154
195
  interface EnrichmentCallbacks {
155
196
  /**
@@ -191,9 +232,12 @@ interface IngestFileOptions {
191
232
  }
192
233
  /**
193
234
  * Caller-supplied enrichment for the per-call store path. Mirrors
194
- * {@link EnrichmentCallbacks} for file ingest the SDK invokes the callback
195
- * (against the caller's own LLM credentials) and merges the result into the
196
- * ingest payload before POSTing, so the server holds no LLM keys.
235
+ * {@link EnrichmentCallbacks} for file ingest. When supplied, the SDK invokes
236
+ * the callback and merges the result into the payload before POSTing. Without
237
+ * a callback, server-side text/entity extraction can run only when the server
238
+ * has an extraction brain configured; otherwise callers must pass graph data
239
+ * or use caller-side hooks. Images always require caller-provided
240
+ * descriptions/hooks.
197
241
  */
198
242
  interface StoreEnrichmentCallbacks {
199
243
  /**
@@ -203,8 +247,8 @@ interface StoreEnrichmentCallbacks {
203
247
  *
204
248
  * Skipped when `entry.extractEntities === false`. When
205
249
  * `entry.extractEntities === true` but this callback is not supplied,
206
- * `MemoryClient.store()` throws a loud error rather than silently no-op'ing
207
- * (same shape as the in-process Memory.store() guard).
250
+ * `MemoryClient.store()` forwards the hint so the server-side extraction
251
+ * brain can run and loud-fail if it is not configured.
208
252
  */
209
253
  extractEntities?: (input: {
210
254
  content: string;
@@ -495,6 +539,24 @@ declare const EmbeddingProviderName: {
495
539
  readonly HTTP: "http";
496
540
  };
497
541
  type EmbeddingProviderName = (typeof EmbeddingProviderName)[keyof typeof EmbeddingProviderName];
542
+ type GraphEnrichmentStatus = 'caller-provided' | 'extracted' | 'merged' | 'opted-out' | 'skipped-duplicate' | 'skipped-sensitive' | 'skipped-unprovisioned' | 'extracted-empty' | 'failed-graph-write' | 'failed-best-effort';
543
+ interface GraphEnrichment {
544
+ status: GraphEnrichmentStatus;
545
+ provider?: 'http' | 'local' | 'none';
546
+ reason?: string;
547
+ action?: string;
548
+ }
549
+ /**
550
+ * A caller-supplied relationship whose source or target name did not resolve to
551
+ * any entity in the same store call (after name normalization), so the edge was
552
+ * not written. Surfaced on the store response so the agent — not just the server
553
+ * log — can see which edges to re-send.
554
+ */
555
+ interface DroppedGraphRelationship {
556
+ source: string;
557
+ target: string;
558
+ type: string;
559
+ }
498
560
  interface MemoryEntry {
499
561
  id: string;
500
562
  content: string;
@@ -529,6 +591,12 @@ interface MemoryEntry {
529
591
  graphEntitiesWritten?: number;
530
592
  /** Number of graph relationships written by this store call. Present on store responses. */
531
593
  graphRelationshipsWritten?: number;
594
+ /** Count of caller relationships dropped because an endpoint name did not match any entity in the call. Present (and >0) only when edges were dropped. */
595
+ graphRelationshipsDropped?: number;
596
+ /** Up to 20 of the dropped relationships (source/target/type), so the agent can re-send them. Present only when edges were dropped. */
597
+ graphRelationshipsDroppedDetail?: DroppedGraphRelationship[];
598
+ /** Graph extraction/enrichment outcome for this store call. Present on graph-targeted store responses. */
599
+ graphEnrichment?: GraphEnrichment;
532
600
  /** Tenant ID for multi-tenant isolation. */
533
601
  tenantId?: string;
534
602
  /** User ID within the tenant. */
@@ -602,8 +670,8 @@ interface MemorySearchParams {
602
670
  * only). The pipeline retrieves and dedups a deeper candidate pool (≥50),
603
671
  * scores it through the cross-encoder, then truncates to `limit` —
604
672
  * reranking after truncation cannot change top-`limit` set membership.
605
- * EMBEDDED ONLY: `MemoryClient.search` rejects it (HTTP API does not
606
- * forward it).
673
+ * Forwarded by `MemoryClient.search` and the HTTP route; core enforces
674
+ * hybrid-only usage.
607
675
  */
608
676
  enableRerank?: boolean;
609
677
  /**
@@ -643,7 +711,7 @@ interface MemorySearchParams {
643
711
  * reinforces); idle alone never revives. See `docs/H8-MEMORY-MODEL-DESIGN.md`.
644
712
  */
645
713
  effort?: 'quick' | 'medium' | 'deep';
646
- /** Maximum sensitivity level to include in results. Entries above this level are excluded or redacted. */
714
+ /** Maximum sensitivity level to include in results. Entries above this level are excluded. */
647
715
  maxSensitivity?: SensitivityLevel;
648
716
  /** Tenant ID for multi-tenant isolation. */
649
717
  tenantId?: string;
@@ -683,10 +751,17 @@ interface MemorySearchResult {
683
751
  strategy: RAGStrategy;
684
752
  /** Visible graph slice that supported the returned entries, when graph traversal contributed. */
685
753
  graph?: GraphTraversalResult;
686
- /** Optional scored entries with relevance scores for ranked results. */
754
+ /**
755
+ * Optional scored entries for ranked results. `score` is the fused RANK score
756
+ * (RRF + recency/importance/access priors) — good for ordering, NOT a relevance
757
+ * magnitude. `vectorSimilarity` is the raw dense query-similarity (LanceDB
758
+ * transformed-L2, 1/(1+distance)); null when the entry has no dense signal
759
+ * (FTS/graph-only). Surface vectorSimilarity, not score, as a "% match".
760
+ */
687
761
  scoredEntries?: Array<{
688
762
  entry: MemoryEntry;
689
763
  score: number;
764
+ vectorSimilarity?: number | null;
690
765
  }>;
691
766
  /** Confidence assessment for abstention. Present when search produces scored entries. */
692
767
  confidence?: {
@@ -918,6 +993,8 @@ interface FetchCorrectionsInput {
918
993
  project?: string;
919
994
  /** Max corrections to return. Default 5, hard cap 5. */
920
995
  limit?: number;
996
+ /** Optional sensitivity cap for hosted/HTTP callers. Omitted preserves trusted in-process behavior. */
997
+ maxSensitivity?: SensitivityLevel;
921
998
  }
922
999
  /** One applicable correction returned by `fetchApplicableCorrections`. */
923
1000
  interface CorrectionRecord {
@@ -1005,11 +1082,10 @@ interface WikiLintReport {
1005
1082
  /**
1006
1083
  * Result shape returned by an LLM entity extractor — entities + relations.
1007
1084
  *
1008
- * Lives in shared so both the server-side Memory.store() merge and any
1009
- * client-side enrichment path (`MemoryClient.store(..., { enrichment })`,
1010
- * the MCP `store_memory` tool that goes through MCP sampling) can speak the
1011
- * exact same structural type without crossing the core→client circular
1012
- * dependency.
1085
+ * Lives in shared so server-side Memory.store(), client-side enrichment
1086
+ * (`MemoryClient.store(..., { enrichment })`), and MCP store forwarding can
1087
+ * speak the exact same structural type without crossing the core→client
1088
+ * circular dependency.
1013
1089
  */
1014
1090
  interface EntityExtractionResult {
1015
1091
  entities: Array<{
@@ -1023,6 +1099,18 @@ interface EntityExtractionResult {
1023
1099
  }>;
1024
1100
  }
1025
1101
  declare function normalizeGraphLabel(value: string, fallback: string): string;
1102
+ /**
1103
+ * Identity key for a graph node: the normalized name alone. `type` is a node
1104
+ * ATTRIBUTE, never part of identity — it is a noisy, role-dependent, LLM-guessed
1105
+ * label that the relationship contract cannot even address (edges reference
1106
+ * names only), so using it as a primary key fractures one real-world entity
1107
+ * into disconnected nodes and severs multi-hop chains.
1108
+ *
1109
+ * The single source of truth for name identity: the SQLite/Neo4j upserts and
1110
+ * the core relationship resolver match by this, and the MCP store guard predicts
1111
+ * edge resolvability with it — all three MUST agree, so they import this one fn.
1112
+ */
1113
+ declare function normalizeNameKey(name: string): string;
1026
1114
  /**
1027
1115
  * Merge caller-provided entities/relationships with LLM-extracted ones.
1028
1116
  *
@@ -1061,6 +1149,8 @@ type TopologyServiceVariant = 'cloud' | 'full';
1061
1149
  * is internal infrastructure and is intentionally NOT exposed here.
1062
1150
  */
1063
1151
  type TopologyEmbeddingLocation = 'inline' | 'remote';
1152
+ /** Server-side graph extraction provider resolved at boot. */
1153
+ type TopologyExtractionProvider = 'http' | 'local' | 'none';
1064
1154
  /**
1065
1155
  * Public topology snapshot returned by `GET /status`. Reflects actual runtime
1066
1156
  * capability (variant detected from import probe, embedding location derived
@@ -1084,6 +1174,9 @@ interface Topology {
1084
1174
  modelId: string;
1085
1175
  dimensions: number;
1086
1176
  };
1177
+ extraction: {
1178
+ provider: TopologyExtractionProvider;
1179
+ };
1087
1180
  }
1088
1181
 
1089
1182
  /** Metadata for a single image extracted from a PDF. */
@@ -1361,4 +1454,12 @@ interface PrincipalContext {
1361
1454
  /** Sentinel tenant ID used in single-tenant deployments. */
1362
1455
  declare const SINGLE_TENANT_ID = "_single";
1363
1456
 
1364
- export { type AgentId, type ApiResponse, type ConsolidationRunResult, type CorrectionInput, type CorrectionRecord, DEFAULTS, DEPRECATED_RAG_STRATEGIES, EmbeddingProviderName, type EnrichmentCallbacks, type EntityExtractionResult, type ExtendedMemoryInterface, type FetchCorrectionsInput, type GraphFailureMode, type GraphNode, type GraphRelationship, type GraphRepairResult, type GraphTraversalResult, type IngestEntity, type IngestErrorEvent, type IngestEvent, type IngestFileOptions, type IngestHeartbeatEvent, type IngestProgressEvent, type IngestRelationship, type IngestResultEvent, type IngestStage, type IngestionResult, type LineageParams, type LineageResult, type LineageVersion, MemoryClient, type MemoryClientOptions, type MemoryEntry, type MemoryIngestRequest, type MemoryInterface, type MemoryListParams, type MemoryListResult, type MemoryLogFilters, type MemorySearchParams, type MemorySearchResult, MemoryServerError, type MemoryStats, MemoryType, type MoveEntriesFilter, MoveFailureReason, type MoveResult, type MoveTarget, NamespaceIsolation, type PrincipalContext, RAGStrategy, type ReinforceParams, type ReinforceResult, type ReinforceSignal, SINGLE_TENANT_ID, SensitivityLevel, type SourceEvidence, type StoreInput, StoreTarget, type TemporalQueryFilters, type TenantScopeOptions, type Timestamp, type Topology, type TopologyServiceVariant, VectorProvider, type WikiLintReport, mergeExtractedEntities, normalizeGraphLabel };
1457
+ interface CreatePyxMemoryOptions {
1458
+ url?: string;
1459
+ apiKey?: string;
1460
+ requestTimeoutMs?: number;
1461
+ defaultHeaders?: Record<string, string>;
1462
+ }
1463
+ declare function createPyxMemory(opts?: CreatePyxMemoryOptions): MemoryClient;
1464
+
1465
+ export { type AgentId, type ApiResponse, type ConsolidationRunResult, type CorrectionInput, type CorrectionRecord, type CreatePyxMemoryOptions, DEFAULTS, DEPRECATED_RAG_STRATEGIES, DisabledMemory, type DroppedGraphRelationship, EmbeddingProviderName, type EnrichmentCallbacks, type EntityExtractionResult, type ExtendedMemoryInterface, type FetchCorrectionsInput, type GraphEnrichment, type GraphEnrichmentStatus, type GraphFailureMode, type GraphNode, type GraphRelationship, type GraphRepairResult, type GraphTraversalResult, type IngestEntity, type IngestErrorEvent, type IngestEvent, type IngestFileOptions, type IngestHeartbeatEvent, type IngestProgressEvent, type IngestRelationship, type IngestResultEvent, type IngestStage, type IngestionResult, type LineageParams, type LineageResult, type LineageVersion, MemoryClient, type MemoryClientOptions, type MemoryEntry, type MemoryIngestRequest, type MemoryInterface, type MemoryListParams, type MemoryListResult, type MemoryLogFilters, type MemorySearchParams, type MemorySearchResult, MemoryServerError, type MemoryStats, MemoryType, type MoveEntriesFilter, MoveFailureReason, type MoveResult, type MoveTarget, NamespaceIsolation, type PrincipalContext, RAGStrategy, type ReinforceParams, type ReinforceResult, type ReinforceSignal, SINGLE_TENANT_ID, SensitivityLevel, type SourceEvidence, type StoreInput, StoreTarget, type TemporalQueryFilters, type TenantScopeOptions, type Timestamp, type Topology, type TopologyExtractionProvider, type TopologyServiceVariant, VectorProvider, type WikiLintReport, createPyxMemory, mergeExtractedEntities, normalizeGraphLabel, normalizeNameKey };
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
+ DisabledMemory,
2
3
  MemoryClient,
3
4
  MemoryServerError
4
- } from "./chunk-UV2DFSKR.mjs";
5
+ } from "./chunk-U3U4MHWS.mjs";
5
6
  import {
6
7
  DEFAULTS,
7
8
  DEPRECATED_RAG_STRATEGIES,
@@ -15,11 +16,27 @@ import {
15
16
  StoreTarget,
16
17
  VectorProvider,
17
18
  mergeExtractedEntities,
18
- normalizeGraphLabel
19
- } from "./chunk-KSTI4M52.mjs";
19
+ normalizeGraphLabel,
20
+ normalizeNameKey
21
+ } from "./chunk-X6AYWXW7.mjs";
22
+
23
+ // src/preset.ts
24
+ var DEFAULT_MEMORY_URL = `http://localhost:${DEFAULTS.MEMORY_SERVER_PORT}`;
25
+ function createPyxMemory(opts = {}) {
26
+ const env = typeof process !== "undefined" ? process.env : {};
27
+ const url = opts.url ?? env.PYX_MEMORY_URL ?? DEFAULT_MEMORY_URL;
28
+ const apiKey = opts.apiKey ?? env.PYX_MEMORY_API_KEY;
29
+ const clientOptions = {
30
+ ...apiKey !== void 0 ? { apiKey } : {},
31
+ ...opts.defaultHeaders !== void 0 ? { defaultHeaders: opts.defaultHeaders } : {},
32
+ ...opts.requestTimeoutMs !== void 0 ? { requestTimeoutMs: opts.requestTimeoutMs } : {}
33
+ };
34
+ return new MemoryClient(url, clientOptions);
35
+ }
20
36
  export {
21
37
  DEFAULTS,
22
38
  DEPRECATED_RAG_STRATEGIES,
39
+ DisabledMemory,
23
40
  EmbeddingProviderName,
24
41
  MemoryClient,
25
42
  MemoryServerError,
@@ -31,6 +48,8 @@ export {
31
48
  SensitivityLevel,
32
49
  StoreTarget,
33
50
  VectorProvider,
51
+ createPyxMemory,
34
52
  mergeExtractedEntities,
35
- normalizeGraphLabel
53
+ normalizeGraphLabel,
54
+ normalizeNameKey
36
55
  };
package/dist/react.mjs CHANGED
@@ -11,9 +11,9 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-DKNGLNN4.mjs";
15
- import "./chunk-UV2DFSKR.mjs";
16
- import "./chunk-KSTI4M52.mjs";
14
+ } from "./chunk-ZILXBWWH.mjs";
15
+ import "./chunk-U3U4MHWS.mjs";
16
+ import "./chunk-X6AYWXW7.mjs";
17
17
 
18
18
  // ../dashboard/src/hooks/use-consolidation-log.ts
19
19
  import { useCallback as useCallback2, useMemo } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "0.45.1",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",
@@ -28,6 +28,10 @@
28
28
  "import": "./dist/dashboard.mjs",
29
29
  "types": "./dist/dashboard.d.ts"
30
30
  },
31
+ "./agent-contract": {
32
+ "import": "./dist/agent-contract.mjs",
33
+ "types": "./dist/agent-contract.d.ts"
34
+ },
31
35
  "./react": {
32
36
  "import": "./dist/react.mjs",
33
37
  "types": "./dist/react.d.ts"
@@ -43,6 +47,7 @@
43
47
  ],
44
48
  "scripts": {
45
49
  "build": "tsup",
50
+ "generate:agent-template": "bun run scripts/generate-agent-template.ts",
46
51
  "test": "bun test",
47
52
  "typecheck": "tsc --noEmit",
48
53
  "clean": "rm -rf dist skills node_modules .turbo"