memorio 5.1.3 → 5.2.0

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 (48) hide show
  1. package/AGENTS.md +22 -13
  2. package/CHANGELOG.md +24 -9
  3. package/README.md +61 -19
  4. package/SECURITY-HARDENING.md +258 -0
  5. package/SECURITY.md +67 -3
  6. package/SUMMARY.md +55 -59
  7. package/adr/002-observer-semantics.md +1 -1
  8. package/adr/003-deep-mutation-semantics.md +1 -1
  9. package/adr/004-array-mutation-semantics.md +1 -1
  10. package/adr/010-logic-phase-0.md +42 -0
  11. package/adr/README.md +16 -11
  12. package/bin/cli.js +82 -60
  13. package/examples/acquired-knowledge.ts +174 -0
  14. package/examples/agent-memory-demo.ts +140 -0
  15. package/examples/sqlite-batched-writes.ts +60 -60
  16. package/examples/sync.ts +90 -90
  17. package/examples/useObserver.tsx +2 -2
  18. package/global.cjs +2026 -115
  19. package/global.js +2021 -116
  20. package/index.cjs +2026 -115
  21. package/index.d.ts +1 -0
  22. package/index.js +2021 -116
  23. package/llms.txt +135 -4
  24. package/markdown/EXTENSION_VSCODE_DESIGN.md +410 -0
  25. package/markdown/LOGIC.md +100 -0
  26. package/markdown/MEMORY-ATTACHMENT.md +17 -10
  27. package/markdown/MEMORY.md +404 -14
  28. package/markdown/MEM_FORMAT.md +313 -0
  29. package/markdown/SQLITE.md +2 -2
  30. package/markdown/STATE.md +27 -3
  31. package/markdown/SYNC.md +19 -15
  32. package/markdown/TEMPORAL.md +297 -0
  33. package/markdown/USEOBSERVER.md +7 -4
  34. package/modules/redux.cjs +1188 -38
  35. package/modules/redux.cjs.map +1 -1
  36. package/modules/redux.js +1187 -38
  37. package/modules/redux.js.map +1 -1
  38. package/package.json +14 -5
  39. package/types/exports.d.ts +47 -3
  40. package/types/logic.d.ts +79 -0
  41. package/types/memorio.d.ts +60 -16
  42. package/types/memory.d.ts +118 -0
  43. package/types/session.d.ts +1 -4
  44. package/types/state.d.ts +19 -5
  45. package/types/store.d.ts +1 -4
  46. package/types/temporal.d.ts +95 -0
  47. package/types/useObserver.d.ts +6 -10
  48. package/vsix/memorio.vsix +0 -0
package/llms.txt CHANGED
@@ -80,17 +80,41 @@ state.remove('items')
80
80
  state.removeAll()
81
81
  ```
82
82
 
83
- **Locking - VERIFY BEFORE PUBLISHING:** this library's own documents currently disagree on scope.
83
+ **Locking:** `memorio` implements **both** lock scopes, using distinct method names.
84
84
 
85
- - One source describes **per-key** locking: `state.config.lock()` freezes only the `config` key; other keys remain writable.
86
- - Another source describes **global** locking: `state.lock()` / `state.unlock()` freezing the entire `state` object at once.
85
+ - **Global lock** - `state.lock()` / `state.unlock()`: freezes the entire `state` surface at once. While locked, every write (top-level set, nested mutation, array mutation, and deletion) is rejected and the assignment throws in strict/ESM code; values are left unchanged. Use it for whole-tree immutability.
86
+ - **Per-key lock** - `state.<key>.lock()` / `state.<key>.unlock()`: locks a single first-level node. While that node is locked, reassigning the key, mutating the node's own properties, calling mutating array methods on a locked array, and deleting the node's properties are all rejected. Only that key is affected; every other top-level key stays writable.
87
87
 
88
- These are materially different behaviors - confirm against the actual source which one (or both, with distinct method names) is implemented, then replace this note with the real signature(s). Do not ship a docs update, or generate code against this section, until this is resolved.
88
+ ```javascript
89
+ state.lock() // block all writes across state
90
+ state.user = { name: 'Sara' } // -> throws "Error: state is locked" while held
91
+ state.unlock() // resume writes
92
+
93
+ state.myArray = [1, 2, 3]
94
+ state.myArray.lock() // lock only this key
95
+ state.myArray.push(4) // -> throws "Error: state is locked"
96
+ state.myArray.unlock() // resume writes to myArray only
97
+ ```
98
+
99
+ Per-key locking is first-level only by design: locking `state.a` does not transitively lock `state.a.b`. Lock each descendant explicitly, or use the global lock for whole-subtree immutability.
89
100
 
90
101
  **Features:**
91
102
  - Automatic path tracking via `__path` property
92
103
  - Nested proxy support
93
104
  - Auto-dispatches events on changes
105
+ - `state.persist(path)` mirrors state path to `store` and syncs writes (see "Getting Started" guide)
106
+
107
+ #### `state.persist(path)`
108
+
109
+ Creates a bidirectional mirror between `state` (live in-memory) and `store` (durable copy). Seeds the initial value from `store` if present, then every write to that path flows to `store` automatically.
110
+
111
+ ```javascript
112
+ const stopPersisting = state.persist('state.user')
113
+ state.user = { name: 'Sara' }
114
+ // ... writes to state.user are mirrored to store
115
+
116
+ stopPersisting() // stops mirroring; store value is preserved
117
+ ```
94
118
 
95
119
  ### `store` - localStorage Persistence
96
120
 
@@ -182,6 +206,61 @@ const value = cache['myKey']
182
206
  delete cache['myKey']
183
207
  ```
