memorio 5.1.4 → 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 (45) hide show
  1. package/AGENTS.md +21 -12
  2. package/CHANGELOG.md +18 -6
  3. package/README.md +44 -7
  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/sync.ts +90 -90
  16. package/examples/useObserver.tsx +2 -2
  17. package/global.cjs +1995 -124
  18. package/global.js +1990 -125
  19. package/index.cjs +1995 -124
  20. package/index.d.ts +1 -0
  21. package/index.js +1990 -125
  22. package/llms.txt +122 -4
  23. package/markdown/EXTENSION_VSCODE_DESIGN.md +410 -0
  24. package/markdown/LOGIC.md +100 -0
  25. package/markdown/MEMORY-ATTACHMENT.md +17 -10
  26. package/markdown/MEMORY.md +378 -15
  27. package/markdown/MEM_FORMAT.md +313 -0
  28. package/markdown/STATE.md +27 -3
  29. package/markdown/SYNC.md +18 -14
  30. package/markdown/TEMPORAL.md +297 -0
  31. package/markdown/USEOBSERVER.md +7 -4
  32. package/modules/redux.cjs +1159 -32
  33. package/modules/redux.cjs.map +1 -1
  34. package/modules/redux.js +1158 -32
  35. package/modules/redux.js.map +1 -1
  36. package/package.json +14 -5
  37. package/types/exports.d.ts +47 -3
  38. package/types/logic.d.ts +79 -0
  39. package/types/memorio.d.ts +60 -16
  40. package/types/memory.d.ts +118 -0
  41. package/types/session.d.ts +1 -4
  42. package/types/store.d.ts +1 -4
  43. package/types/temporal.d.ts +95 -0
  44. package/types/useObserver.d.ts +6 -10
  45. package/vsix/memorio.vsix +0 -0
