Files
openchamber/packages/web/server/lib/hermes-integration/runtime.js
T
bot-hermes be159dac19 feat: add Hermes integration panel to Settings → Integrations
Server-side:
- New hermes-integration module (runtime.js + routes.js + tests)
  exposing GET /api/openchamber/hermes/status
- Reports availability of agent-activity, session-steer, and plan-gate
  runtimes plus plan-gate default config
- Wired into feature-routes-runtime.js alongside existing integrations

UI:
- New HermesIntegration.tsx collapsible panel in Integrations page
- Connection status indicator (green/red dot) with 30s polling
- Plan gate default toggle (persists via updateDesktopSettings +
  recordDeferredOpenCodeRestart)
- Read-only status rows for activity stream, steer channel, plan gate
- Server version and uptime display

Settings:
- hermesPlanGateDefault field in settings registry and useUIStore
- Search index entry with keywords: hermes, agent, integration, etc.
- i18n keys for all 12 locales (English text as fallback)

Verification:
- Server tests: 7/7 passed (runtime + routes)
- UI tests: 4/4 passed (HermesIntegration.test.tsx)
- Typecheck: clean (no new errors)
- oxlint: clean on all new/modified files
2026-09-10 11:25:51 +00:00

64 lines
1.7 KiB
JavaScript

// Hermes integration status: reports the availability and configuration of the
// three agent-to-agent runtimes (activity stream, steer channel, plan gate) that
// power the Hermes ↔ OpenChamber link. Pure read-only — does not modify any
// existing runtime.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PACKAGE_VERSION = (() => {
try {
const packagePath = path.resolve(__dirname, '..', '..', 'package.json');
const raw = fs.readFileSync(packagePath, 'utf8');
const pkg = JSON.parse(raw);
if (pkg && typeof pkg.version === 'string' && pkg.version.trim().length > 0) {
return pkg.version.trim();
}
} catch {
// fall through
}
return 'unknown';
})();
export function createHermesIntegrationRuntime({
agentActivityRuntime,
sessionSteerRuntime,
planGateRuntime,
readSettingsFromDiskMigrated,
startedAt = Date.now,
}) {
const getStatus = async () => {
const settings = await readSettingsFromDiskMigrated().catch(() => null);
const uptimeMs = Date.now() - startedAt();
return {
ok: true,
server: {
version: PACKAGE_VERSION,
uptimeMs,
},
integrations: {
agentActivity: {
available: agentActivityRuntime != null,
},
steer: {
available: sessionSteerRuntime != null,
},
planGate: {
available: planGateRuntime != null,
},
},
config: {
planGateDefault: Boolean(settings?.hermesPlanGateDefault),
activityRateLimitMs: 2000,
},
};
};
return { getStatus };
}