184
208
 
209
+ ### `memory` - Application Memory (confidence, TTL, tags, lifecycle)
210
+
211
+ Structured key/value memory with confidence scoring, TTL, tagging, scope-based persistence, and full lifecycle (remember/update/supersede/forget).
212
+
213
+ ```javascript
214
+ import { memory } from 'memorio'
215
+
216
+ // Store with metadata
217
+ await memory.remember('user.language', 'Italian', {
218
+ type: 'preference',
219
+ confidence: 0.92,
220
+ tags: ['user', 'ui'],
221
+ source: 'conversation'
222
+ })
223
+
224
+ // Retrieve by key (respects confidence threshold, TTL)
225
+ const language = await memory.recall('user.language')
226
+
227
+ // Update supersedes the old entry (history preserved)
228
+ await memory.update('user.language', 'English', { confidence: 0.95 })
229
+
230
+ // Ranked retrieval - rule-based (tags/type/confidence/recency), NOT embedding search
231
+ const ctx = await memory.context({
232
+ tags: 'user',
233
+ types: ['preference', 'decision'],
234
+ minConfidence: 0.7,
235
+ maxEntries: 10,
236
+ scopes: ['local', 'durable']
237
+ })
238
+ ```
239
+
240
+ **Acquired Knowledge** - `memory.acquire()` stores structured knowledge claims that apply under specific conditions; `context({ acquired })` retrieves only the claims matching the current situation:
241
+
242
+ Autonomous persistent acquisition is experimental and optional. Treat retrieved claims as evidence, not authority, and verify them against current reality. Acquire only non-obvious reusable experience supported by evidence—not repository facts—and keep observation, inference, verified result, and acquired experience distinct. Architecture rationale, evidenced user technical decisions, documentation interpretation, and verified fix consequences are the Phase 1 focus. `nothing worth acquiring` is always valid. See `markdown/MEMORY.md#guidance-for-autonomous-use` for safety guidance and the unresolved autonomous-memory-safety TODO.
243
+
244
+ ```javascript
245
+ // Record a lesson that applies to CSS grid layout with min-content tracks
246
+ await memory.acquire({
247
+ id: 'css.grid.min-content-margins',
248
+ knowledge: { guidance: 'min-content incorporates inner element margins; replacing with fixed width changes effective track width' },
249
+ applies: { area: 'css', component: 'layout', pattern: 'grid-min-content' },
250
+ evidence: [{ id: 'layout-debug-20260918', source: 'real-project' }]
251
+ })
252
+
253
+ // Later, retrieve only knowledge applicable to this specific situation
254
+ const result = await memory.context({
255
+ acquired: { area: 'css', component: 'layout', pattern: 'grid-min-content', element: 'aside' }
256
+ })
257
+ // { knowledgeVersion, claims: [...] }
258
+ ```
259
+
260
+ **Matching semantics:** a claim matches when every key in its `applicability` equals the corresponding key in the query context. When multiple claims match, the most specific (most applicability keys) wins - more specific claims act as overrides, general claims as fallbacks. Use `refines` to link a more-specific claim to a general one, or `supersedes` to replace an older claim entirely.
261
+
262
+ Acquired knowledge persists to `.memorio/project.mem` in Node.js/Bun, or to `store` in browser/edge environments. Make experimental autonomous persistence visible to the user. `memory.clear()` destructively wipes all memories and acquired knowledge; tests must point persistence at an isolated temporary project directory and clean up only that data.
263
+
185
264
  ### `idb` - IndexedDB
186
265
 
187
266
  Structured, persistent, async database (browser-only). **Disabled in Node.js/Deno** - calls will warn and no-op; use `store` or `session` there instead.
@@ -534,6 +613,58 @@ Patch utilities convert mutations to RFC 6902-style patches for replay, sync, an
534
613
  import { mutationToPatch, diffToPatch, canMerge, mergePatches } from 'memorio'
