@rlabs-inc/memory 0.4.13 → 0.4.15
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.
- package/README.md +30 -0
- package/package.json +1 -1
- package/src/core/engine.ts +112 -2
- package/src/core/retrieval.ts +62 -0
- package/src/core/store.ts +107 -0
- package/src/server/index.ts +73 -9
package/README.md
CHANGED
|
@@ -46,6 +46,21 @@ That's it. Now use Claude Code normally—memories are extracted and surfaced au
|
|
|
46
46
|
|
|
47
47
|
## Features
|
|
48
48
|
|
|
49
|
+
### Action Items Signal (`***`)
|
|
50
|
+
Add `***` at the end of any message to retrieve all pending action items:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
"hey Watson, let's continue with the project ***"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This returns all memories marked as:
|
|
57
|
+
- `action_required: true`
|
|
58
|
+
- `awaiting_implementation: true`
|
|
59
|
+
- `awaiting_decision: true`
|
|
60
|
+
- `context_type: 'unresolved'`
|
|
61
|
+
|
|
62
|
+
Zero overhead for normal messages—detection is a simple `endsWith` check.
|
|
63
|
+
|
|
49
64
|
### Semantic Embeddings
|
|
50
65
|
Uses `all-MiniLM-L6-v2` for 384-dimensional embeddings. Memories are retrieved by meaning, not just keywords.
|
|
51
66
|
|
|
@@ -383,6 +398,21 @@ This isn't just about remembering facts. It's about preserving:
|
|
|
383
398
|
|
|
384
399
|
## Changelog
|
|
385
400
|
|
|
401
|
+
### v0.4.15
|
|
402
|
+
- **Feature**: PATCH `/memory/:id` endpoint for updating memory metadata
|
|
403
|
+
- Supports: `importance_weight`, `exclude_from_retrieval`, `status`, `action_required`, and more
|
|
404
|
+
- Enables dashboards and tools for memory curation (promote/demote/bury)
|
|
405
|
+
|
|
406
|
+
### v0.4.14
|
|
407
|
+
- **Feature**: Action items signal (`***`) - add to end of message to retrieve all pending items
|
|
408
|
+
- **Feature**: New `getActionItems()` retrieval function with special formatting
|
|
409
|
+
- Zero overhead for normal messages - detection is a simple string check
|
|
410
|
+
|
|
411
|
+
### v0.4.13
|
|
412
|
+
- **Fix**: Segmented transcript curation for large sessions (400+ messages)
|
|
413
|
+
- **Improvement**: Unified curator fallback and ingest command behavior
|
|
414
|
+
- **Tech**: Segments at ~150k tokens, accumulates memories, merges summaries with part markers
|
|
415
|
+
|
|
386
416
|
### v0.4.12
|
|
387
417
|
- **Simplify**: Removed Zod structured outputs - session resumption only
|
|
388
418
|
- **Improvement**: Uses existing battle-tested JSON parser instead of Zod
|
package/package.json
CHANGED
package/src/core/engine.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { homedir } from 'os'
|
|
7
7
|
import { join } from 'path'
|
|
8
8
|
import { MemoryStore, createStore } from './store.ts'
|
|
9
|
-
import { SmartVectorRetrieval, createRetrieval, type SessionContext } from './retrieval.ts'
|
|
9
|
+
import { SmartVectorRetrieval, createRetrieval, getActionItems, type SessionContext } from './retrieval.ts'
|
|
10
10
|
import type {
|
|
11
11
|
CuratedMemory,
|
|
12
12
|
StoredMemory,
|
|
@@ -66,6 +66,13 @@ export interface EngineConfig {
|
|
|
66
66
|
personalMemoriesEnabled?: boolean
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Retrieval mode
|
|
71
|
+
* - 'normal': Standard activation signal retrieval (default)
|
|
72
|
+
* - 'action_items': Return all memories marked as requiring action
|
|
73
|
+
*/
|
|
74
|
+
export type RetrievalMode = 'normal' | 'action_items'
|
|
75
|
+
|
|
69
76
|
/**
|
|
70
77
|
* Context request parameters
|
|
71
78
|
*/
|
|
@@ -75,6 +82,7 @@ export interface ContextRequest {
|
|
|
75
82
|
currentMessage: string
|
|
76
83
|
maxMemories?: number
|
|
77
84
|
projectPath?: string // Required for 'local' storage mode
|
|
85
|
+
mode?: RetrievalMode // Retrieval mode (default: 'normal')
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
/**
|
|
@@ -212,7 +220,23 @@ export class MemoryEngine {
|
|
|
212
220
|
return { memories: [], formatted: '' }
|
|
213
221
|
}
|
|
214
222
|
|
|
215
|
-
//
|
|
223
|
+
// ACTION ITEMS MODE: Return all memories marked as requiring action
|
|
224
|
+
// Triggered by *** signal at end of message
|
|
225
|
+
if (request.mode === 'action_items') {
|
|
226
|
+
const actionItems = getActionItems(allMemories, projectId)
|
|
227
|
+
|
|
228
|
+
// Update injected memories for deduplication
|
|
229
|
+
for (const memory of actionItems) {
|
|
230
|
+
injectedIds.add(memory.id)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
memories: actionItems,
|
|
235
|
+
formatted: this._formatActionItems(actionItems),
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// NORMAL MODE: Filter out already-injected memories (deduplication)
|
|
216
240
|
const candidateMemories = allMemories.filter(m => !injectedIds.has(m.id))
|
|
217
241
|
|
|
218
242
|
if (!candidateMemories.length) {
|
|
@@ -380,6 +404,42 @@ export class MemoryEngine {
|
|
|
380
404
|
return [...projectMemories, ...globalMemories]
|
|
381
405
|
}
|
|
382
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Update a memory's metadata
|
|
409
|
+
* Used for curation actions: promote/demote, bury, archive
|
|
410
|
+
*/
|
|
411
|
+
async updateMemory(
|
|
412
|
+
projectId: string,
|
|
413
|
+
memoryId: string,
|
|
414
|
+
updates: {
|
|
415
|
+
importance_weight?: number
|
|
416
|
+
confidence_score?: number
|
|
417
|
+
exclude_from_retrieval?: boolean
|
|
418
|
+
status?: 'active' | 'pending' | 'superseded' | 'deprecated' | 'archived'
|
|
419
|
+
action_required?: boolean
|
|
420
|
+
awaiting_implementation?: boolean
|
|
421
|
+
awaiting_decision?: boolean
|
|
422
|
+
semantic_tags?: string[]
|
|
423
|
+
trigger_phrases?: string[]
|
|
424
|
+
},
|
|
425
|
+
projectPath?: string
|
|
426
|
+
): Promise<{ success: boolean; updated_fields: string[] }> {
|
|
427
|
+
const store = await this._getStore(projectId, projectPath)
|
|
428
|
+
return store.updateMemory(projectId, memoryId, updates)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Get a single memory by ID
|
|
433
|
+
*/
|
|
434
|
+
async getMemory(
|
|
435
|
+
projectId: string,
|
|
436
|
+
memoryId: string,
|
|
437
|
+
projectPath?: string
|
|
438
|
+
): Promise<StoredMemory | null> {
|
|
439
|
+
const store = await this._getStore(projectId, projectPath)
|
|
440
|
+
return store.getMemory(projectId, memoryId)
|
|
441
|
+
}
|
|
442
|
+
|
|
383
443
|
// ================================================================
|
|
384
444
|
// FORMATTING
|
|
385
445
|
// ================================================================
|
|
@@ -628,6 +688,56 @@ export class MemoryEngine {
|
|
|
628
688
|
return parts.join('\n')
|
|
629
689
|
}
|
|
630
690
|
|
|
691
|
+
/**
|
|
692
|
+
* Format action items for injection
|
|
693
|
+
* Different header to make it clear this is the full action items list
|
|
694
|
+
*/
|
|
695
|
+
private _formatActionItems(memories: RetrievalResult[]): string {
|
|
696
|
+
if (!memories.length) {
|
|
697
|
+
return '# Action Items\n\nNo pending action items found.'
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const parts: string[] = ['# Action Items']
|
|
701
|
+
parts.push(`\n*${memories.length} pending item${memories.length === 1 ? '' : 's'}*\n`)
|
|
702
|
+
|
|
703
|
+
for (const memory of memories) {
|
|
704
|
+
const importance = memory.importance_weight?.toFixed(1) || '0.5'
|
|
705
|
+
const emoji = getMemoryEmoji(memory.context_type || 'general')
|
|
706
|
+
const age = memory.updated_at ? this._formatAge(memory.updated_at) :
|
|
707
|
+
memory.created_at ? this._formatAge(memory.created_at) : ''
|
|
708
|
+
|
|
709
|
+
// Flags
|
|
710
|
+
const flags: string[] = []
|
|
711
|
+
if (memory.action_required) flags.push('⚡ACTION')
|
|
712
|
+
if (memory.awaiting_implementation) flags.push('🔨IMPL')
|
|
713
|
+
if (memory.awaiting_decision) flags.push('❓DECISION')
|
|
714
|
+
if (memory.context_type === 'unresolved') flags.push('❓UNRESOLVED')
|
|
715
|
+
const flagStr = flags.length ? ` [${flags.join(' ')}]` : ''
|
|
716
|
+
|
|
717
|
+
// Short ID for reference
|
|
718
|
+
const shortId = memory.id.slice(-6)
|
|
719
|
+
|
|
720
|
+
// Display: headline if available, otherwise content
|
|
721
|
+
const displayText = memory.headline || memory.content
|
|
722
|
+
|
|
723
|
+
parts.push(`[${emoji} ${importance} • ${age} • #${shortId}]${flagStr}`)
|
|
724
|
+
parts.push(`${displayText}`)
|
|
725
|
+
|
|
726
|
+
// Always show full content for action items (they need context)
|
|
727
|
+
if (memory.headline && memory.content) {
|
|
728
|
+
const contentLines = memory.content.split('\n')
|
|
729
|
+
for (const line of contentLines) {
|
|
730
|
+
if (line.trim()) {
|
|
731
|
+
parts.push(` ${line}`)
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
parts.push('') // Blank line between items
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return parts.join('\n')
|
|
739
|
+
}
|
|
740
|
+
|
|
631
741
|
/**
|
|
632
742
|
* Get resolved storage paths for a project
|
|
633
743
|
* Returns the actual paths based on current engine configuration
|
package/src/core/retrieval.ts
CHANGED
|
@@ -777,3 +777,65 @@ export class SmartVectorRetrieval {
|
|
|
777
777
|
export function createRetrieval(): SmartVectorRetrieval {
|
|
778
778
|
return new SmartVectorRetrieval()
|
|
779
779
|
}
|
|
780
|
+
|
|
781
|
+
// ============================================================================
|
|
782
|
+
// ACTION ITEMS RETRIEVAL
|
|
783
|
+
// Returns all memories marked as requiring action
|
|
784
|
+
// Triggered by *** signal at end of message (detected in hook)
|
|
785
|
+
// ============================================================================
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Get all memories marked as requiring action
|
|
789
|
+
*
|
|
790
|
+
* Filters for memories with:
|
|
791
|
+
* - action_required: true
|
|
792
|
+
* - awaiting_implementation: true
|
|
793
|
+
* - awaiting_decision: true
|
|
794
|
+
* - context_type: 'todo'
|
|
795
|
+
*
|
|
796
|
+
* Returns them sorted by importance, newest first within same importance
|
|
797
|
+
*/
|
|
798
|
+
export function getActionItems(
|
|
799
|
+
allMemories: StoredMemory[],
|
|
800
|
+
currentProjectId: string
|
|
801
|
+
): RetrievalResult[] {
|
|
802
|
+
// Filter to active action items
|
|
803
|
+
const actionItems = allMemories.filter(memory => {
|
|
804
|
+
// Must be active
|
|
805
|
+
if (memory.status && memory.status !== 'active') return false
|
|
806
|
+
if (memory.exclude_from_retrieval === true) return false
|
|
807
|
+
|
|
808
|
+
// Must be for this project or global
|
|
809
|
+
const isGlobal = memory.scope === 'global' || memory.project_id === 'global'
|
|
810
|
+
if (!isGlobal && memory.project_id !== currentProjectId) return false
|
|
811
|
+
|
|
812
|
+
// Must have at least one action flag
|
|
813
|
+
return (
|
|
814
|
+
memory.action_required === true ||
|
|
815
|
+
memory.awaiting_implementation === true ||
|
|
816
|
+
memory.awaiting_decision === true ||
|
|
817
|
+
memory.context_type === 'unresolved' // unresolved = todos, blockers, open questions
|
|
818
|
+
)
|
|
819
|
+
})
|
|
820
|
+
|
|
821
|
+
// Sort by importance (desc), then by created_at (desc = newest first)
|
|
822
|
+
actionItems.sort((a, b) => {
|
|
823
|
+
const aImportance = a.importance_weight ?? 0.5
|
|
824
|
+
const bImportance = b.importance_weight ?? 0.5
|
|
825
|
+
if (bImportance !== aImportance) {
|
|
826
|
+
return bImportance - aImportance
|
|
827
|
+
}
|
|
828
|
+
// Newest first
|
|
829
|
+
const aTime = a.created_at ?? 0
|
|
830
|
+
const bTime = b.created_at ?? 0
|
|
831
|
+
return bTime - aTime
|
|
832
|
+
})
|
|
833
|
+
|
|
834
|
+
// Convert to result format
|
|
835
|
+
return actionItems.map(memory => ({
|
|
836
|
+
...memory,
|
|
837
|
+
score: 1.0, // All action items are relevant by definition
|
|
838
|
+
relevance_score: 1.0,
|
|
839
|
+
value_score: memory.importance_weight ?? 0.5,
|
|
840
|
+
}))
|
|
841
|
+
}
|
package/src/core/store.ts
CHANGED
|
@@ -534,6 +534,113 @@ export class MemoryStore {
|
|
|
534
534
|
return id
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Update a memory's metadata fields
|
|
539
|
+
* Used for curation actions: promote/demote importance, bury, archive
|
|
540
|
+
*/
|
|
541
|
+
async updateMemory(
|
|
542
|
+
projectId: string,
|
|
543
|
+
memoryId: string,
|
|
544
|
+
updates: {
|
|
545
|
+
importance_weight?: number
|
|
546
|
+
confidence_score?: number
|
|
547
|
+
exclude_from_retrieval?: boolean
|
|
548
|
+
status?: 'active' | 'pending' | 'superseded' | 'deprecated' | 'archived'
|
|
549
|
+
action_required?: boolean
|
|
550
|
+
awaiting_implementation?: boolean
|
|
551
|
+
awaiting_decision?: boolean
|
|
552
|
+
semantic_tags?: string[]
|
|
553
|
+
trigger_phrases?: string[]
|
|
554
|
+
}
|
|
555
|
+
): Promise<{ success: boolean; updated_fields: string[] }> {
|
|
556
|
+
const { memories } = await this.getProject(projectId)
|
|
557
|
+
|
|
558
|
+
// Check memory exists
|
|
559
|
+
const existing = memories.get(memoryId)
|
|
560
|
+
if (!existing) {
|
|
561
|
+
return { success: false, updated_fields: [] }
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Build update object with only provided fields
|
|
565
|
+
const updateData: Record<string, any> = {}
|
|
566
|
+
const updatedFields: string[] = []
|
|
567
|
+
|
|
568
|
+
if (updates.importance_weight !== undefined) {
|
|
569
|
+
updateData.importance_weight = Math.max(0, Math.min(1, updates.importance_weight))
|
|
570
|
+
updatedFields.push('importance_weight')
|
|
571
|
+
}
|
|
572
|
+
if (updates.confidence_score !== undefined) {
|
|
573
|
+
updateData.confidence_score = Math.max(0, Math.min(1, updates.confidence_score))
|
|
574
|
+
updatedFields.push('confidence_score')
|
|
575
|
+
}
|
|
576
|
+
if (updates.exclude_from_retrieval !== undefined) {
|
|
577
|
+
updateData.exclude_from_retrieval = updates.exclude_from_retrieval
|
|
578
|
+
updatedFields.push('exclude_from_retrieval')
|
|
579
|
+
}
|
|
580
|
+
if (updates.status !== undefined) {
|
|
581
|
+
updateData.status = updates.status
|
|
582
|
+
updatedFields.push('status')
|
|
583
|
+
}
|
|
584
|
+
if (updates.action_required !== undefined) {
|
|
585
|
+
updateData.action_required = updates.action_required
|
|
586
|
+
updatedFields.push('action_required')
|
|
587
|
+
}
|
|
588
|
+
if (updates.awaiting_implementation !== undefined) {
|
|
589
|
+
updateData.awaiting_implementation = updates.awaiting_implementation
|
|
590
|
+
updatedFields.push('awaiting_implementation')
|
|
591
|
+
}
|
|
592
|
+
if (updates.awaiting_decision !== undefined) {
|
|
593
|
+
updateData.awaiting_decision = updates.awaiting_decision
|
|
594
|
+
updatedFields.push('awaiting_decision')
|
|
595
|
+
}
|
|
596
|
+
if (updates.semantic_tags !== undefined) {
|
|
597
|
+
updateData.semantic_tags = updates.semantic_tags
|
|
598
|
+
updatedFields.push('semantic_tags')
|
|
599
|
+
}
|
|
600
|
+
if (updates.trigger_phrases !== undefined) {
|
|
601
|
+
updateData.trigger_phrases = updates.trigger_phrases
|
|
602
|
+
updatedFields.push('trigger_phrases')
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (updatedFields.length === 0) {
|
|
606
|
+
return { success: true, updated_fields: [] }
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Perform update
|
|
610
|
+
memories.update(memoryId, updateData)
|
|
611
|
+
|
|
612
|
+
return { success: true, updated_fields: updatedFields }
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Get a single memory by ID
|
|
617
|
+
*/
|
|
618
|
+
async getMemory(projectId: string, memoryId: string): Promise<StoredMemory | null> {
|
|
619
|
+
const { memories } = await this.getProject(projectId)
|
|
620
|
+
const record = memories.get(memoryId)
|
|
621
|
+
if (!record) return null
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
id: record.id,
|
|
625
|
+
headline: record.headline ?? '',
|
|
626
|
+
content: record.content,
|
|
627
|
+
reasoning: record.reasoning,
|
|
628
|
+
importance_weight: record.importance_weight,
|
|
629
|
+
confidence_score: record.confidence_score,
|
|
630
|
+
context_type: record.context_type as StoredMemory['context_type'],
|
|
631
|
+
status: record.status as StoredMemory['status'],
|
|
632
|
+
exclude_from_retrieval: record.exclude_from_retrieval,
|
|
633
|
+
action_required: record.action_required,
|
|
634
|
+
awaiting_implementation: record.awaiting_implementation,
|
|
635
|
+
awaiting_decision: record.awaiting_decision,
|
|
636
|
+
semantic_tags: record.semantic_tags,
|
|
637
|
+
trigger_phrases: record.trigger_phrases,
|
|
638
|
+
project_id: record.project_id,
|
|
639
|
+
created_at: record.created_at,
|
|
640
|
+
updated_at: record.updated_at,
|
|
641
|
+
} as StoredMemory
|
|
642
|
+
}
|
|
643
|
+
|
|
537
644
|
/**
|
|
538
645
|
* Get all memories for a project
|
|
539
646
|
*/
|
package/src/server/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ interface ContextRequest {
|
|
|
43
43
|
current_message?: string
|
|
44
44
|
max_memories?: number
|
|
45
45
|
project_path?: string
|
|
46
|
+
mode?: 'normal' | 'action_items' // Can be set explicitly or auto-detected via ***
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
interface ProcessRequest {
|
|
@@ -136,26 +137,42 @@ export async function createServer(config: ServerConfig = {}) {
|
|
|
136
137
|
|
|
137
138
|
logger.request('POST', '/memory/context', body.project_id)
|
|
138
139
|
|
|
140
|
+
// Detect *** signal at end of message for action items mode
|
|
141
|
+
let message = body.current_message ?? ''
|
|
142
|
+
let mode = body.mode ?? 'normal'
|
|
143
|
+
|
|
144
|
+
if (message.trimEnd().endsWith('***')) {
|
|
145
|
+
mode = 'action_items'
|
|
146
|
+
// Strip the *** signal from the message
|
|
147
|
+
message = message.trimEnd().slice(0, -3).trimEnd()
|
|
148
|
+
logger.debug('Action items mode detected (*** signal)', 'server')
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
const result = await engine.getContext({
|
|
140
152
|
sessionId: body.session_id,
|
|
141
153
|
projectId: body.project_id,
|
|
142
|
-
currentMessage:
|
|
154
|
+
currentMessage: message,
|
|
143
155
|
maxMemories: body.max_memories,
|
|
144
156
|
projectPath: body.project_path,
|
|
157
|
+
mode,
|
|
145
158
|
})
|
|
146
159
|
|
|
147
160
|
// Log what happened
|
|
148
161
|
if (result.primer) {
|
|
149
162
|
logger.primer(`Session primer for ${body.project_id}`)
|
|
150
163
|
} else if (result.memories.length > 0) {
|
|
151
|
-
|
|
152
|
-
result.memories.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
if (mode === 'action_items') {
|
|
165
|
+
logger.info(`Returning ${result.memories.length} action item${result.memories.length === 1 ? '' : 's'}`)
|
|
166
|
+
} else {
|
|
167
|
+
logger.logRetrievedMemories(
|
|
168
|
+
result.memories.map(m => ({
|
|
169
|
+
content: m.content,
|
|
170
|
+
score: m.score,
|
|
171
|
+
context_type: m.context_type,
|
|
172
|
+
})),
|
|
173
|
+
message
|
|
174
|
+
)
|
|
175
|
+
}
|
|
159
176
|
}
|
|
160
177
|
|
|
161
178
|
return Response.json({
|
|
@@ -167,6 +184,7 @@ export async function createServer(config: ServerConfig = {}) {
|
|
|
167
184
|
curator_enabled: true,
|
|
168
185
|
memories_count: result.memories.length,
|
|
169
186
|
has_primer: !!result.primer,
|
|
187
|
+
mode, // Include the mode that was used
|
|
170
188
|
}, { headers: corsHeaders })
|
|
171
189
|
}
|
|
172
190
|
|
|
@@ -369,6 +387,52 @@ export async function createServer(config: ServerConfig = {}) {
|
|
|
369
387
|
})
|
|
370
388
|
}
|
|
371
389
|
|
|
390
|
+
// PATCH memory - update metadata for curation (promote/demote/bury)
|
|
391
|
+
const patchMatch = path.match(/^\/memory\/([a-zA-Z0-9_-]+)$/)
|
|
392
|
+
if (patchMatch && req.method === 'PATCH') {
|
|
393
|
+
const memoryId = patchMatch[1]
|
|
394
|
+
const body = await req.json() as {
|
|
395
|
+
project_id: string
|
|
396
|
+
importance_weight?: number
|
|
397
|
+
confidence_score?: number
|
|
398
|
+
exclude_from_retrieval?: boolean
|
|
399
|
+
status?: 'active' | 'pending' | 'superseded' | 'deprecated' | 'archived'
|
|
400
|
+
action_required?: boolean
|
|
401
|
+
awaiting_implementation?: boolean
|
|
402
|
+
awaiting_decision?: boolean
|
|
403
|
+
semantic_tags?: string[]
|
|
404
|
+
trigger_phrases?: string[]
|
|
405
|
+
project_path?: string
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!body.project_id) {
|
|
409
|
+
return Response.json(
|
|
410
|
+
{ success: false, error: 'project_id is required' },
|
|
411
|
+
{ status: 400, headers: corsHeaders }
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
logger.request('PATCH', `/memory/${memoryId}`, body.project_id)
|
|
416
|
+
|
|
417
|
+
const { project_id, project_path, ...updates } = body
|
|
418
|
+
const result = await engine.updateMemory(project_id, memoryId, updates, project_path)
|
|
419
|
+
|
|
420
|
+
if (!result.success) {
|
|
421
|
+
return Response.json(
|
|
422
|
+
{ success: false, error: 'Memory not found', memory_id: memoryId },
|
|
423
|
+
{ status: 404, headers: corsHeaders }
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
logger.info(`Updated memory ${memoryId}: ${result.updated_fields.join(', ')}`)
|
|
428
|
+
|
|
429
|
+
return Response.json({
|
|
430
|
+
success: true,
|
|
431
|
+
memory_id: memoryId,
|
|
432
|
+
updated_fields: result.updated_fields,
|
|
433
|
+
}, { headers: corsHeaders })
|
|
434
|
+
}
|
|
435
|
+
|
|
372
436
|
// 404
|
|
373
437
|
return Response.json(
|
|
374
438
|
{ error: 'Not found', path },
|