CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
dream.ts166 linesDownload Raw Back to memory
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import * as fs from 'node:fs/promises';8import type { Config } from '../config/config.js';9import { atomicWriteFile } from '../utils/atomicFileWrite.js';10import { getAutoMemoryMetadataPath } from './paths.js';11import { planManagedAutoMemoryDreamByAgent } from './dreamAgentPlanner.js';12import { rebuildManagedAutoMemoryIndex } from './indexer.js';13import { ensureAutoMemoryScaffold } from './store.js';14import {15  AUTO_MEMORY_TYPES,16  type AutoMemoryMetadata,17  type AutoMemoryType,18} from './types.js';19import { logMemoryDream, MemoryDreamEvent } from '../telemetry/index.js';20 21export interface AutoMemoryDreamResult {22  touchedTopics: AutoMemoryType[];23  dedupedEntries: number;24  systemMessage?: string;25}26 27async function runDreamByAgent(28  projectRoot: string,29  config: Config,30  abortSignal?: AbortSignal,31  options: { suppressChatRecording?: boolean } = {},32): Promise<AutoMemoryDreamResult> {33  const result = await planManagedAutoMemoryDreamByAgent(34    config,35    projectRoot,36    abortSignal,37    { suppressChatRecording: options.suppressChatRecording },38  );39 40  // Infer which topics were touched from the file paths41  const touchedTopics = new Set<AutoMemoryType>();42  for (const filePath of result.filesTouched) {43    const normalized = filePath.replace(/\\/g, '/');44    for (const type of AUTO_MEMORY_TYPES) {45      if (normalized.includes(`/${type}/`)) {46        touchedTopics.add(type);47      }48    }49  }50 51  const summary = result.finalText52    ? result.finalText.trim().slice(0, 300)53    : `updated ${result.filesTouched.length} file(s)`;54 55  return {56    touchedTopics: [...touchedTopics],57    dedupedEntries: 0,58    systemMessage: `Managed auto-memory dream (agent): ${summary}`,59  };60}61 62export async function runManagedAutoMemoryDream(63  projectRoot: string,64  now = new Date(),65  config?: Config,66  abortSignal?: AbortSignal,67  options: {68    trigger?: 'auto' | 'manual';69    recordMetadata?: boolean;70    suppressChatRecording?: boolean;71  } = {},72): Promise<AutoMemoryDreamResult> {73  await ensureAutoMemoryScaffold(projectRoot, now);74  const t0 = Date.now();75 76  if (!config) {77    throw new Error(78      'Managed auto-memory dream requires config for forked-agent execution.',79    );80  }81 82  const agentResult = await runDreamByAgent(projectRoot, config, abortSignal, {83    suppressChatRecording: options.suppressChatRecording,84  });85  // Cancel-aware ordering:86  //   1. If aborted before this point, return the agent's partial result87  //      WITHOUT rebuilding the index — index rebuild can be expensive88  //      and re-running a cancelled dream cycle next time will rebuild89  //      against the latest topic files anyway.90  //   2. If still alive, rebuild the index (informational, powers91  //      recall) — but only when topics actually changed.92  // Scheduler-gating metadata (`lastDreamAt`, `lastDreamSessionId`,93  // `lastDreamTouchedTopics`, `lastDreamStatus`) is intentionally NOT94  // written here — `MemoryManager.runDream` owns the atomic95  // status-flip + metadata-write sequence to close the cancel race96  // window where a writeFile finishing concurrently with a cancel97  // could persist gating metadata for a record the manager is about98  // to mark `'cancelled'`.99  if (abortSignal?.aborted) return agentResult;100  if (agentResult.touchedTopics.length > 0) {101    await rebuildManagedAutoMemoryIndex(projectRoot);102  }103  if (options.recordMetadata) {104    await updateDreamMetadataResult(105      projectRoot,106      now,107      agentResult.touchedTopics,108    );109  }110 111  logMemoryDream(112    config,113    new MemoryDreamEvent({114      trigger: options.trigger ?? 'auto',115      status: agentResult.touchedTopics.length > 0 ? 'updated' : 'noop',116      deduped_entries: agentResult.dedupedEntries,117      touched_topics: agentResult.touchedTopics,118      duration_ms: Date.now() - t0,119    }),120  );121  return agentResult;122}123 124async function updateDreamMetadataResult(125  projectRoot: string,126  now: Date,127  touchedTopics: AutoMemoryType[],128  sessionId?: string,129): Promise<void> {130  const metadataPath = getAutoMemoryMetadataPath(projectRoot);131  try {132    const content = await fs.readFile(metadataPath, 'utf-8');133    const metadata = JSON.parse(content) as AutoMemoryMetadata;134    metadata.updatedAt = now.toISOString();135    metadata.lastDreamAt = now.toISOString();136    metadata.lastDreamTouchedTopics = touchedTopics;137    metadata.lastDreamStatus = touchedTopics.length > 0 ? 'updated' : 'noop';138    if (sessionId !== undefined) {139      metadata.lastDreamSessionId = sessionId;140      metadata.recentSessionIdsSinceDream = [];141    }142    await atomicWriteFile(143      metadataPath,144      `${JSON.stringify(metadata, null, 2)}\n`,145      { encoding: 'utf-8' },146    );147  } catch {148    // Best-effort metadata bump.149  }150}151 152/**153 * Record that the user manually ran /dream. Called from the CLI command's154 * onComplete callback after the main agent turn finishes writing memory files.155 * Writes lastDreamAt, lastDreamSessionId, and resets recentSessionIdsSinceDream156 * so that the scheduler's same-session dedupe check prevents a redundant157 * auto-dream from firing in the same session.158 */159export async function writeDreamManualRunToMetadata(160  projectRoot: string,161  sessionId: string,162  now = new Date(),163): Promise<void> {164  return updateDreamMetadataResult(projectRoot, now, [], sessionId);165}166 
basant307/AI_Governance_Project · CoolFace