535
614
  ```
536
615
 
616
+ ### `memorio.temporal` - Temporal Memory (Time-Travel Query)
617
+
618
+ The temporal engine maintains an immutable log of every mutation with HLC timestamps, enabling point-in-time queries, diffing, and branching. Temporal tracking is **enabled by default** and independent from `enableHistory()`:
619
+
620
+ ```javascript
621
+ import { state, memorio } from 'memorio'
622
+
623
+ state.counter = 1
624
+ state.counter = 2
625
+ state.counter = 3
626
+
627
+ // History: all mutations at a path, sorted chronologically
628
+ const mutations = memorio.temporal.history('counter')
629
+
630
+ // at(): point-in-time value lookup (returns deep clone, never mutates live state)
631
+ const hlc1 = mutations[0].hlc
632
+ memorio.temporal.at('counter', hlc1) // 1
633
+ memorio.temporal.at('counter', Date.now()) // 3
634
+
635
+ // HLC strings include logical counter for precise per-mutation queries;
636
+ // numeric timestamps include all mutations at the same physical millisecond.
637
+ ```
638
+
639
+ ```javascript
640
+ // diff(): changes within a time range (from is exclusive, to is inclusive)
641
+ const hlc2 = mutations[1].hlc
642
+ memorio.temporal.diff('counter', hlc2, Date.now())
643
+ // [{ path: 'counter', before: 2, after: 3, operation: 'set', hlc, timestamp }]
644
+
645
+ // replay(): reconstruct full state at a point in time
646
+ memorio.temporal.replay() // full history
647
+ memorio.temporal.replay({ from: hlc1, to: hlc2 }) // time range
648
+
649
+ // explain(): provenance metadata for a path
650
+ const info = memorio.temporal.explain('user.role')
651
+ // { path, currentValue, previousValue, history, createdAt, lastChangedAt, ... }
652
+
653
+ // branch(): isolated temporal world (mutations don't affect live state)
654
+ const branch = memorio.temporal.branch()
655
+ branch.state.counter = 99
656
+ memorio.temporal.at('counter', Date.now()) // still 3 in main state
657
+ branch.discard() // or branch.commit()
658
+
659
+ // Configuration
660
+ memorio.temporal.enable(false) // stop recording
661
+ memorio.temporal.isEnabled() // false
662
+ memorio.temporal.configure({ maxEntries: 50000 })
663
+ memorio.temporal.stats() // { total, byPath }
664
+ memorio.temporal.size() // mutation count
665
+ memorio.temporal.clear() // wipe log (does not affect live state)
666
+ ```
667
+
537
668
  ### `memorio.trace()` - Mutation Log
538
669
 
539
670
  ```javascript
