feat: component wiring integration — orphan cleanup, Memory page, WS handlers

Memory page: Added REST endpoints (routes/memory.ts, 3 GETs: list/daily/dreams),
React route in App.tsx, nav link in ProjectSidebar (Brain icon).

Orphan components wired: KeyboardShortcutsDialog (? key in AppShell),
McpResponseDisplay (MCP tool results in ToolCallLine), CacheShapeBadge
(StatsLine in MessageBubble). MessageBoundary + MessageListErrorBoundary
confirmed already wired in MarkdownRenderer/MessageList.

Dead code cleanup: useDraftPersistence integrated into ChatInput
(localStorage draft save/restore/clear on send). message-parts barrel
made canonical — MessageBubble imports from it; StatsLine updated with
CacheShapeBadge parity. api.settings.inference typed wrapper added;
InferenceSettings raw fetch replaced.

WS frame handlers: reasoning_delta (accumulates like delta), tool_trace_start,
tool_trace_finish, collision_warning, agent_message acknowledged in
useSessionStream. CollisionWarningEvent + AgentMessageEvent added to
sessionEvents union. Forwarding in useCoderUserEvents. reasoning_delta
+ collision_warning added to web WsFrame type. useSidebar default case
fixes pre-existing fallthrough error.

Workflow engine: services/workflow/index.ts documented as experimental;
coder flow-runner (apps/coder/src/services/flow-runner.ts) is canonical.

Verification: web type-check clean, server build clean, 627 tests pass.
This commit is contained in:
2026-06-08 04:30:09 +00:00
parent 3724016b24
commit fc281f5b78
17 changed files with 268 additions and 710 deletions

View File

@@ -21,6 +21,7 @@ import { registerSkillsRoutes } from './routes/skills.js';
import { registerTraceRoutes } from './routes/traces.js';
import { registerToolsRoutes } from './routes/tools.js';
import { registerAnalyticsRoutes } from './routes/analytics.js';
import { registerMemoryRoutes } from './routes/memory.js';
import { registerInferenceSettingsRoutes } from './routes/inference-settings.js';
import { createInferenceRunner, runInferenceWithModel } from './services/inference/index.js';
@@ -155,6 +156,7 @@ async function main() {
hasActiveInference: (chatId) => inference.hasActive(chatId),
});
registerTraceRoutes(app, sql);
registerMemoryRoutes(app, sql);
registerToolsRoutes(app, sql);
registerAnalyticsRoutes(app, sql);
registerInferenceSettingsRoutes(app);

View File

@@ -0,0 +1,91 @@
import type { FastifyInstance } from 'fastify';
import type { Sql } from '../db.js';
// ── Row types matching memory_entries table columns ───────────────────────
// These mirror the frontend types in apps/web/src/api/types.ts.
interface MemoryEntryRow {
id: string;
topic: string;
title: string;
content: string;
tags: string[];
}
interface DailyMemoryEntryRow extends MemoryEntryRow {
date: string;
}
interface DreamEntryRow {
date: string;
content: string;
}
export function registerMemoryRoutes(app: FastifyInstance, sql: Sql): void {
// GET /api/memory?project_id=<id> — topic-based memory entries
app.get<{ Querystring: { project_id?: string } }>(
'/api/memory',
async (req) => {
const projectId = req.query.project_id
if (!projectId) {
return { entries: [] }
}
const rows = await sql<MemoryEntryRow[]>`
SELECT id, topic, title, content, COALESCE(tags, ARRAY[]::text[]) AS tags
FROM memory_entries
WHERE project_id = ${projectId}
AND date IS NULL
ORDER BY created_at DESC
`
return { entries: rows }
},
)
// GET /api/memory/daily?project_id=<id> — daily log entries
app.get<{ Querystring: { project_id?: string } }>(
'/api/memory/daily',
async (req) => {
const projectId = req.query.project_id
if (!projectId) {
return { entries: [] }
}
const rows = await sql<DailyMemoryEntryRow[]>`
SELECT
id, topic, title, content,
COALESCE(tags, ARRAY[]::text[]) AS tags,
date::text AS date
FROM memory_entries
WHERE project_id = ${projectId}
AND date IS NOT NULL
AND mood IS NULL
ORDER BY date DESC, created_at DESC
`
return { entries: rows }
},
)
// GET /api/memory/dreams?project_id=<id> — dream consolidation diaries
app.get<{ Querystring: { project_id?: string } }>(
'/api/memory/dreams',
async (req) => {
const projectId = req.query.project_id
if (!projectId) {
return { entries: [] }
}
const rows = await sql<DreamEntryRow[]>`
SELECT date::text AS date, content
FROM memory_entries
WHERE project_id = ${projectId}
AND mood IS NOT NULL
ORDER BY date DESC, created_at DESC
`
return { entries: rows }
},
)
}

View File

@@ -1,5 +1,35 @@
// v2.8.0: Dynamic Workflow Engine — public surface.
//
// ## Status: experimental / intentionally decoupled from the coder flow-runner
//
// This module is an in-process multi-agent orchestrator that creates BooChat
// sessions+chats and dispatches inference via the native `runInference`
// pipeline. It is NOT currently wired into the server (`apps/server/src/index.ts`)
// — no routes import it, no service initialises it, and the server has no
// `projectRoot`/`projectId` concept at startup. All code is preserved for future
// evaluation but is not in use.
//
// ## Relationship to the coder flow-runner
//
// The canonical orchestrator implementation lives at:
// `apps/coder/src/services/flow-runner.ts` (1102 lines, actively wired)
//
// The two modules serve different dispatch strategies:
//
// | Dimension | Server WorkflowManager (this) | Coder flow-runner |
// |-------------------|-----------------------------------|------------------------------------|
// | Dispatch | In-process via `runInference` | Task rows → external agent binary |
// | Agent target | BooChat native inference | qwen via PTY (--approval-mode plan)|
// | Session model | Per-agent BooChat sessions+chats | Per-step synthetic sessions |
// | Persistence | In-memory (Map<runId, state>) | DB-backed (flow_runs/flow_steps) |
// | Lifecycle | Polling loop + AbortController | Dispatcher hook (onTaskTerminal) |
// | Status | Experimental, not wired | Active, production |
//
// These two engines are NOT competitors — they are alternative approaches for
// different dispatch surfaces. Use the coder flow-runner for the current
// orchestrator; revisit this module if in-process BooChat-native multi-agent
// orchestration becomes a requirement.
//
// Re-exports all types and classes from the workflow sub-modules so consumers
// import from a single entry point:
//