@rlabs-inc/memory 0.5.1 → 0.5.3
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/package.json +1 -1
- package/src/core/manager.ts +54 -21
- package/src/server/index.ts +23 -0
package/package.json
CHANGED
package/src/core/manager.ts
CHANGED
|
@@ -735,8 +735,18 @@ Please process these memories according to your management procedure. Use the ex
|
|
|
735
735
|
|
|
736
736
|
const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths)
|
|
737
737
|
|
|
738
|
-
//
|
|
739
|
-
|
|
738
|
+
// Resolve storage paths from config
|
|
739
|
+
const centralPath = join(homedir(), '.local', 'share', 'memory')
|
|
740
|
+
const projectPath = storagePaths?.projectPath ?? join(centralPath, projectId)
|
|
741
|
+
const globalPath = storagePaths?.globalPath ?? join(centralPath, 'global')
|
|
742
|
+
const storageMode = storagePaths?.storageMode ?? 'central'
|
|
743
|
+
|
|
744
|
+
// Determine cwd based on storage mode:
|
|
745
|
+
// - Central: cwd = centralPath (parent of both project and global, write access to both)
|
|
746
|
+
// - Local: cwd = projectPath (write access to project only, global is read-only)
|
|
747
|
+
const managerCwd = storageMode === 'central' ? centralPath : projectPath
|
|
748
|
+
|
|
749
|
+
// Write system prompt to temp file (in cwd so it's accessible)
|
|
740
750
|
const geminiSystemPrompt = `${systemPrompt}
|
|
741
751
|
|
|
742
752
|
## Available Tools
|
|
@@ -747,40 +757,63 @@ You have access to the following tools to manage memory files:
|
|
|
747
757
|
|
|
748
758
|
Use these tools to read existing memories, write updates, and manage the memory filesystem.
|
|
749
759
|
`
|
|
750
|
-
const tempPromptPath = join(
|
|
760
|
+
const tempPromptPath = join(managerCwd, '.gemini-manager-prompt.md')
|
|
761
|
+
await Bun.write(tempPromptPath, geminiSystemPrompt)
|
|
751
762
|
|
|
752
|
-
//
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
763
|
+
// Copy user's Gemini settings to managerCwd with hooks disabled
|
|
764
|
+
// This prevents the manager's session from triggering hooks recursively
|
|
765
|
+
const userSettingsPath = join(homedir(), '.gemini', 'settings.json')
|
|
766
|
+
const managerSettingsDir = join(managerCwd, '.gemini')
|
|
767
|
+
const managerSettingsPath = join(managerSettingsDir, 'settings.json')
|
|
758
768
|
|
|
759
|
-
|
|
769
|
+
try {
|
|
770
|
+
let settings: any = {}
|
|
771
|
+
|
|
772
|
+
// Load user's settings if they exist
|
|
773
|
+
if (existsSync(userSettingsPath)) {
|
|
774
|
+
const userSettings = await Bun.file(userSettingsPath).text()
|
|
775
|
+
settings = JSON.parse(userSettings)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Disable hooks (hooks.enabled inside hooks object)
|
|
779
|
+
if (!settings.hooks) {
|
|
780
|
+
settings.hooks = {}
|
|
781
|
+
}
|
|
782
|
+
settings.hooks.enabled = false
|
|
783
|
+
|
|
784
|
+
// Ensure .gemini directory exists in managerCwd
|
|
785
|
+
if (!existsSync(managerSettingsDir)) {
|
|
786
|
+
const { mkdirSync } = await import('fs')
|
|
787
|
+
mkdirSync(managerSettingsDir, { recursive: true })
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
await Bun.write(managerSettingsPath, JSON.stringify(settings, null, 2))
|
|
791
|
+
logger.debug(`Manager Gemini: Created settings with hooks disabled at ${managerSettingsPath}`, 'manager')
|
|
792
|
+
} catch (err: any) {
|
|
793
|
+
logger.debug(`Manager Gemini: Could not create settings file: ${err.message}`, 'manager')
|
|
794
|
+
}
|
|
760
795
|
|
|
761
796
|
logger.debug(`Manager Gemini: Starting management for project ${projectId}`, 'manager')
|
|
762
797
|
logger.debug(`Manager Gemini: Processing ${result.memories.length} memories`, 'manager')
|
|
763
|
-
logger.debug(`Manager Gemini:
|
|
798
|
+
logger.debug(`Manager Gemini: Storage mode: ${storageMode}, cwd: ${managerCwd}`, 'manager')
|
|
764
799
|
|
|
765
|
-
// Build CLI command
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
const memoryStoragePath = join(homedir(), '.local', 'share', 'memory')
|
|
800
|
+
// Build CLI command
|
|
801
|
+
// - cwd gives write access to that tree
|
|
802
|
+
// - --include-directories adds read access to other paths
|
|
769
803
|
const args = [
|
|
770
804
|
'-p', userMessage,
|
|
771
805
|
'--output-format', 'json',
|
|
772
806
|
'--yolo', // Auto-approve file operations
|
|
773
|
-
'--include-directories',
|
|
807
|
+
'--include-directories', projectPath,
|
|
808
|
+
'--include-directories', globalPath,
|
|
774
809
|
]
|
|
775
810
|
|
|
776
|
-
logger.debug(`Manager Gemini: Spawning gemini CLI`, 'manager')
|
|
777
|
-
logger.debug(`Manager Gemini: Running from directory: ${memoryStoragePath}`, 'manager')
|
|
811
|
+
logger.debug(`Manager Gemini: Spawning gemini CLI from ${managerCwd}`, 'manager')
|
|
778
812
|
|
|
779
813
|
// Execute CLI with system prompt via environment variable
|
|
780
|
-
//
|
|
781
|
-
// This allows both READ and WRITE operations (--include-directories only allows reads)
|
|
814
|
+
// cwd determines write access, --include-directories adds read access
|
|
782
815
|
const proc = Bun.spawn(['gemini', ...args], {
|
|
783
|
-
cwd:
|
|
816
|
+
cwd: managerCwd,
|
|
784
817
|
env: {
|
|
785
818
|
...process.env,
|
|
786
819
|
MEMORY_CURATOR_ACTIVE: '1', // Prevent recursive hook triggering
|
package/src/server/index.ts
CHANGED
|
@@ -64,6 +64,13 @@ interface CheckpointRequest {
|
|
|
64
64
|
project_path?: string
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Track sessions currently being curated to prevent recursive calls.
|
|
69
|
+
* When Gemini CLI spawns hooks, env vars may not propagate, so we
|
|
70
|
+
* need server-side deduplication.
|
|
71
|
+
*/
|
|
72
|
+
const sessionsBeingCurated = new Set<string>()
|
|
73
|
+
|
|
67
74
|
/**
|
|
68
75
|
* Create and start the memory server
|
|
69
76
|
*/
|
|
@@ -210,6 +217,19 @@ export async function createServer(config: ServerConfig = {}) {
|
|
|
210
217
|
if (path === '/memory/checkpoint' && req.method === 'POST') {
|
|
211
218
|
const body = await req.json() as CheckpointRequest
|
|
212
219
|
|
|
220
|
+
// Prevent recursive curation - Gemini CLI doesn't propagate env vars to hooks
|
|
221
|
+
// so MEMORY_CURATOR_ACTIVE check in hooks doesn't work. Dedupe at server level.
|
|
222
|
+
if (sessionsBeingCurated.has(body.claude_session_id)) {
|
|
223
|
+
logger.debug(`Skipping duplicate curation request for session ${body.claude_session_id}`, 'server')
|
|
224
|
+
return Response.json({
|
|
225
|
+
success: true,
|
|
226
|
+
message: 'Curation already in progress for this session',
|
|
227
|
+
}, { headers: corsHeaders })
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Mark session as being curated BEFORE async work starts
|
|
231
|
+
sessionsBeingCurated.add(body.claude_session_id)
|
|
232
|
+
|
|
213
233
|
logger.logCurationStart(body.claude_session_id, body.trigger)
|
|
214
234
|
|
|
215
235
|
// Fire and forget - don't block the response
|
|
@@ -339,6 +359,9 @@ export async function createServer(config: ServerConfig = {}) {
|
|
|
339
359
|
}
|
|
340
360
|
} catch (error) {
|
|
341
361
|
logger.error(`Curation failed: ${error}`)
|
|
362
|
+
} finally {
|
|
363
|
+
// Release the session lock - allows future curation requests for this session
|
|
364
|
+
sessionsBeingCurated.delete(body.claude_session_id)
|
|
342
365
|
}
|
|
343
366
|
})
|
|
344
367
|
|