@@ -0,0 +1,410 @@
1
+ # Memorio VSCode Extension - Technical Design
2
+
3
+ ## Status
4
+ **Proposed** - Phase 1 (IntelliSense + Hover) ready for implementation.
5
+
6
+ ## 1. Architecture overview
7
+
8
+ ### 1.1 Recommended tooling
9
+
10
+ | Concern | Tool | Rationale |
11
+ |---|---|---|
12
+ | Language/Runtime | TypeScript + Node.js (VSCode extension host) | VS Code extension API is Node-based; no Bun dependency in extension host |
13
+ | Static analysis | **ts-morph** (not raw TS Compiler API) | 80% less boilerplate than raw TS API for AST traversal + pattern matching. `ts-morph` wraps the TS Compiler API with a friendlier query DSL (`getDescendantsOfKind`, `forEachDescendant`). Performance on large workspaces is acceptable with file-level caching (see 3.3). |
14
+ | Webview UI | React + `reactflow` | Industry standard for interactive graph editors; tree-shakable; well-maintained |
15
+ | Webview bundling | `esbuild` (via `vsce`) | Fast bundling for webview assets; `vsce` handles packaging |
16
+ | State sharing | `vscode.Memento` (ExtensionContext.globalState) | Persists layout preferences per-session, not per-workspace |
17
+ | Communication | `postMessage` + `onDidReceiveMessage` | Standard VS Code webview↔extension host pattern |
18
+
19
+ ### 1.2 Architecture decision: single analysis pipeline
20
+
21
+ **Decision**: One AST analysis pass feeds both diagnostics and the graph view. Not two separate pipelines.
22
+
23
+ ```
24
+ Workspace files
25
+ → ts-morph Project (file-level cache)
26
+ → MemorioCallScanner (extracts all memorio.* calls)
27
+ → DiagnosticProvider (writes to vscode.DiagnosticCollection)
28
+ → GraphBuilder (produces GraphSnapshot)
29
+ → StoreGraphPanel (webview)
30
+ ```
31
+
32
+ **Rationale**: Both features need the same raw data - which memorio APIs are called, from which files, with which arguments. Running two traversals doubles startup cost on large workspaces. The scanner produces an intermediate `MemorioCall[]` structure; both consumers read from it.
33
+
34
+ ### 1.3 Performance strategy for large workspaces
35
+
36
+ | Problem | Solution |
37
+ |---|---|
38
+ | Full AST scan on every keystroke | Debounce + file-level cache; only re-scan the changed file |
39
+ | Workspace-wide scan blocks extension host | `vscode.workspace.createFileSystemWatcher` + incremental `TextDocument` change events; scan only open files unless `memorio: Analyze Workspace` is invoked manually |
40
+ | Large files (>5000 lines) | Skip or use syntax-only scan (no type resolution) |
41
+ | ts-morph Project memory | Dispose unused `SourceFile` objects; reuse single `Project` instance with `skipAddingFilesFromTsConfig` and explicit `addSourceFileAtOffset` |
42
+
43
+ ## 2. Project structure
44
+
45
+ ```
46
+ extension/vscode/
47
+ ├── package.json # Extension manifest (contributes, commands, views)
48
+ ├── tsconfig.json
49
+ ├── src/
50
+ │ ├── extension.ts # Entry point: register commands, providers, webview
51
+ │ ├── analyzer/
52
+ │ │ ├── MemorioCallScanner.ts # AST traversal via ts-morph
53
+ │ │ ├── callTypes.ts # MemorioCall, CallKind, LocationInfo types
54
+ │ │ ├── cache.ts # File-level result cache + debouncer
55
+ │ │ └── patterns.ts # Known memorio API patterns (import paths, call signatures)
56
+ │ ├── graph/
57
+ │ │ ├── GraphBuilder.ts # Converts MemorioCall[] → GraphSnapshot
58
+ │ │ ├── schema.ts # GraphNode, GraphEdge, GraphSnapshot types
59
+ │ │ └── nodeKinds.ts # NodeKind enum + metadata defaults
60
+ │ ├── diagnostics/
61
+ │ │ ├── DiagnosticGenerator.ts # Maps MemorioCall[] → vscode.Diagnostic[]
62
+ │ │ ├── rules.ts # Heuristic rule definitions
63
+ │ │ └── RuleSeverity.ts
64
+ │ ├── webview/
65
+ │ │ ├── StoreGraphPanel.ts # WebviewPanel lifecycle, postMessage routing
66
+ │ │ ├── assets/
67
+ │ │ │ ├── index.html
68
+ │ │ │ └── graph-view.tsx # React + ReactFlow component (bundled)
69
+ │ │ └── messageProtocol.ts # Typed protocol: GraphSnapshot ↔ webview messages
70
+ │ ├── lang/
71
+ │ │ ├── memorioCompletion.ts # TypeScript completions (monkeypatch vscode.languages)
72
+ │ │ └── memorioHover.ts # Hover documentation provider
73
+ │ ├── snippets.ts # Snippet definitions
74
+ │ └── utils/
75
+ │ ├── assets.ts # Icon/path helpers
76
+ │ └── logger.ts # Thin wrapper around vscode.OutputChannel
77
+ └── media/
78
+ ├── memorio-icon-dark.svg # Generated (no pre-existing assets in repo)
79
+ └── memorio-icon-light.svg
80
+ ```
81
+
82
+ **No runtime injection**: All files under `analyzer/`, `graph/`, `diagnostics/` are pure read-only static analysis. They do not import from `memoro` - they pattern-match AST nodes against known API names. This ensures zero impact on user code.
83
+
84
+ ## 3. Schema dati del grafo
85
+
86
+ ### 3.1 Types
87
+
88
+ ```ts
89
+ // --- Node kinds (each maps to a memorio concept) ---
90
+ enum NodeKind {
91
+ State = 'state', // import { state } from 'memorio'
92
+ Store = 'store', // import { store } from 'memorio'
93
+ Session = 'session',
94
+ Cache = 'cache',
95
+ IDB = 'idb',
96
+ SQLite = 'sqlite',
97
+ Memory = 'memory',
98
+ Journal = 'journal',
99
+ Context = 'context', // memorio.createContext(...)
100
+ ReduxBridge = 'redux-bridge', // createMemorioReduxBridge(...)
101
+ Observer = 'observer', // observer('path', cb)
102
+ Schema = 'schema', // registerSchema('name', schema)
103
+ SyncProvider = 'sync-provider', // memory.configure({ provider: {...} })
104
+ }
105
+
106
+ // --- Core node type ---
107
+ interface GraphNode {
108
+ id: string // Deterministic: `${filePath}:${callId}`
109
+ kind: NodeKind
110
+ label: string // Derived: context name, store name, or kind
111
+ filePath: string // Absolute path to source file
112
+ range: Range // VS Code range for go-to-definition
113
+ meta: NodeMeta
114
+ }
115
+
116
+ interface NodeMeta {
117
+ persisted: boolean
118
+ encrypted: boolean // true if encryptionKey on context/config
119
+ readonly: boolean // true if only observed, never mutated
120
+ beta: boolean // true for sqlite (alpha), journal (beta)
121
+ schemaRegistered?: string // schema name if registerSchema used
122
+ sizeEstimate?: number // byte estimate if computable
123
+ version?: string // engine version (sqlite: bun:sqlite vs sql.js)
124
+ }
125
+
126
+ // --- Edge types (relationships) ---
127
+ type EdgeKind =
128
+ | 'observes' // observer → store/state
129
+ | 'syncs-to' // memory → sync-provider (push)
130
+ | 'syncs-from' // sync-provider → memory (pull)
131
+ | 'bridges-to' // redux-bridge → redux store
132
+ | 'isolated-from' // context → parent context
133
+ | 'uses-schema' // state/store → schema
134
+ | 'persists-to' // store/session → idb/sqlite
135
+
136
+ interface GraphEdge {
137
+ id: string // `${sourceId}->${targetId}:${kind}`
138
+ source: string // source node id
139
+ target: string // target node id
140
+ kind: EdgeKind
141
+ label?: string // optional: conflict resolution strategy, sync direction
142
+ meta?: EdgeMeta
143
+ }
144
+
145
+ interface EdgeMeta {
146
+ resolution?: 'hlc' | 'custom' | 'remote-wins' // sync edge only
147
+ direction?: 'push' | 'pull' // sync edge only
148
+ path?: string // observes edge: observed path
149
+ }
150
+
151
+ // --- Complete graph snapshot ---
152
+ interface GraphSnapshot {
153
+ version: string // semver: "1.0"
154
+ nodes: GraphNode[]
155
+ edges: GraphEdge[]
156
+ workspaceRoot: string
157
+ generatedAt: number // epoch ms
158
+ }
159
+ ```
160
+
161
+ ### 3.2 Edge examples (from real memorio patterns)
162
+
163
+ ```ts
164
+ // Source file:
165
+ const ctx = memorio.createContext('tenant:acme')
166
+ const encryptedCtx = memorio.createContext('tenant:acme', { encryptionKey: key })
167
+ ctx.store.set('user', { id: 1 })
168
+ encryptedCtx.store.set('secret', { token: 'abc' })
169
+
170
+ memorio.memory.configure({
171
+ provider: {
172
+ push: ops => fetch('/sync', { method: 'POST', body: JSON.stringify(ops) }),
173
+ pull: since => fetch(`/sync?since=${since}`).then(r => r.json())
174
+ },
175
+ resolveConflict: (local, remote) => local.confidence >= remote.confence ? local : remote
176
+ })
177
+ ```
178
+
179
+ Produces:
180
+ ```
181
+ Nodes:
182
+ context[tenant:acme] kind=Context meta.persisted=false
183
+ context[tenant:acme#enc] kind=Context meta.encrypted=true
184
+ store[tenant:acme] kind=Store meta.persisted=false
185
+ store[tenant:acme#enc] kind=Store meta.encrypted=true
186
+ memory[default] kind=Memory meta.persisted=true
187
+
188
+ Edges:
189
+ store[tenant:acme] → observes state kind=observes
190
+ store[tenant:acme#enc] → isolated-from → context[tenant:acme] kind=isolated-from
191
+ memory[default] → syncs-to → sync-provider kind=syncs-to label="custom"
192
+ memory[default] → syncs-from → sync-provider kind=syncs-from label="custom"
193
+ ```
194
+
195
+ ### 3.3 Node metadata inference
196
+
197
+ | Metadata | How inferred |
198
+ |---|---|
199
+ | `persisted` | File imports `store` or `session` or `idb` (not `state`/`cache`) |
200
+ | `encrypted` | Presence of `encryptionKey` arg on `createContext`, or `store.config({ encryptionKey })` |
201
+ | `beta` | `NodeKind.SQLite` → true; `NodeKind.Journal` → true; all others false |
202
+ | `schemaRegistered` | `registerSchema(name, schema)` call detected in same file |
203
+ | `sizeEstimate` | Computed from `store.db.size()` / `idb.db.size()` at runtime (if available); null otherwise |
204
+
205
+ ## 4. Webview component sketch
206
+
207
+ ### 4.1 React Flow node types
208
+
209
+ ```tsx
210
+ // Custom node for store/context (badge shows persistence/encryption)
211
+ function StoreNode({ data }: NodeProps<GraphNode>): JSX.Element {
212
+ const badge = (
213
+ <div className="badges">
214
+ {data.meta.encrypted && <span className="badge badge--encrypted">🔒</span>}
215
+ {data.meta.persisted && <span className="badge badge--persisted">💾</span>}
216
+ {data.meta.beta && <span className="badge badge--beta">β</span>}
217
+ </div>
218
+ )
219
+ return (
220
+ <div className="node store-node">
221
+ <div className="node__header">
222
+ <span className="node__icon">🗄️</span>
223
+ <span className="node__label">{data.label}</span>
224
+ </div>
225
+ {badge}
226
+ <div className="node__meta">
227
+ {data.meta.schemaRegistered && <SchemaTag schema={data.meta.schemaRegistered} />}
228
+ </div>
229
+ </div>
230
+ )
231
+ }
232
+
233
+ // Timeline node (undo/redo snapshot)
234
+ function TimelineNode({ data }: NodeProps<TimelineNodeData>): JSX.Element {
235
+ return (
236
+ <div className="node timeline-node">
237
+ <div className="node__header">
238
+ <span className="node__icon">📸</span>
239
+ <span className="node__label">Snapshot #{data.snapshotId}</span>
240
+ </div>
241
+ <div className="node__detail">{data.mutationCount} mutations</div>
242
+ </div>
243
+ )
244
+ }
245
+ ```
246
+
247
+ ### 4.2 Sidebar detail panel (click a node)
248
+
249
+ ```tsx
250
+ function NodeDetailsPanel({ node }: { node: GraphNode | undefined }) {
251
+ if (!node) return <div className="details--empty">Select a node</div>
252
+
253
+ return (
254
+ <div className="details-panel">
255
+ <h3>{node.label}</h3>
256
+ <dl>
257
+ <dt>Layer</dt><dd>{node.kind}</dd>
258
+ <dt>File</dt><dd><a href={`vscode://file/${node.filePath}`}>{node.filePath}</a></dd>
259
+ <dt>Persisted</dt><dd>{node.meta.persisted ? 'Yes' : 'No'}</dd>
260
+ <dt>Encrypted</dt><dd>{node.meta.encrypted ? 'Yes' : 'No'}</dd>
261
+ {node.meta.schemaRegistered && <><dt>Schema</dt><dd>{node.meta.schemaRegistered}</dd></>}
262
+ {node.meta.beta && <><dt>Beta</dt><dd>Experimental API - expect changes</dd></>}
263
+ </dl>
264
+ <button onClick={() => openDefinition(node.filePath, node.range)}>
265
+ Go to definition
266
+ </button>
267
+ </div>
268
+ )
269
+ }
270
+ ```
271
+
272
+ ### 4.3 Timeline toggle
273
+
274
+ A `Toggle` button in the webview toolbar switches the graph from **Layer view** (default graph) to **Timeline view** (linear undo/redo history for the selected store). Timeline nodes are populated from `trace()` output if the user's code calls `memorio.trace()`.
275
+
276
+ ## 5. Commands and settings
277
+
278
+ ### 5.1 Commands (`package.json` `contributes.commands`)
279
+
280
+ | Command ID | Title | When |
281
+ |---|---|---|
282
+ | `memorio.analyzeWorkspace` | Memorio: Analyze Workspace | editorTextFocus |
283
+ | `memorio.toggleGraphView` | Memorio: Show Store Graph | view:memorioGraphView |
284
+ | `memorio.refreshGraph` | Memorio: Refresh Graph | webviewFocused |
285
+ | `memorio.toggleTimeline` | Memorio: Toggle Timeline | webviewFocused |
286
+ | `memorio.openLogs` | Memorio: Open Logs | view:memorioGraphView |
287
+ | `memorio.openExtensionSettings` | Memorio: Settings | view:memorioGraphView |
288
+
289
+ ### 5.2 View containers
290
+
291
+ | View | Location | Type |
292
+ |---|---|---|
293
+ | `memorioGraphView` | `explorer` sidebar | WebviewView (graph panel in sidebar) |
294
+ | `memorioTimelineView` | `memorioGraphView` tab | WebviewPanel (tabbed view in graph view) |
295
+
296
+ ### 5.3 Settings (`package.json` `contributes.configuration`)
297
+
298
+ | Setting | Default | Description |
299
+ |---|---|---|
300
+ | `memorio.analysis.includePatterns` | `["**/*.{ts,tsx,js,jsx}"]` | Glob patterns for files to analyze |
301
+ | `memorio.analysis.excludePatterns` | `["**/node_modules/**", "**/dist/**"]` | Glob patterns to skip |
302
+ | `memorio.graph.autoRefresh` | `true` | Auto-refresh graph on file save |
303
+ | `memorio.diagnostics.enable` | `true` | Enable real-time diagnostic warnings |
304
+ | `memorio.graph.saveLayout` | `"session"` | `session` (reset on reload) / `workspace` (persist to .vscode) |
305
+
306
+ ### 5.4 Comparison with competitor
307
+
308
+ | Feature | Competitor (Memorio) | This extension |
309
+ |---|---|---|
310
+ | IntelliSense | ✅ autocomplete on memorio imports | ✅ + hover docs with link to README sections |
311
+ | Hover | Basic type info | ✅ Type + options + doc link + example |
312
+ | Snippets | Template snippets only | ✅ + dynamic snippets based on context/schema |
313
+ | Diagnostics | Warning for missing namespace | ✅ + risk pattern detection (__proto__, constructor) + hint on `includeObsolete` |
314
+ | State Explorer | Static text tree | ✅ Interactive React Flow graph with node metadata |
315
+ | - | - | ✅ Timeline view (undo/redo history) |
316
+ | - | - | ✅ Conflict resolution strategy labels on sync edges |
317
+ | - | - | ✅ Incremental workspace analysis (debounced, cached) |
318
+
319
+ ## 6. Diagnostic rules (heuristic, AST-based)
320
+
321
+ **⚠️ Declaration**: These diagnostics are heuristic - based on AST pattern matching, not a TypeScript type-checker. They reduce false negatives but may produce false positives. They never block compilation or execution.
322
+
323
+ | Rule ID | Severity | Trigger pattern | Message |
324
+ |---|---|---|---|
325
+ | `memorio-missing-store` | Hint | `observer('state.x', cb)` without prior `store`/`session` import in same file | "State observed but no persistence layer imported - changes won't survive reload" |
326
+ | `memorio-include-obsolete-hint` | Information | `memory.recall(key)` without `{ includeObsolete: true }` | "Consider `includeObsolete: true` to see superseded values for this key" |
327
+ | `memorio-prototype-pollution-risk` | Warning | `state[key] = value` where `key` is a variable (dynamic key on state proxy) | "Dynamic key assignment to state may cause prototype pollution - use literal keys or `typed<T>()`" |
328
+ | `memorio-__proto-assignment` | Error | `state.__proto__` or `state.constructor` or `state.prototype` anywhere | "Assignment to `__proto__`/`constructor`/`prototype` can cause prototype pollution - use `typed<T>()` for schema-safe state" |
329
+ | `memorio-unencrypted-store` | Hint | `store.set('token', value)` without `store.config({ encryptionKey })` | "Token stored without encryption - consider `store.config({ encryptionKey })`" |
330
+ | `memorio-sqlite-beta` | Information | `sqlite` import in production file | "sqlite is beta - API may change in minor releases" |
331
+
332
+ ## 7. React Flow component architecture
333
+
334
+ ```
335
+ StoreGraphView (React Flow Canvas)
336
+ ├── NodeTypes: StoreNode, ContextNode, MemoryNode, SchemaNode, ReduxBridgeNode, TimelineNode
337
+ ├── EdgeTypes: default (arrow), SyncEdge (dashed, labeled)
338
+ ├── Controls: MiniMap, Controls (zoom), Background (grid)
339
+ ├── Toolbar: FilterToggle, TimelineToggle, RefreshButton
340
+ └── Sidebar: NodeDetailsPanel (conditional)
341
+
342
+ FilterToggle → filters by NodeKind and meta flags (beta, encrypted)
343
+ TimelineToggle → swaps edges/nodes to timeline view for selected store
344
+ ```
345
+
346
+ ## 8. Message protocol (extension host ↔ webview)
347
+
348
+ ```ts
349
+ type WebviewMessage =
350
+ | { type: 'graphSnapshot'; data: GraphSnapshot }
351
+ | { type: 'nodeSelected'; nodeId: string }
352
+ | { type: 'goToDefinition'; filePath: string; range: Range }
353
+ | { type: 'refreshRequested' }
354
+ | { type: 'filterChanged'; nodeKinds: NodeKind[] }
355
+
356
+ type HostMessage =
357
+ | { type: 'webviewReady' }
358
+ | { type: 'definitionRequested'; filePath: string; range: Range }
359
+ | { type: 'filterChanged'; nodeKinds: NodeKind[] }
360
+ | { type: 'timelineToggle' }
361
+ ```
362
+
363
+ ## 9. Roadmap a fasi
364
+
365
+ ### Fase 1 - IntelliSense + Hover (2-3 developer-days)
366
+ - `memorioCompletion.ts`: autocomplete for `state.*`, `store.*`, `memorio.memory.*`, etc.
367
+ - `memorioHover.ts`: hover shows type + options + doc link
368
+ - Snippets: `ctx-enc` (createContext with encryption), `schema-array` (registerSchema), `memory-sync` (configure with provider)
369
+ - **No graph view yet** - can ship independently, provides immediate value
370
+
371
+ ### Fase 2 - Diagnostica (1-2 developer-days)
372
+ - `DiagnosticGenerator.ts` + `rules.ts`
373
+ - `memorio-__proto-assignment`, `memorio-prototype-pollution-risk`, `memorio-missing-store`, `memorio-include-obsolete-hint`
374
+ - Integrates into the MemorioCallScanner from Fase 1
375
+ - **No webview needed**
376
+
377
+ ### Fase 3 - Graph view statica (5-7 developer-days)
378
+ - `GraphBuilder.ts` + `StoreGraphPanel.ts`
379
+ - React Flow webview with store/context/memory/sqlite nodes, edge rendering (observes, isolate, bridges)
380
+ - Basic sidebar details (layer, file, persisted, encrypted, beta)
381
+ - Go-to-definition integration
382
+ - **No timeline yet**
383
+
384
+ ### Fase 4 - Timeline undo/redo (3-4 developer-days)
385
+ - Toggle button in webview toolbar
386
+ - Timeline nodes from `trace()` output
387
+ - Linear temporal view of snapshots/operations per selected store
388
+ - **Requires timeline node type + TimelineNode component**
389
+
390
+ ### Fase 5 - Aggiornamento incrementale + performance (2-3 developer-days)
391
+ - File-level cache (`cache.ts`)
392
+ - Debounced re-analysis on save
393
+ - Large file skipping (syntax-only mode for files >5000 lines)
394
+ - `globalState` (VSCode Memento) layout persistence with `session` mode (no disk writes per design constraint)
395
+
396
+ ### Out of scope (future)
397
+ - Remote sync provider connection (would require user's backend integration)
398
+ - Runtime state inspection (would require injecting code into user's process - violates the "no runtime injection" constraint)
399
+ - Export graph to image/PNG (nice-to-have, no core value)
400
+
401
+ ## 10. Constraints honored
402
+
403
+ | Constraint | How met |
404
+ |---|---|
405
+ | No security/compliance claims in marketing | Diagnostics use technical descriptions ("may cause prototype pollution"), not "security layer" language |
406
+ | Diagnostics declared as heuristic | Explicit ⚠️ at top of §6, rule docs say "AST pattern matching, not type-checker" |
407
+ | No changes to memorio public API | All analyzer modules are read-only AST pattern matching; no imports from `memorio` |
408
+ | Honest naming | "Store Graph View" (not "State Explorer"), "Dependency graph" only if the graph shows dependencies (which it does: observes → syncs → bridges) |
409
+ | Cross-runtime support | Extension is Node.js-only (VSCode extension host), but documents the cross-platform support for the underlying library |
410
+ | No runtime injection | Static analysis only - zero code executed from analyzed files |
@@ -0,0 +1,100 @@
1
+ # `memorio.logic`
2
+
3
+ > **Status:** IMPLEMENTED and VERIFIED — PHASE 0 PASS
4
+ > **Scope:** Current public runtime contract; not a PHASE 1 roadmap
5
+
6
+ `memorio.logic` records structured situation/action/outcome experiences, selects applicable
7
+ precedents, derives simple recommendations, and reports measurements. It reuses acquired-knowledge
8
+ primitives; it is not an autonomous reasoning engine, truth engine, causal graph, or replacement for
9
+ AI reasoning.
10
+
11
+ ## Public API
12
+
13
+ ```ts
14
+ import { logic, LogicEngine } from 'memorio'
15
+
16
+ await logic.experience({
17
+ id: 'exp.locking-1',
18
+ situation: { symptom: 'a nested write bypassed the intended guard' },
19
+ action: { correction: 'apply the node-level guard' },
20
+ outcome: { result: 'the contract test passes' },
21
+ evidence: [{ id: 'locking-contract', source: 'test-suite' }],
22
+ applicability: { layer: 'logic', topic: 'locking' },
23
+ epistemicType: 'verified'
24
+ })
25
+
26
+ const precedents = await logic.precedents({
27
+ context: { layer: 'logic', topic: 'locking' }
28
+ })
29
+ ```
30
+
31
+ The PHASE 0 surface is:
32
+
33
+ - `experience(opts): Promise<void>` — append an immutable experience claim.
34
+ - `precedents(query): Promise<AcquiredClaim[]>` — return selected active claims.
35
+ - `context(query): Promise<AcquiredContext>` — return selected claims plus repository version.
36
+ - `measure(query): Promise<LogicMeasurement>` — report all active Logic claims, selected claims,
37
+ selected IDs, repository version, and the current process-wide temporal mutation count.
38
+ - `learn(query): Promise<LogicRule[]>` — project selected experiences into rules. The recommendation
39
+ is the stored `outcome`; confidence is a fixed mapping from epistemic type, not learned statistics.
40
+ - `stats(): Promise<{ experiences: number; version: number }>` — count repository-level active claims.
41
+ - `clear(): Promise<void>` — delete only the writable `logic.mem` source.
42
+ - `configure({ disabled })` — make the singleton a no-op while disabled.
43
+
44
+ `logic` is a singleton instance of `LogicEngine`. It is available as a named module export and as
45
+ `globalThis.memorio.logic`; explicit global installation also includes `globalThis.logic`.
46
+
47
+ ## Persistence and isolation
48
+
49
+ In Node.js/Bun, Logic uses `.memorio/logic/logic.mem`. Acquired project experience uses
50
+ `.memorio/project.mem`.
51
+
52
+ These are intentionally separate `MemDirectoryKnowledgeRepository` instances rooted in different
53
+ directories. Each discovers, combines, locks, versions, clears, validates IDs, and validates revision
54
+ references only within its own repository. Consequently:
55
+
56
+ - Logic does not discover or clear `project.mem`.
57
+ - Project memory does not discover or clear `logic.mem`.
58
+ - Claim-ID uniqueness and `refines`/`supersedes`/`retracts` references are repository-local.
59
+ - Cross-repository revision chains and unified provenance are not implemented.
60
+
61
+ This isolation is a current architectural boundary, not evidence that cross-repository composition
62
+ exists. When no working-directory runtime is available, Logic uses an in-memory repository. Unlike
63
+ acquired project memory, PHASE 0 Logic does not use the browser `store` fallback.
64
+
65
+ ## Selection and the `_all` correction
66
+
67
+ Logic context selection uses the same exact structured applicability and most-specific-wins semantics
68
+ as acquired memory. PHASE 0 initially added an `_all` selector sentinel so `stats()` and `measure()`
69
+ could count all claims. Focused design verification found that it violated the selector invariant,
70
+ could collide with a real applicability key, and still selected only maximum-specificity candidates.
71
+
72
+ The sentinel was removed. `stats()` and the `total` field of `measure()` now load the Logic repository
73
+ and count `activeClaims(state)` directly. A regression test covers mixed applicability specificity.
74
+ The chronology is retained in project history; this document describes the corrected current state.
75
+
76
+ ```text
77
+ initial Logic implementation
78
+ -> contract tests pass
79
+ -> focused design verification
80
+ -> `_all` selector problem discovered
81
+ -> HI + AI decision
82
+ -> `_all` removed
83
+ -> stats()/measure() use repository.load() + activeClaims()
84
+ -> mixed-specificity regression verification
85
+ -> PHASE 0 PASS
86
+ ```
87
+
88
+ The regression uses three active claims with three, two, and one applicability keys. The corrected
89
+ result is `stats().experiences === 3`; the rejected wildcard approach could incorrectly return `1`.
90
+
91
+ ## Verification boundary
92
+
93
+ The PHASE 0 contract test verifies the singleton, API surface, global exposure, experience selection,
94
+ revision, measurement, rule projection, stats, disabled behavior, clearing, repository isolation, and
95
+ the mixed-specificity regression. This verifies mechanics, not the truth of stored experience.
96
+
97
+ ## Not implemented
98
+
99
+ PHASE 0 does not implement semantic similarity, autonomous acquisition, autonomous truth evaluation,
100
+ causal inference, cross-repository composition, identity/authority policy, trust scoring, or PHASE 1.