Skip to content
Merged
2 changes: 1 addition & 1 deletion src/mirror-cli/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export const MIRROR_CLI_COMMAND_HELP: MirrorCliCommandHelp[] = [
description: "Verify canonical lore files against a lore manifest.",
args: [],
options: [
"--manifest <path>: lore manifest path (default: <MIRROR_LORE_DIR>/manifest.json)",
"--manifest <path>: lore manifest path (default: <resolved lore dir>/manifest.json)",
"--dir <path>: canonical lore directory (default: MIRROR_LORE_DIR or ./lore-scrolls)",
"--json: emit stable machine-readable JSON",
],
Expand Down
7 changes: 4 additions & 3 deletions src/mirror-gateway/chat_route_runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
buildHttpChatAdapterEnvelope,
type MirrorAdapterResponseEnvelope,
} from "../mirror-adapters/index.js";
import { incrementMetric, logMirrorEvent } from "../mirror-observability/index.js";
import type { MirrorObservabilityContext } from "../mirror-observability/index.js";
import type { MirrorPolicyContext } from "../mirror-policy/index.js";
import { withMirrorCorrelation } from "../mirror-runtime/index.js";

Expand All @@ -22,6 +22,7 @@ type ExecuteMirrorGatewayChatRouteOptions = {
executeAdapterRequest: (
envelope: ReturnType<typeof buildHttpChatAdapterEnvelope>,
) => Promise<MirrorAdapterResponseEnvelope>;
observability: Pick<MirrorObservabilityContext, "incrementMetric" | "logEvent">;
onRuntimeEvent?: (type: string, payload?: Record<string, unknown>) => void;
readIngressAdapterDescriptor: ReadIngressAdapterDescriptor;
readIngressRoutePath: (req: express.Request) => string;
Expand All @@ -48,8 +49,8 @@ export async function executeMirrorGatewayChatRoute(
const adapterDescriptor = options.readIngressAdapterDescriptor(
options.readIngressRoutePath(req),
);
incrementMetric("chat_requests");
logMirrorEvent("chat.pipeline", { route: "mirror.chat" });
options.observability.incrementMetric("chat_requests");
options.observability.logEvent("chat.pipeline", { route: "mirror.chat" });
options.onRuntimeEvent?.(
"chat.started",
withMirrorCorrelation(
Expand Down
11 changes: 10 additions & 1 deletion src/mirror-gateway/mirror_gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
type MirrorAdapterRequestEnvelope,
type MirrorAdapterResponseEnvelope,
} from "../mirror-adapters/index.js";
import type { MirrorObservabilityContext } from "../mirror-observability/index.js";
import {
buildMirrorChatPolicyTarget,
buildMirrorAdapterPolicyTarget,
Expand Down Expand Up @@ -185,7 +186,14 @@ function buildAdapterPolicyContext(envelope: MirrorAdapterRequestEnvelope): Mirr

export function createMirrorGateway(
basePath = "/mirror",
options: { providerPlane?: MirrorProviderPlane; policy?: MirrorPolicyEngine } = {},
options: {
observability?: Pick<
MirrorObservabilityContext,
"incrementMetric" | "incrementToolExecution" | "logEvent"
>;
providerPlane?: MirrorProviderPlane;
policy?: MirrorPolicyEngine;
} = {},
): MirrorGateway {
const registry = createMirrorToolRegistry(getMirrorNativeSkillTools());
const actionRuntime = createMirrorActionRuntime(
Expand Down Expand Up @@ -309,6 +317,7 @@ export function createMirrorGateway(
}

const handlers = createMirrorGatewayHandlers(toolRegistry, {
observability: options.observability,
providerPlane: options.providerPlane,
executeAdapterRequest: executeAdapterRequestInternal,
});
Expand Down
52 changes: 39 additions & 13 deletions src/mirror-gateway/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import {
buildHttpToolAdapterEnvelope,
type MirrorAdapterResponseEnvelope,
} from "../mirror-adapters/index.js";
import { incrementMetric, logMirrorEvent } from "../mirror-observability/index.js";
import {
getCurrentMirrorObservabilityContext,
type MirrorObservabilityContext,
} from "../mirror-observability/index.js";
import { type MirrorPolicyContext } from "../mirror-policy/index.js";
import { type MirrorProviderConfig, type MirrorProviderPlane } from "../mirror-provider/index.js";
import { resolveMirrorTraceId } from "../mirror-runtime/index.js";
Expand Down Expand Up @@ -83,6 +86,10 @@ export type MirrorGatewayHandlers = {
export function createMirrorGatewayHandlers(
registry = createMirrorToolRegistry(getMirrorNativeSkillTools()),
options: {
observability?: Pick<
MirrorObservabilityContext,
"incrementMetric" | "incrementToolExecution" | "logEvent"
>;
provider?: MirrorProviderConfig;
providerPlane?: MirrorProviderPlane;
onRuntimeEvent?: (type: string, payload?: Record<string, unknown>) => void;
Expand All @@ -93,6 +100,23 @@ export function createMirrorGatewayHandlers(
) => Promise<MirrorAdapterResponseEnvelope>;
},
): MirrorGatewayHandlers {
const observability =
options.observability ??
({
incrementMetric(name, amount) {
getCurrentMirrorObservabilityContext().incrementMetric(name, amount);
},
incrementToolExecution(toolName) {
getCurrentMirrorObservabilityContext().incrementToolExecution(toolName);
},
logEvent(event, fields) {
getCurrentMirrorObservabilityContext().logEvent(event, fields);
},
} satisfies Pick<
MirrorObservabilityContext,
"incrementMetric" | "incrementToolExecution" | "logEvent"
>);

function buildPolicyContext(
req: express.Request,
body: Record<string, unknown>,
Expand Down Expand Up @@ -140,30 +164,30 @@ export function createMirrorGatewayHandlers(

function recordWorkspaceToolObservability(toolName: string): void {
if (toolName.startsWith("mirror.task.")) {
incrementMetric("workspace_events");
incrementMetric("task_operations");
logMirrorEvent("workspace.task", { tool: toolName });
observability.incrementMetric("workspace_events");
observability.incrementMetric("task_operations");
observability.logEvent("workspace.task", { tool: toolName });
return;
}

if (toolName.startsWith("mirror.reminder.")) {
incrementMetric("workspace_events");
incrementMetric("reminder_operations");
logMirrorEvent("workspace.reminder", { tool: toolName });
observability.incrementMetric("workspace_events");
observability.incrementMetric("reminder_operations");
observability.logEvent("workspace.reminder", { tool: toolName });
return;
}

if (toolName.startsWith("mirror.heartbeat.")) {
incrementMetric("workspace_events");
incrementMetric("heartbeat_operations");
logMirrorEvent("workspace.heartbeat", { tool: toolName });
observability.incrementMetric("workspace_events");
observability.incrementMetric("heartbeat_operations");
observability.logEvent("workspace.heartbeat", { tool: toolName });
return;
}

if (toolName.startsWith("mirror.monk.")) {
incrementMetric("workspace_events");
incrementMetric("monk_actions");
logMirrorEvent("workspace.monk", { tool: toolName });
observability.incrementMetric("workspace_events");
observability.incrementMetric("monk_actions");
observability.logEvent("workspace.monk", { tool: toolName });
}
}

Expand Down Expand Up @@ -206,6 +230,7 @@ export function createMirrorGatewayHandlers(
{
buildPolicyContext,
executeAdapterRequest: options.executeAdapterRequest,
observability,
onRuntimeEvent: options.onRuntimeEvent,
readIngressAdapterDescriptor,
readIngressRoutePath,
Expand All @@ -231,6 +256,7 @@ export function createMirrorGatewayHandlers(
{
buildPolicyContext,
executeAdapterRequest: options.executeAdapterRequest,
observability,
onRuntimeEvent: options.onRuntimeEvent,
readIngressAdapterDescriptor,
readIngressRoutePath,
Expand Down
9 changes: 5 additions & 4 deletions src/mirror-gateway/tool_route_runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
buildHttpToolAdapterEnvelope,
type MirrorAdapterResponseEnvelope,
} from "../mirror-adapters/index.js";
import { incrementToolExecution, logMirrorEvent } from "../mirror-observability/index.js";
import type { MirrorObservabilityContext } from "../mirror-observability/index.js";
import type { MirrorPolicyContext } from "../mirror-policy/index.js";
import { withMirrorCorrelation } from "../mirror-runtime/index.js";
import type { MirrorSkillTool } from "../mirror/skills/index.js";
Expand All @@ -25,6 +25,7 @@ type ExecuteMirrorGatewayToolRouteOptions = {
executeAdapterRequest: (
envelope: ReturnType<typeof buildHttpToolAdapterEnvelope>,
) => Promise<MirrorAdapterResponseEnvelope>;
observability: Pick<MirrorObservabilityContext, "incrementToolExecution" | "logEvent">;
onRuntimeEvent?: (type: string, payload?: Record<string, unknown>) => void;
readIngressAdapterDescriptor: ReadIngressAdapterDescriptor;
readIngressRoutePath: (req: express.Request) => string;
Expand Down Expand Up @@ -78,9 +79,9 @@ export async function executeMirrorGatewayToolRoute(
},
),
);
incrementToolExecution(options.tool.metadata.name);
logMirrorEvent("tool.execution", { tool: options.tool.metadata.name });
logMirrorEvent("action.execution", { action: options.tool.metadata.name });
options.observability.incrementToolExecution(options.tool.metadata.name);
options.observability.logEvent("tool.execution", { tool: options.tool.metadata.name });
options.observability.logEvent("action.execution", { action: options.tool.metadata.name });
options.onRuntimeEvent?.(
"action.execution.started",
withMirrorCorrelation(
Expand Down
1 change: 1 addition & 0 deletions src/mirror-service/mirror_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export async function startMirrorService(
const actionRuntime = gateway.actionRuntime;

const handlers = createMirrorGatewayHandlers(gateway.registry, {
observability,
providerPlane,
onRuntimeEvent: daemon.publishRuntimeEvent,
executeAdapterRequest: async (envelope) => await runtimeHost.executeAdapterRequest(envelope),
Expand Down
5 changes: 4 additions & 1 deletion src/mirror-service/runtime_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ export async function createMirrorRuntimeHost(
lifecycle,
providerPlane,
});
const gateway = createMirrorGateway("/mirror", { providerPlane });
const gateway = createMirrorGateway("/mirror", {
observability: daemon.getObservability(),
providerPlane,
});
const syncManager = createMirrorSyncManager({
nodeId: config.nodeId,
loreDir: config.loreDir,
Expand Down
2 changes: 1 addition & 1 deletion src/mirror/telemetry_tail/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export function registerMirrorTelemetryCli(program: Command): void {
mirror
.command("verify-lore")
.description("Compatibility wrapper for mirror verify-lore")
.option("--manifest <path>", "Lore manifest path (default: <MIRROR_LORE_DIR>/manifest.json)")
.option("--manifest <path>", "Lore manifest path (default: <resolved lore dir>/manifest.json)")
.option("--dir <path>", "Canonical lore directory (default: MIRROR_LORE_DIR or ./lore-scrolls)")
.option("--json", "Output machine-readable JSON", false)
.action(async (opts: { manifest?: string; dir?: string; json?: boolean }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/mirrordaemon/debug_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { buildDebugSnapshot } from "./runtime_state.js";

export function getMirrordaemonDebugState(
daemon: Mirrordaemon,
params: { port?: number; baseUrl?: string | null; peersKnown?: number } = {},
params: { port?: number; baseUrl?: string | null } = {},
) {
return buildDebugSnapshot(daemon, params);
}
5 changes: 2 additions & 3 deletions src/mirrordaemon/runtime_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ export function buildHealthSummary(
): MirrordaemonHealthSummary {
const runtime = buildRuntimeSummary(daemon, overrides);
const boot = daemon.getBootSnapshot();
const metrics = daemon.getObservability().getMetrics();

return {
...runtime,
Expand All @@ -211,7 +210,7 @@ export function buildHealthSummary(
fallback_available: boot.readiness.provider.fallback_available,
},
sync: {
peers_known: overrides.peers?.length ?? metrics.gauges.peers_known ?? 0,
peers_known: overrides.peers?.length ?? 0,
},
observability: {
metrics_available: true,
Expand All @@ -222,7 +221,7 @@ export function buildHealthSummary(

export function buildDebugSnapshot(
daemon: Mirrordaemon,
overrides: RuntimeStateOverrides & { peersKnown?: number } = {},
overrides: RuntimeStateOverrides = {},
): MirrordaemonDebugSnapshot {
const observability = daemon.getObservability();
return {
Expand Down
3 changes: 3 additions & 0 deletions test/mirror-help-surface-boundary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ describe("mirror standalone help surface", () => {
expect(schemaSource).toContain('command: "status"');
expect(schemaSource).toContain('command: "verify-lore"');
expect(schemaSource).toContain('command: "sync"');
expect(schemaSource).toContain(
"--manifest <path>: lore manifest path (default: <resolved lore dir>/manifest.json)",
);
});
});
Loading