fix(FN-2036): wire agent reflections runtime and UI dependencies
- Add a reusable .btn-secondary style (including light-theme hover/focus states) for reflections tab actions - Initialize ReflectionStore in reflection API routes before reading or writing reflections - Create AgentStore and ReflectionStore during in-process runtime startup and wire AgentReflectionService into TaskExecutor - Reuse the initialized AgentStore for HeartbeatMonitor and trigger scheduler setup with non-fatal guards
This commit is contained in:
@@ -1192,6 +1192,22 @@ body {
|
||||
box-shadow: var(--glow-warning);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: var(--card-hover);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
.btn-secondary:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
.btn-secondary:focus-visible {
|
||||
box-shadow: var(--focus-ring-strong);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
@@ -10841,6 +10857,16 @@ html .column.drag-over * {
|
||||
box-shadow: var(--glow-warning);
|
||||
}
|
||||
|
||||
[data-theme="light"] .btn-secondary {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
}
|
||||
[data-theme="light"] .btn-secondary:hover {
|
||||
background: var(--card-hover);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
THEME SELECTOR COMPONENT STYLES
|
||||
============================================================ */
|
||||
|
||||
@@ -13016,6 +13016,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const agentStore = new AgentStore({ rootDir: scopedStore.getFusionDir() });
|
||||
const reflectionStore = new ReflectionStore({ rootDir: scopedStore.getFusionDir() });
|
||||
await agentStore.init();
|
||||
await reflectionStore.init();
|
||||
|
||||
const agentId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
|
||||
if (!agentId) {
|
||||
@@ -13056,6 +13057,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const agentStore = new AgentStore({ rootDir: scopedStore.getFusionDir() });
|
||||
const reflectionStore = new ReflectionStore({ rootDir: scopedStore.getFusionDir() });
|
||||
await agentStore.init();
|
||||
await reflectionStore.init();
|
||||
|
||||
const agentId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
|
||||
if (!agentId) {
|
||||
@@ -13097,6 +13099,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const agentStore = new AgentStore({ rootDir: taskStore.getRootDir() });
|
||||
const reflectionStore = new ReflectionStore({ rootDir: taskStore.getRootDir() });
|
||||
await agentStore.init();
|
||||
await reflectionStore.init();
|
||||
|
||||
const agentId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
|
||||
if (!agentId) {
|
||||
@@ -13142,6 +13145,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const agentStore = new AgentStore({ rootDir: scopedStore.getFusionDir() });
|
||||
const reflectionStore = new ReflectionStore({ rootDir: scopedStore.getFusionDir() });
|
||||
await agentStore.init();
|
||||
await reflectionStore.init();
|
||||
|
||||
const agentId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
|
||||
if (!agentId) {
|
||||
@@ -13182,6 +13186,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const agentStore = new AgentStore({ rootDir: taskStore.getRootDir() });
|
||||
const reflectionStore = new ReflectionStore({ rootDir: taskStore.getRootDir() });
|
||||
await agentStore.init();
|
||||
await reflectionStore.init();
|
||||
|
||||
const agentId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
|
||||
if (!agentId) {
|
||||
|
||||
@@ -254,7 +254,7 @@ export class InProcessRuntime
|
||||
},
|
||||
});
|
||||
|
||||
// 5. Initialize TaskExecutor
|
||||
// 5b. Initialize TaskExecutor
|
||||
this.stuckTaskDetector = new StuckTaskDetector(this.taskStore, {
|
||||
beforeRequeue: (taskId) => this.selfHealingManager?.checkStuckBudget(taskId) ?? Promise.resolve(true),
|
||||
onLoopDetected: (event) => this.executor?.handleLoopDetected(event) ?? Promise.resolve(false),
|
||||
@@ -268,6 +268,46 @@ export class InProcessRuntime
|
||||
},
|
||||
});
|
||||
|
||||
// 5a. Initialize AgentStore (required for reflection service and heartbeat monitoring)
|
||||
let agentStoreForReflection: import("@fusion/core").AgentStore | undefined;
|
||||
try {
|
||||
const { AgentStore: AgentStoreClass } = await import("@fusion/core");
|
||||
agentStoreForReflection = new AgentStoreClass({ rootDir: this.taskStore.getFusionDir() });
|
||||
await agentStoreForReflection.init();
|
||||
runtimeLog.log("AgentStore initialized for reflection service");
|
||||
} catch (agentErr) {
|
||||
runtimeLog.warn(`AgentStore initialization failed (reflection service will be unavailable):`, agentErr instanceof Error ? agentErr.message : agentErr);
|
||||
}
|
||||
this.agentStore = agentStoreForReflection;
|
||||
|
||||
// 5b. Initialize ReflectionStore for agent reflections
|
||||
let reflectionStoreForService: import("@fusion/core").ReflectionStore | undefined;
|
||||
try {
|
||||
const { ReflectionStore: ReflectionStoreClass } = await import("@fusion/core");
|
||||
reflectionStoreForService = new ReflectionStoreClass({ rootDir: this.taskStore.getFusionDir() });
|
||||
await reflectionStoreForService.init();
|
||||
runtimeLog.log("ReflectionStore initialized for reflection service");
|
||||
} catch (reflErr) {
|
||||
runtimeLog.warn(`ReflectionStore initialization failed (reflection service will be unavailable):`, reflErr instanceof Error ? reflErr.message : reflErr);
|
||||
}
|
||||
|
||||
// 5c. Initialize AgentReflectionService (requires agentStore and reflectionStore)
|
||||
let reflectionService: import("@fusion/engine").AgentReflectionService | undefined;
|
||||
if (agentStoreForReflection && reflectionStoreForService) {
|
||||
try {
|
||||
const { AgentReflectionService: AgentReflectionServiceClass } = await import("@fusion/engine");
|
||||
reflectionService = new AgentReflectionServiceClass({
|
||||
agentStore: agentStoreForReflection,
|
||||
taskStore: this.taskStore,
|
||||
reflectionStore: reflectionStoreForService,
|
||||
rootDir: this.config.workingDirectory,
|
||||
});
|
||||
runtimeLog.log("AgentReflectionService initialized");
|
||||
} catch (reflServiceErr) {
|
||||
runtimeLog.warn(`AgentReflectionService initialization failed:`, reflServiceErr instanceof Error ? reflServiceErr.message : reflServiceErr);
|
||||
}
|
||||
}
|
||||
|
||||
const executorOptions: TaskExecutorOptions = {
|
||||
semaphore: this.globalSemaphore,
|
||||
pool: this.worktreePool,
|
||||
@@ -276,6 +316,7 @@ export class InProcessRuntime
|
||||
pluginRunner: this.pluginRunner,
|
||||
messageStore: this.messageStore,
|
||||
missionStore,
|
||||
reflectionService,
|
||||
onSliceComplete: (slice) => {
|
||||
void this.scheduler.onSliceComplete(slice);
|
||||
},
|
||||
@@ -363,12 +404,11 @@ export class InProcessRuntime
|
||||
executorOptions
|
||||
);
|
||||
|
||||
// 6. Initialize AgentStore and HeartbeatMonitor
|
||||
try {
|
||||
const { AgentStore: AgentStoreClass } = await import("@fusion/core");
|
||||
this.agentStore = new AgentStoreClass({ rootDir: this.taskStore.getFusionDir() });
|
||||
await this.agentStore.init();
|
||||
|
||||
// 6. Initialize HeartbeatMonitor (reuses AgentStore from step 5a)
|
||||
if (this.heartbeatMonitor) {
|
||||
// Already started — nothing to do
|
||||
}
|
||||
if (!this.heartbeatMonitor && this.agentStore) {
|
||||
this.heartbeatMonitor = new HeartbeatMonitor({
|
||||
store: this.agentStore,
|
||||
agentStore: this.agentStore, // enables per-agent config resolution
|
||||
@@ -383,8 +423,10 @@ export class InProcessRuntime
|
||||
},
|
||||
});
|
||||
this.heartbeatMonitor.start();
|
||||
}
|
||||
|
||||
// Initialize HeartbeatTriggerScheduler
|
||||
// 6a. Initialize HeartbeatTriggerScheduler (only if agentStore is available)
|
||||
if (this.agentStore) {
|
||||
this.triggerScheduler = new HeartbeatTriggerScheduler(
|
||||
this.agentStore,
|
||||
async (agentId, source, context: WakeContext) => {
|
||||
@@ -465,10 +507,7 @@ export class InProcessRuntime
|
||||
runtimeLog.warn(`Failed to register agents for heartbeat triggers:`, regErr);
|
||||
}
|
||||
|
||||
runtimeLog.log(`AgentStore, HeartbeatMonitor, and TriggerScheduler initialized`);
|
||||
} catch (agentErr) {
|
||||
// Non-fatal — agent monitoring is optional
|
||||
runtimeLog.warn(`AgentStore initialization failed (continuing without agent monitoring):`, agentErr);
|
||||
runtimeLog.log(`HeartbeatMonitor and TriggerScheduler initialized`);
|
||||
}
|
||||
|
||||
// 7. Initialize TriageProcessor (task specification)
|
||||
|
||||
Reference in New Issue
Block a user