@@ -0,0 +1,140 @@
1
+ /**
2
+ * agent-memory-demo.ts
3
+ *
4
+ * Scenario: An AI agent that remembers user preferences across conversations.
5
+ *
6
+ * Demonstrates:
7
+ * - Conversation 1: agent remembers a user preference
8
+ * - Conversation 2: agent recalls the preference
9
+ * - Context generation: agent gets relevant context for a query
10
+ * - Temporal history: agent queries how state evolved
11
+ * - Provenance: agent explains why it knows what it knows
12
+ */
13
+
14
+ import { createAgentMemory } from '../../agent'
15
+
16
+ async function main() {
17
+ const agent = createAgentMemory('support-agent')
18
+
19
+ // Clear any prior state for a clean demo
20
+ await agent.clear()
21
+
22
+ // ─── Conversation 1 ───────────────────────────────────────────
23
+ console.log('=== Conversation 1 ===')
24
+ console.log('User: "I prefer PostgreSQL for databases."')
25
+
26
+ // Agent remembers the preference with metadata
27
+ await agent.remember('user.pref.database', 'PostgreSQL', {
28
+ type: 'preference',
29
+ confidence: 0.91,
30
+ source: 'conversation-1',
31
+ tags: ['database', 'infrastructure'],
32
+ ttl: null // persistent memory
33
+ })
34
+
35
+ console.log('Agent: "Got it. PostgreSQL it is."')
36
+ console.log('')
37
+
38
+ // ─── Conversation 2 ─────────────────────────────────────────
39
+ console.log('=== Conversation 2 (later) ===')
40
+ console.log('User: "What database did I say I preferred?"')
41
+
42
+ // Agent recalls the memory
43
+ const preference = await agent.recall('user.pref.database')
44
+ console.log(`Agent: "You said ${preference?.value}."`)
45
+ console.log('')
46
+
47
+ // ─── Context Generation ─────────────────────────────────────
48
+ console.log('=== Context for Agent Query ===')
49
+ console.log('User: "What databases do you know about for this project?"')
50
+
51
+ const context = await agent.context({
52
+ tags: ['database'],
53
+ types: ['preference', 'decision', 'fact'],
54
+ minConfidence: 0.5,
55
+ maxEntries: 10
56
+ })
57
+
58
+ console.log('Agent Context (relevant memories):')
59
+ for (const entry of context) {
60
+ console.log(` - ${entry.key}: ${entry.value}`)
61
+ console.log(` type: ${entry.type}, confidence: ${entry.confidence}, age: ${Math.round(entry.age / 1000)}s`)
62
+ console.log(` tags: ${entry.tags.join(', ')}`)
63
+ }
64
+ console.log('')
65
+
66
+ // ─── Memory Evolution (supersedence) ─────────────────────────
67
+ console.log('=== Memory Evolution ===')
68
+
69
+ // Update the preference (creates superseded entries)
70
+ await agent.remember('user.pref.database', 'MySQL', {
71
+ type: 'preference',
72
+ confidence: 0.85,
73
+ source: 'conversation-3',
74
+ tags: ['database', 'infrastructure']
75
+ })
76
+
77
+ // Small delay to ensure unique timestamps for superseded entries
78
+ await new Promise(resolve => setTimeout(resolve, 5))
79
+
80
+ await agent.remember('user.pref.database', 'SQLite', {
81
+ type: 'preference',
82
+ confidence: 0.92,
83
+ source: 'conversation-4',
84
+ tags: ['database', 'infrastructure']
85
+ })
86
+
87
+ // Query the latest state
88
+ const latest = await agent.recall('user.pref.database')
89
+ console.log(`Latest preference: ${latest?.value} (confidence: ${latest?.confidence})`)
90
+
91
+ // Query all entries including superseded
92
+ const mem = (globalThis as any).memorio.memory
93
+ const allEntries = await mem._loadAll()
94
+ const keyHistory = allEntries
95
+ .filter((e: any) => e.key === `agent:support-agent:user.pref.database`)
96
+ .sort((a: any, b: any) => a.createdAt - b.createdAt)
97
+
98
+ console.log('History of this decision:')
99
+ for (const entry of keyHistory) {
100
+ const statusIcon = entry.status === 'superseded' ? ' (superseded)' : ' (current)'
101
+ console.log(` - ${new Date(entry.createdAt).toISOString()}: ${entry.value}${statusIcon}`)
102
+ console.log(` source: ${entry.source}, confidence: ${entry.confidence}`)
103
+ }
104
+ console.log('')
105
+
106
+ // ─── Provenance / Explain ───────────────────────────────────
107
+ console.log('=== Provenance ===')
108
+ console.log('User: "Why do you know SQLite?"')
109
+
110
+ const current = await agent.recall('user.pref.database')
111
+ console.log(`Agent: "I know SQLite because:"`)
112
+ console.log(` - Source: ${current?.source}`)
113
+ console.log(` - Confidence: ${current?.confidence}`)
114
+ console.log(` - Stored at: ${new Date(current!.createdAt).toISOString()}`)
115
+ console.log(` - Tags: ${current?.tags.join(', ')}`)
116
+ console.log('')
117
+
118
+ // Show why we know about PostgreSQL (even though it was superseded)
119
+ const postgresqlEntry = keyHistory.find((e: any) => e.value === 'PostgreSQL')
120
+ if (postgresqlEntry) {
121
+ console.log(`Agent: "PostgreSQL was previously known because:"`)
122
+ console.log(` - Source: ${postgresqlEntry.source}`)
123
+ console.log(` - Confidence: ${postgresqlEntry.confidence}`)
124
+ console.log(` - Status: ${postgresqlEntry.status}`)
125
+ console.log(` - Stored at: ${new Date(postgresqlEntry.createdAt).toISOString()}`)
126
+ console.log(` - Tags: ${postgresqlEntry.tags.join(', ')}`)
127
+ }
128
+ console.log('')
129
+
130
+ // ─── Stats ─────────────────────────────────────────────────
131
+ console.log('=== Memory Stats ===')
132
+ const stats = await agent.stats()
133
+ console.log(`Total memories: ${stats.total}`)
134
+ console.log(`By scope:`, stats.byScope)
135
+ console.log(`By type:`, stats.byType)
136
+
137
+ console.log('\n=== Demo Complete ===')
138
+ }
139
+
140
+ main().catch(console.error)
package/examples/sync.ts CHANGED
@@ -1,90 +1,90 @@
1
- /**
2
- * sync.ts
3
- *
4
- * Scenario: an app using `memorio.memory` that wants to survive being
5
- * offline (data is born local, in the journal, and always readable) and
6
- * mirror itself to a backend when a connection is available.
7
- *
8
- * The cloud is a transport/persistence provider here, never the source of
9
- * truth - sync mirrors the local journal, it doesn't replace it. Provide
10
- * your own `SyncProvider`; memorio doesn't ship one.
11
- */
12
- import { memorio } from 'memorio'
13
-
14
- interface MemoryEntry {
15
- key: string
16
- value: unknown
17
- confidence?: number
18
- source?: string
19
- scope?: 'device' | 'user' | 'shared'
20
- }
21
-
22
- interface SyncProvider {
23
- push(ops: MemoryEntry[]): Promise<{ synced: string[]; conflicts?: string[]; error?: string }>
24
- pull?(since?: number): Promise<MemoryEntry[]>
25
- resolve?(op: MemoryEntry): Promise<'local' | 'remote' | 'merge'>
26
- }
27
-
28
- const backendProvider: SyncProvider = {
29
- push: ops =>
30
- fetch('/api/sync', {
31
- method: 'POST',
32
- body: JSON.stringify(ops),
33
- headers: { 'Content-Type': 'application/json' }
34
- }).then(r => r.json()),
35
-
36
- pull: since => fetch(`/api/sync?since=${since ?? 0}`).then(r => r.json()),
37
-
38
- // Higher-confidence entry wins locally; for 'shared' scope the provider
39
- // (i.e. your backend) makes the final call instead.
40
- resolve: async op => (op.confidence ?? 0) >= 0.8 ? 'local' : 'remote'
41
- }
42
-
43
- export async function configureSync(namespace: string) {
44
- // `scope: 'device'` data never syncs (sticky to this device) - only
45
- // 'user' and 'shared' scoped entries flow through the provider below.
46
- memorio.memory.configure({
47
- namespace, // e.g. `user:123:device:abc` - partitions the local journal
48
- provider: backendProvider,
49
- auto: true // auto-replay the journal on focus/online (default: true)
50
- })
51
-
52
- await memorio.memory.ready
53
- }
54
-
55
- export async function rememberAndSync(key: string, value: unknown) {
56
- // Local-first: this resolves immediately from the local store, no
57
- // network round-trip required.
58
- await memorio.memory.remember(key, value, { scope: 'user', source: 'app' })
59
- }
60
-
61
- // ---------------------------------------------------------------------------
62
- // The journal: the durable op log that drives cloud reconciliation
63
- // ---------------------------------------------------------------------------
64
-
65
- export async function inspectPendingOps() {
66
- const pending = await memorio.memory.journal.pending()
67
- console.debug('Operations waiting to sync:', pending)
68
- return pending
69
- }
70
-
71
- export async function forceReplay() {
72
- // Pushes pending() to the provider, marks synced entries, and pulls if
73
- // the provider implements it. Normally triggered automatically (`auto:
74
- // true`) on focus/online - call this manually to sync on demand instead.
75
- const ack = await memorio.memory.journal.replay()
76
- console.debug('Sync result:', ack)
77
- return ack
78
- }
79
-
80
- export async function resetJournal() {
81
- // Wipes the current namespace's journal only - does not touch the
82
- // memory entries themselves, just the pending sync log.
83
- await memorio.memory.journal.clear()
84
- }
85
-
86
- // Usage:
87
- // await configureSync('user:123:device:abc')
88
- // await rememberAndSync('user.language', 'Italian')
89
- // await inspectPendingOps()
90
- // await forceReplay()
1
+ /**
2
+ * sync.ts
3
+ *
4
+ * Scenario: an app using `memorio.memory` that wants to survive being
5
+ * offline (data is born local, in the journal, and always readable) and
6
+ * mirror itself to a backend when a connection is available.
7
+ *
8
+ * The cloud is a transport/persistence provider here, never the source of
9
+ * truth - sync mirrors the local journal, it doesn't replace it. Provide
10
+ * your own `SyncProvider`; memorio doesn't ship one.
11
+ */
12
+ import { memorio } from 'memorio'
13
+
14
+ interface MemoryEntry {
15
+ key: string
16
+ value: unknown
17
+ confidence?: number
18
+ source?: string
19
+ scope?: 'hot' | 'session' | 'local' | 'durable'
20
+ }
21
+
22
+ interface SyncProvider {
23
+ push(ops: MemoryEntry[]): Promise<{ synced: string[]; conflicts?: string[]; error?: string }>
24
+ pull?(since?: number): Promise<MemoryEntry[]>
25
+ resolveConflict?(local: MemoryEntry, remote: MemoryEntry): Promise<'local' | 'remote' | 'merge'>
26
+ }
27
+
28
+ const backendProvider: SyncProvider = {
29
+ push: ops =>
30
+ fetch('/api/sync', {
31
+ method: 'POST',
32
+ body: JSON.stringify(ops),
33
+ headers: { 'Content-Type': 'application/json' }
34
+ }).then(r => r.json()),
35
+
36
+ pull: since => fetch(`/api/sync?since=${since ?? 0}`).then(r => r.json()),
37
+
38
+ // Higher-confidence entry wins locally; for 'shared' scope the provider
39
+ // (i.e. your backend) makes the final call instead.
40
+ resolveConflict: async (local, remote) => (local.confidence ?? 0) >= 0.8 ? 'local' : 'remote'
41
+ }
42
+
43
+ export async function configureSync(namespace: string) {
44
+ // Namespace partitions the journal; only entries written while sync is
45
+ // configured (via configure()) flow through the provider below.
46
+ memorio.memory.configure({
47
+ namespace, // e.g. `user:123:device:abc` - partitions the local journal
48
+ provider: backendProvider,
49
+ auto: true // auto-replay the journal on focus/online (default: true)
50
+ })
51
+
52
+ await memorio.memory.journal.status() // substrate is 'store'; journal is ready
53
+ }
54
+
55
+ export async function rememberAndSync(key: string, value: unknown) {
56
+ // Local-first: this resolves immediately from the local store, no
57
+ // network round-trip required.
58
+ await memorio.memory.remember(key, value, { scope: 'local', source: 'app' })
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // The journal: the durable op log that drives cloud reconciliation
63
+ // ---------------------------------------------------------------------------
64
+
65
+ export async function inspectPendingOps() {
66
+ const pending = await memorio.memory.journal.pending()
67
+ console.debug('Operations waiting to sync:', pending)
68
+ return pending
69
+ }
70
+
71
+ export async function forceReplay() {
72
+ // Pushes pending() to the provider, marks synced entries, and pulls if
73
+ // the provider implements it. Normally triggered automatically (`auto:
74
+ // true`) on focus/online - call this manually to sync on demand instead.
75
+ const ack = await memorio.memory.journal.replay()
76
+ console.debug('Sync result:', ack)
77
+ return ack
78
+ }
79
+
80
+ export async function resetJournal() {
81
+ // Wipes the current namespace's journal only - does not touch the
82
+ // memory entries themselves, just the pending sync log.
83
+ await memorio.memory.journal.clear()
84
+ }
85
+
86
+ // Usage:
87
+ // await configureSync('user:123:device:abc')
88
+ // await rememberAndSync('user.language', 'Italian')
89
+ // await inspectPendingOps()
90
+ // await forceReplay()
@@ -58,8 +58,8 @@ function AutoDiscovery() {
58
58
  function SyncedComponent() {
59
59
  const [localData, setLocalData] = useState(null)
60
60
 
61
- useObserver((newValue: unknown) => {
62
- setLocalData(newValue)
61
+ useObserver(() => {
62
+ setLocalData(state.data)
63
63
  }, [state.data])
64
64
 
65
65
  return <div>{localData}</div>