MCPcopy
hub / github.com/coder/mux / MemoryService

Class MemoryService

src/node/services/memoryService.ts:458–1099  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

456// ---------------------------------------------------------------------------
457
458export class MemoryService extends EventEmitter {
459 /** Serializes mutating commands per physical root (agent tool + UI writes). */
460 private readonly locks = new MutexMap<string>();
461 constructor(
462 private readonly config: Config,
463 /** Host-local sidecar for pins + usage stats, recorded at this chokepoint. */
464 private readonly metaService: MemoryMetaService
465 ) {
466 super();
467 }
468
469 // -------------------------------------------------------------------------
470 // Usage stats (sidecar): recorded here — the single chokepoint every agent
471 // command and UI write funnels through. UI reads (readFileWithSha) are
472 // intentionally not counted: stats track agent usage, not human browsing.
473 // Best-effort: stats failures must never break a memory command.
474 // -------------------------------------------------------------------------
475
476 /** Logical sidecar key, or null when the scope has no stable identity. */
477 private logicalKeyFor(ctx: MemoryScopeContext, scope: MemoryScope, relPath: string) {
478 if (scope === "project" && ctx.projectPath === "") return null;
479 return memoryLogicalKey(scope, relPath, {
480 projectPath: ctx.projectPath,
481 workspaceId: ctx.workspaceId,
482 });
483 }
484
485 private async recordUsage(
486 ctx: MemoryScopeContext,
487 scope: MemoryScope,
488 relPath: string,
489 options: { write: boolean }
490 ): Promise<void> {
491 try {
492 const key = this.logicalKeyFor(ctx, scope, relPath);
493 if (key === null) return;
494 await this.metaService.recordAccess(key, options);
495 } catch (error) {
496 log.debug("[MemoryService] failed to record memory usage", { scope, relPath, error });
497 }
498 }
499
500 private async recordRename(
501 ctx: MemoryScopeContext,
502 scope: MemoryScope,
503 oldRelPath: string,
504 newRelPath: string
505 ): Promise<void> {
506 try {
507 const oldKey = this.logicalKeyFor(ctx, scope, oldRelPath);
508 const newKey = this.logicalKeyFor(ctx, scope, newRelPath);
509 if (oldKey === null || newKey === null) return;
510 // Pins and stats follow the file; the rename itself counts as a use.
511 await this.metaService.renameKeys(oldKey, newKey);
512 await this.metaService.recordAccess(newKey, { write: true });
513 } catch (error) {
514 log.debug("[MemoryService] failed to move memory usage stats on rename", {
515 scope,

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected