diff --git a/src/gateway/node-connect-reconcile.ts b/src/gateway/node-connect-reconcile.ts index 6b5111985bd..babd9f4ff52 100644 --- a/src/gateway/node-connect-reconcile.ts +++ b/src/gateway/node-connect-reconcile.ts @@ -1,3 +1,5 @@ +// Gateway node connect reconciliation. +// Computes approved runtime surfaces and pending pairing upgrades on reconnect. import type { ConnectParams } from "../../packages/gateway-protocol/src/index.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { diff --git a/src/gateway/node-pending-work.ts b/src/gateway/node-pending-work.ts index ef4015df25e..35757f310b1 100644 --- a/src/gateway/node-pending-work.ts +++ b/src/gateway/node-pending-work.ts @@ -1,3 +1,5 @@ +// Gateway pending node-work queue. +// Stores short-lived per-node prompts until connected nodes drain/ack them. import { randomUUID } from "node:crypto"; import { asDateTimestampMs, @@ -69,6 +71,8 @@ function getOrCreateState(nodeId: string): NodePendingWorkState { } function pruneExpired(state: NodePendingWorkState, nowMs: number): boolean { + // Expiry pruning bumps revision so polling nodes can observe that work changed + // even when no explicit acknowledge call happened. const validNowMs = asDateTimestampMs(nowMs); if (validNowMs === undefined) { return false; diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index d77ce2b0b1b..af707e5ff4f 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -1,3 +1,5 @@ +// Gateway node event dispatcher. +// Handles device/node-originated events and routes them to sessions/channels. import { randomUUID } from "node:crypto"; import { normalizeLowercaseStringOrEmpty, @@ -117,6 +119,8 @@ function shouldDropDuplicateVoiceTranscript(params: { fingerprint: string; now: number; }): boolean { + // Voice providers can replay identical transcript fragments during reconnect. + // Keep only a bounded last fingerprint per session to avoid duplicate sends. const previous = recentVoiceTranscripts.get(params.sessionKey); if ( previous && diff --git a/src/gateway/server-node-session-runtime.ts b/src/gateway/server-node-session-runtime.ts index 3c332fb14e3..59166a61a90 100644 --- a/src/gateway/server-node-session-runtime.ts +++ b/src/gateway/server-node-session-runtime.ts @@ -1,3 +1,5 @@ +// Gateway node session runtime factory. +// Creates node registry, subscription, and voice-wake fanout state. import { NodeRegistry, type SerializedEventPayload } from "./node-registry.js"; import { createSessionEventSubscriberRegistry, diff --git a/src/gateway/server-node-subscriptions.ts b/src/gateway/server-node-subscriptions.ts index 1db711aa3e7..71534d4548a 100644 --- a/src/gateway/server-node-subscriptions.ts +++ b/src/gateway/server-node-subscriptions.ts @@ -1,3 +1,5 @@ +// Gateway node subscription manager. +// Maintains bidirectional node/session fanout indexes. import { serializeEventPayload, type SerializedEventPayload } from "./node-registry.js"; // Node subscription manager keeps bidirectional node/session indexes so gateway @@ -92,6 +94,8 @@ export function createNodeSubscriptionManager(): NodeSubscriptionManager { if (!nodeSet) { return; } + // Remove reverse session indexes before deleting the node index so session + // fanout cannot retain disconnected node ids. for (const sessionKey of nodeSet) { const sessionSet = sessionSubscribers.get(sessionKey); sessionSet?.delete(normalizedNodeId);