@rlabs-inc/memory 0.4.14 → 0.5.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.
@@ -7,7 +7,7 @@ import { MemoryEngine, createEngine, type EngineConfig } from '../core/engine.ts
7
7
  import { Curator, createCurator, type CuratorConfig } from '../core/curator.ts'
8
8
  import { EmbeddingGenerator, createEmbeddings } from '../core/embeddings.ts'
9
9
  import { Manager, createManager, type ManagerConfig } from '../core/manager.ts'
10
- import type { CurationTrigger } from '../types/memory.ts'
10
+ import type { CurationTrigger, CurationResult } from '../types/memory.ts'
11
11
  import { logger } from '../utils/logger.ts'
12
12
 
13
13
  /**
@@ -215,29 +215,42 @@ export async function createServer(config: ServerConfig = {}) {
215
215
  // Fire and forget - don't block the response
216
216
  setImmediate(async () => {
217
217
  try {
218
- // Try session resume first (v2) - gets full context including tool uses
219
- // Falls back to segmented transcript parsing if resume fails
220
- let result = await curator.curateWithSessionResume(
221
- body.claude_session_id,
222
- body.trigger
223
- )
218
+ let result: CurationResult
224
219
 
225
- // Fallback to transcript-based curation WITH SEGMENTATION if resume returned nothing
226
- // This matches the ingest command behavior - breaks large sessions into segments
227
- if (result.memories.length === 0) {
228
- logger.debug('Session resume returned no memories, falling back to segmented transcript parsing', 'server')
229
- result = await curator.curateFromSessionFileWithSegments(
220
+ // Branch on CLI type - Gemini CLI vs Claude Code
221
+ if (body.cli_type === 'gemini-cli') {
222
+ // Use Gemini CLI for curation (no Claude dependency)
223
+ logger.debug('Using Gemini CLI for curation', 'server')
224
+ result = await curator.curateWithGeminiCLI(
230
225
  body.claude_session_id,
231
- body.trigger,
232
- body.cwd,
233
- 150000, // 150k tokens per segment
234
- (progress) => {
235
- logger.debug(
236
- `Curation segment ${progress.segmentIndex + 1}/${progress.totalSegments}: ${progress.memoriesExtracted} memories (~${Math.round(progress.tokensInSegment / 1000)}k tokens)`,
237
- 'server'
238
- )
239
- }
226
+ body.trigger
227
+ )
228
+ } else {
229
+ // Default: Use Claude Code (session resume or transcript parsing)
230
+ // Try session resume first (v2) - gets full context including tool uses
231
+ // Falls back to segmented transcript parsing if resume fails
232
+ result = await curator.curateWithSessionResume(
233
+ body.claude_session_id,
234
+ body.trigger
240
235
  )
236
+
237
+ // Fallback to transcript-based curation WITH SEGMENTATION if resume returned nothing
238
+ // This matches the ingest command behavior - breaks large sessions into segments
239
+ if (result.memories.length === 0) {
240
+ logger.debug('Session resume returned no memories, falling back to segmented transcript parsing', 'server')
241
+ result = await curator.curateFromSessionFileWithSegments(
242
+ body.claude_session_id,
243
+ body.trigger,
244
+ body.cwd,
245
+ 150000, // 150k tokens per segment
246
+ (progress) => {
247
+ logger.debug(
248
+ `Curation segment ${progress.segmentIndex + 1}/${progress.totalSegments}: ${progress.memoriesExtracted} memories (~${Math.round(progress.tokensInSegment / 1000)}k tokens)`,
249
+ 'server'
250
+ )
251
+ }
252
+ )
253
+ }
241
254
  }
242
255
 
243
256
  if (result.memories.length > 0) {
@@ -255,19 +268,34 @@ export async function createServer(config: ServerConfig = {}) {
255
268
  const sessionNumber = await engine.getSessionNumber(body.project_id, body.project_path)
256
269
  // Get resolved storage paths from engine config (runtime values, not hardcoded)
257
270
  const storagePaths = engine.getStoragePaths(body.project_id, body.project_path)
271
+ // Remember cli_type for manager
272
+ const cliType = body.cli_type
258
273
 
259
274
  setImmediate(async () => {
260
275
  try {
261
276
  logger.logManagementStart(result.memories.length)
262
277
  const startTime = Date.now()
263
278
 
264
- // Use SDK mode - more reliable than CLI which can go off-rails
265
- const managementResult = await manager.manageWithSDK(
266
- body.project_id,
267
- sessionNumber,
268
- result,
269
- storagePaths
270
- )
279
+ // Use appropriate mode based on CLI type
280
+ let managementResult
281
+ if (cliType === 'gemini-cli') {
282
+ // Use Gemini CLI for management (no Claude dependency)
283
+ logger.debug('Using Gemini CLI for management', 'server')
284
+ managementResult = await manager.manageWithGeminiCLI(
285
+ body.project_id,
286
+ sessionNumber,
287
+ result,
288
+ storagePaths
289
+ )
290
+ } else {
291
+ // Use Claude Agent SDK mode - more reliable than CLI
292
+ managementResult = await manager.manageWithSDK(
293
+ body.project_id,
294
+ sessionNumber,
295
+ result,
296
+ storagePaths
297
+ )
298
+ }
271
299
 
272
300
  logger.logManagementComplete({
273
301
  success: managementResult.success,
@@ -387,6 +415,52 @@ export async function createServer(config: ServerConfig = {}) {
387
415
  })
388
416
  }
389
417
 
418
+ // PATCH memory - update metadata for curation (promote/demote/bury)
419
+ const patchMatch = path.match(/^\/memory\/([a-zA-Z0-9_-]+)$/)
420
+ if (patchMatch && req.method === 'PATCH') {
421
+ const memoryId = patchMatch[1]
422
+ const body = await req.json() as {
423
+ project_id: string
424
+ importance_weight?: number
425
+ confidence_score?: number
426
+ exclude_from_retrieval?: boolean
427
+ status?: 'active' | 'pending' | 'superseded' | 'deprecated' | 'archived'
428
+ action_required?: boolean
429
+ awaiting_implementation?: boolean
430
+ awaiting_decision?: boolean
431
+ semantic_tags?: string[]
432
+ trigger_phrases?: string[]
433
+ project_path?: string
434
+ }
435
+
436
+ if (!body.project_id) {
437
+ return Response.json(
438
+ { success: false, error: 'project_id is required' },
439
+ { status: 400, headers: corsHeaders }
440
+ )
441
+ }
442
+
443
+ logger.request('PATCH', `/memory/${memoryId}`, body.project_id)
444
+
445
+ const { project_id, project_path, ...updates } = body
446
+ const result = await engine.updateMemory(project_id, memoryId, updates, project_path)
447
+
448
+ if (!result.success) {
449
+ return Response.json(
450
+ { success: false, error: 'Memory not found', memory_id: memoryId },
451
+ { status: 404, headers: corsHeaders }
452
+ )
453
+ }
454
+
455
+ logger.info(`Updated memory ${memoryId}: ${result.updated_fields.join(', ')}`)
456
+
457
+ return Response.json({
458
+ success: true,
459
+ memory_id: memoryId,
460
+ updated_fields: result.updated_fields,
461
+ }, { headers: corsHeaders })
462
+ }
463
+
390
464
  // 404
391
465
  return Response.json(
392
466
  { error: 'Not found', path },