Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"prepack": "npm run build",
"typecheck": "tsc --noEmit",
"test": "jest",
"test:core": "jest --runInBand --forceExit src/agent/communication/__tests__/agentMessageBus.test.ts src/agent/core/executors/__tests__/strategyExecutor.test.ts src/agent/core/executors/__tests__/hypothesisExecutor.test.ts src/agent/context/__tests__/enhancedSessionContext.test.ts src/tests/adbTools.test.ts src/services/__tests__/sessionLogger.test.ts src/services/__tests__/traceAnalysisSkillConfig.test.ts src/agent/agents/domain/__tests__/registry.test.ts src/agentv3/__tests__/sqlIncludeInjector.test.ts src/agentv3/__tests__/analysisPatternMemory.test.ts src/agentv3/__tests__/claudeRuntimeRuntimeSnapshots.test.ts src/middleware/__tests__/auth.test.ts src/services/__tests__/rbac.test.ts src/routes/__tests__/agentRoutesRbac.test.ts src/routes/__tests__/ownerGuardRoutes.test.ts src/routes/__tests__/requestContextRouteCoverage.test.ts src/middleware/__tests__/legacyApiCompatibility.test.ts src/services/__tests__/enterpriseDb.test.ts src/services/__tests__/enterpriseSchema.test.ts src/services/__tests__/enterpriseRepository.test.ts src/services/__tests__/enterpriseKnowledgeScope.test.ts src/services/__tests__/runtimeSnapshotStore.test.ts src/services/providerManager/__tests__/enterpriseProviderStore.test.ts src/routes/__tests__/enterpriseTraceMetadataRoutes.test.ts src/routes/__tests__/enterpriseReportRoutes.test.ts",
"test:core": "jest --runInBand --forceExit src/agent/communication/__tests__/agentMessageBus.test.ts src/agent/core/executors/__tests__/strategyExecutor.test.ts src/agent/core/executors/__tests__/hypothesisExecutor.test.ts src/agent/context/__tests__/enhancedSessionContext.test.ts src/tests/adbTools.test.ts src/services/__tests__/sessionLogger.test.ts src/services/__tests__/traceAnalysisSkillConfig.test.ts src/agent/agents/domain/__tests__/registry.test.ts src/agentv3/__tests__/sqlIncludeInjector.test.ts src/agentv3/__tests__/analysisPatternMemory.test.ts src/agentv3/__tests__/claudeRuntimeRuntimeSnapshots.test.ts src/middleware/__tests__/auth.test.ts src/services/__tests__/rbac.test.ts src/routes/__tests__/agentRoutesRbac.test.ts src/routes/__tests__/ownerGuardRoutes.test.ts src/routes/__tests__/requestContextRouteCoverage.test.ts src/middleware/__tests__/legacyApiCompatibility.test.ts src/services/__tests__/enterpriseDb.test.ts src/services/__tests__/enterpriseSchema.test.ts src/services/__tests__/enterpriseRepository.test.ts src/services/__tests__/enterpriseKnowledgeScope.test.ts src/services/__tests__/enterpriseMigration.test.ts src/services/__tests__/runtimeSnapshotStore.test.ts src/services/providerManager/__tests__/enterpriseProviderStore.test.ts src/routes/__tests__/enterpriseTraceMetadataRoutes.test.ts src/routes/__tests__/enterpriseReportRoutes.test.ts",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:unit": "jest --testPathPatterns=src/tests",
Expand All @@ -56,6 +56,7 @@
"test:all": "npm run test:gate",
"trace-processor:ensure": "node scripts/ensure-trace-processor.cjs",
"verify:pr": "npm run validate:skills && npm run validate:strategies && npm run typecheck && npm run build && node scripts/check-cli-pack.cjs && npm run test:core && npm run trace-processor:ensure && npm run test:scene-trace-regression",
"enterprise:migration": "tsx src/scripts/enterpriseMigrationSnapshot.ts",
"skill:validate": "tsx src/cli/index.ts validate",
"validate:skills": "tsx src/cli/index.ts validate --contracts --all",
"validate:strategies": "tsx src/cli/index.ts validate --strategies",
Expand Down
2 changes: 1 addition & 1 deletion backend/src/agentv3/analysisPatternMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ function patternMatchesKnowledgeScope(
pattern: {provenance?: PatternProvenance},
scope: KnowledgeScope | undefined,
): boolean {
if (!enterpriseKnowledgeStoreEnabled()) return true;
if (!enterpriseKnowledgeStoreEnabled() && !scope) return true;
const resolved = resolveKnowledgeScope(scope);
return (
pattern.provenance?.sourceTenantId === resolved.tenantId &&
Expand Down
2 changes: 1 addition & 1 deletion backend/src/agentv3/claudeMcpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ const REASONING_NUDGE_EN = '\n\n[REFLECT] Before the next action: what is the ke
export const MIN_PHASE_SUMMARY_CHARS = 15;

function sqlErrorLogFile(scope?: KnowledgeScope): string {
if (!enterpriseKnowledgeStoreEnabled()) {
if (!enterpriseKnowledgeStoreEnabled() && !scope) {
return path.join(SQL_ERROR_LOG_DIR, 'error_fix_pairs.json');
}
const resolved = resolveKnowledgeScope(scope);
Expand Down
40 changes: 22 additions & 18 deletions backend/src/agentv3/claudeRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,17 @@ import {
applyCapturedEntities,
} from '../agent/core/entityCapture';
import { DEFAULT_OUTPUT_LANGUAGE, localize } from './outputLanguage';
import { resolveFeatureConfig } from '../config';
import {
deleteClaudeSessionMapRuntimeSnapshots,
loadClaudeSessionMapFromRuntimeSnapshots,
saveClaudeSessionMapToRuntimeSnapshots,
type ClaudeSessionMapRuntimeEntry,
} from '../services/runtimeSnapshotStore';
import {
enterpriseDbWritesEnabled,
legacyFilesystemReadAuthorityEnabled,
legacyFilesystemWritesEnabled,
} from '../services/enterpriseMigration';
import type { ProviderScope } from '../services/providerManager';
import type { KnowledgeScope } from '../services/scopedKnowledgeStore';

Expand All @@ -104,8 +108,12 @@ interface SessionMapEntry {
updatedAt: number;
}

function enterpriseSessionMapStoreEnabled(): boolean {
return resolveFeatureConfig(process.env).enterprise;
function enterpriseSessionMapDbWritesEnabled(): boolean {
return enterpriseDbWritesEnabled();
}

function legacySessionMapWritesEnabled(): boolean {
return legacyFilesystemWritesEnabled();
}

function loadPersistedSessionMap(): Map<string, SessionMapEntry> {
Expand All @@ -130,22 +138,16 @@ function loadPersistedSessionMap(): Map<string, SessionMapEntry> {
}

function loadSessionMapForCurrentMode(): Map<string, SessionMapEntry> {
if (!enterpriseSessionMapStoreEnabled()) {
if (legacyFilesystemReadAuthorityEnabled()) {
return loadPersistedSessionMap();
}

try {
const dbMap = loadClaudeSessionMapFromRuntimeSnapshots(SESSION_MAP_MAX_AGE_MS);
if (dbMap.size > 0) return dbMap;
return loadClaudeSessionMapFromRuntimeSnapshots(SESSION_MAP_MAX_AGE_MS);
} catch (err) {
console.warn('[ClaudeRuntime] Failed to load runtime_snapshots session map:', (err as Error).message);
}

const legacyMap = loadPersistedSessionMap();
if (legacyMap.size > 0) {
console.warn('[ClaudeRuntime] Loaded legacy logs/claude_session_map.json for migration; future enterprise writes use runtime_snapshots');
}
return legacyMap;
return new Map();
}

function providerScopeFromOptions(options: AnalysisOptions): ProviderScope | undefined {
Expand Down Expand Up @@ -420,11 +422,12 @@ export class ClaudeRuntime extends EventEmitter implements IOrchestrator {
entry: ClaudeSessionMapRuntimeEntry,
options: AnalysisOptions,
): void {
if (!enterpriseSessionMapStoreEnabled()) {
if (legacySessionMapWritesEnabled()) {
savePersistedSessionMap(this.sessionMap);
return;
}

if (!enterpriseSessionMapDbWritesEnabled()) return;

if (!options.tenantId || !options.workspaceId) {
console.warn('[ClaudeRuntime] Enterprise session map persistence skipped: missing tenant/workspace scope');
return;
Expand Down Expand Up @@ -600,7 +603,7 @@ export class ClaudeRuntime extends EventEmitter implements IOrchestrator {
&& (Date.now() - (existingSessionMapEntry.updatedAt || 0) < SDK_SESSION_FRESHNESS_MS)
? existingSessionMapEntry.sdkSessionId
: undefined;
if (existingSessionMapEntry && existingSdkSessionId && enterpriseSessionMapStoreEnabled()) {
if (existingSessionMapEntry && existingSdkSessionId && enterpriseSessionMapDbWritesEnabled()) {
this.persistSessionMapEntry(sessionId, traceId, ctx.sessionMapKey, existingSessionMapEntry, options);
}

Expand Down Expand Up @@ -1716,7 +1719,7 @@ export class ClaudeRuntime extends EventEmitter implements IOrchestrator {
&& (Date.now() - (sessionMapEntry.updatedAt || 0) < SDK_SESSION_FRESHNESS_MS)
? sessionMapEntry.sdkSessionId
: undefined;
if (sessionMapEntry && existingSdkSessionId && enterpriseSessionMapStoreEnabled()) {
if (sessionMapEntry && existingSdkSessionId && enterpriseSessionMapDbWritesEnabled()) {
this.persistSessionMapEntry(sessionId, traceId, sessionMapKey, sessionMapEntry, options);
}
const sdkEnv = createSdkEnv(options.providerId, providerScope);
Expand Down Expand Up @@ -1965,13 +1968,14 @@ export class ClaudeRuntime extends EventEmitter implements IOrchestrator {
this.sessionHypotheses.delete(sessionId);
this.sessionUncertaintyFlags.delete(sessionId);
this.activeAnalyses.delete(sessionId);
if (enterpriseSessionMapStoreEnabled()) {
if (enterpriseSessionMapDbWritesEnabled()) {
try {
deleteClaudeSessionMapRuntimeSnapshots(sessionId);
} catch (err) {
console.warn('[ClaudeRuntime] Failed to delete runtime_snapshots session map:', (err as Error).message);
}
} else {
}
if (legacySessionMapWritesEnabled()) {
// Use immediate save — session is being removed, must persist before cleanup completes
savePersistedSessionMapSync(this.sessionMap);
}
Expand Down
44 changes: 27 additions & 17 deletions backend/src/agentv3/projectMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ import {
type MemoryScope,
} from '../types/sparkContracts';
import {
enterpriseKnowledgeDbWritesEnabled,
enterpriseKnowledgeStoreEnabled,
legacyKnowledgeFilesystemWritesEnabled,
type KnowledgeScope,
getScopedKnowledgeRecord,
listScopedKnowledgeRecords,
Expand Down Expand Up @@ -155,7 +157,11 @@ export class ProjectMemory {
): void {
this.load();
this.assertSaveInvariants(entry);
if (enterpriseKnowledgeStoreEnabled()) {
if (legacyKnowledgeFilesystemWritesEnabled()) {
this.entries.set(entry.entryId, entry);
this.persist();
}
if (enterpriseKnowledgeDbWritesEnabled()) {
upsertScopedKnowledgeRecord(
KNOWLEDGE_KIND,
entry.entryId,
Expand All @@ -168,10 +174,7 @@ export class ProjectMemory {
sourceRunId: storageScope?.sourceRunId ?? storageScope?.runId,
},
);
return;
}
this.entries.set(entry.entryId, entry);
this.persist();
}

/** Get an entry by id. */
Expand All @@ -196,13 +199,17 @@ export class ProjectMemory {
entryId: string,
storageScope?: KnowledgeScope,
): boolean {
if (enterpriseKnowledgeStoreEnabled()) {
return removeScopedKnowledgeRecord(KNOWLEDGE_KIND, entryId, storageScope);
let removed = false;
if (enterpriseKnowledgeDbWritesEnabled()) {
removed = removeScopedKnowledgeRecord(KNOWLEDGE_KIND, entryId, storageScope) || removed;
}
this.load();
const had = this.entries.delete(entryId);
if (had) this.persist();
return had;
if (legacyKnowledgeFilesystemWritesEnabled()) {
this.load();
const had = this.entries.delete(entryId);
if (had) this.persist();
removed = had || removed;
}
return removed;
}

/** Filtered list, deterministically ordered by entryId. */
Expand Down Expand Up @@ -329,7 +336,15 @@ export class ProjectMemory {
promotionLevel: (entry.promotionLevel ?? 0) + 1,
promotionPolicy: policy,
};
if (enterpriseKnowledgeStoreEnabled()) {
const auditEntry = {entryId, policy, auditedAt: Date.now()};
let auditRecorded = false;
if (legacyKnowledgeFilesystemWritesEnabled()) {
this.entries.set(entryId, promoted);
this.auditLog.push(auditEntry);
this.persist();
auditRecorded = true;
}
if (enterpriseKnowledgeDbWritesEnabled()) {
upsertScopedKnowledgeRecord(
KNOWLEDGE_KIND,
entryId,
Expand All @@ -342,13 +357,8 @@ export class ProjectMemory {
sourceRunId: storageScope?.sourceRunId ?? storageScope?.runId,
},
);
this.auditLog.push({entryId, policy, auditedAt: Date.now()});
this.persist();
return;
if (!auditRecorded) this.auditLog.push(auditEntry);
}
this.entries.set(entryId, promoted);
this.auditLog.push({entryId, policy, auditedAt: Date.now()});
this.persist();
}

/** Read-only view of the audit log, sorted by audit time ascending. */
Expand Down
Loading