Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/src/auto-reply/reply/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 8 kB image not shown  

Quelle  session-delivery.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import type { SessionEntry } from "../../config/sessions.js";
import { buildAgentMainSessionKey } from "../../routing/session-key.js";
import { parseAgentSessionKey } from "../../sessions/session-key-utils.js";
import {
  normalizeLowercaseStringOrEmpty,
  normalizeOptionalLowercaseString,
  normalizeOptionalString,
} from "../../shared/string-coerce.js";
import {
  deliveryContextFromSession,
  deliveryContextKey,
  normalizeDeliveryContext,
} from "../../utils/delivery-context.js";
import {
  INTERNAL_MESSAGE_CHANNEL,
  isDeliverableMessageChannel,
  normalizeMessageChannel,
} from "../../utils/message-channel.js";
import type { MsgContext } from "../templating.js";

export type LegacyMainDeliveryRetirement = {
  key: string;
  entry: SessionEntry;
};

function resolveSessionKeyChannelHint(sessionKey?: string): string | undefined {
  const parsed = parseAgentSessionKey(sessionKey);
  if (!parsed?.rest) {
    return undefined;
  }
  const head = normalizeOptionalLowercaseString(parsed.rest.split(":")[0]);
  if (!head || head === "main" || head === "cron" || head === "subagent" || head === "acp") {
    return undefined;
  }
  return normalizeMessageChannel(head);
}

function isMainSessionKey(sessionKey?: string): boolean {
  const parsed = parseAgentSessionKey(sessionKey);
  if (!parsed) {
    return normalizeLowercaseStringOrEmpty(sessionKey) === "main";
  }
  return normalizeLowercaseStringOrEmpty(parsed.rest) === "main";
}

const DIRECT_SESSION_MARKERS = new Set(["direct", "dm"]);
const THREAD_SESSION_MARKERS = new Set(["thread", "topic"]);

function hasStrictDirectSessionTail(parts: string[], markerIndex: number): boolean {
  const peerId = normalizeOptionalString(parts[markerIndex + 1]);
  if (!peerId) {
    return false;
  }
  const tail = parts.slice(markerIndex + 2);
  if (tail.length === 0) {
    return true;
  }
  return (
    tail.length === 2 &&
    THREAD_SESSION_MARKERS.has(tail[0] ?? "") &&
    Boolean(normalizeOptionalString(tail[1]))
  );
}

function isDirectSessionKey(sessionKey?: string): boolean {
  const raw = normalizeLowercaseStringOrEmpty(sessionKey);
  if (!raw) {
    return false;
  }
  const scoped = parseAgentSessionKey(raw)?.rest ?? raw;
  const parts = scoped.split(":").filter(Boolean);
  if (parts.length < 2) {
    return false;
  }
  if (DIRECT_SESSION_MARKERS.has(parts[0] ?? "")) {
    return hasStrictDirectSessionTail(parts, 0);
  }
  const channel = normalizeMessageChannel(parts[0]);
  if (!channel || !isDeliverableMessageChannel(channel)) {
    return false;
  }
  if (DIRECT_SESSION_MARKERS.has(parts[1] ?? "")) {
    return hasStrictDirectSessionTail(parts, 1);
  }
  return Boolean(normalizeOptionalString(parts[1])) && DIRECT_SESSION_MARKERS.has(parts[2] ?? "")
    ? hasStrictDirectSessionTail(parts, 2)
    : false;
}

function isExternalRoutingChannel(channel?: string): channel is string {
  return Boolean(
    channel && channel !== INTERNAL_MESSAGE_CHANNEL && isDeliverableMessageChannel(channel),
  );
}

export function resolveLastChannelRaw(params: {
  originatingChannelRaw?: string;
  persistedLastChannel?: string;
  sessionKey?: string;
  isInterSession?: boolean;
}): string | undefined {
  const originatingChannel = normalizeMessageChannel(params.originatingChannelRaw);
  // WebChat should own reply routing for direct-session UI turns, but only when
  // the session has no established external delivery route. If the session was
  // created via an external channel (e.g. Telegram, iMessage), webchat/dashboard
  // access must not overwrite the persisted route — doing so causes subagent
  // completion events to be delivered to the dashboard instead of the original
  // channel. See: https://github.com/openclaw/openclaw/issues/47745
  const persistedChannel = normalizeMessageChannel(params.persistedLastChannel);
  const sessionKeyChannelHint = resolveSessionKeyChannelHint(params.sessionKey);
  const hasEstablishedExternalRoute =
    isExternalRoutingChannel(persistedChannel) || isExternalRoutingChannel(sessionKeyChannelHint);
  // Inter-session messages (sessions_send) always arrive with channel=webchat,
  // but must never overwrite an already-established external delivery route.
  // Without this guard, a sessions_send call resets lastChannel to webchat,
  // causing subsequent Discord (or other external) deliveries to be lost.
  // See: https://github.com/openclaw/openclaw/issues/54441
  if (params.isInterSession && hasEstablishedExternalRoute) {
    return persistedChannel || sessionKeyChannelHint;
  }
  if (
    originatingChannel === INTERNAL_MESSAGE_CHANNEL &&
    !hasEstablishedExternalRoute &&
    (isMainSessionKey(params.sessionKey) || isDirectSessionKey(params.sessionKey))
  ) {
    return params.originatingChannelRaw;
  }
  let resolved = params.originatingChannelRaw || params.persistedLastChannel;
  // Internal/non-deliverable sources should not overwrite previously known
  // external delivery routes (or explicit channel hints from the session key).
  if (!isExternalRoutingChannel(originatingChannel)) {
    if (isExternalRoutingChannel(persistedChannel)) {
      resolved = persistedChannel;
    } else if (isExternalRoutingChannel(sessionKeyChannelHint)) {
      resolved = sessionKeyChannelHint;
    }
  }
  return resolved;
}

export function resolveLastToRaw(params: {
  originatingChannelRaw?: string;
  originatingToRaw?: string;
  toRaw?: string;
  persistedLastTo?: string;
  persistedLastChannel?: string;
  sessionKey?: string;
  isInterSession?: boolean;
}): string | undefined {
  const originatingChannel = normalizeMessageChannel(params.originatingChannelRaw);
  const persistedChannel = normalizeMessageChannel(params.persistedLastChannel);
  const sessionKeyChannelHint = resolveSessionKeyChannelHint(params.sessionKey);
  const hasEstablishedExternalRouteForTo =
    isExternalRoutingChannel(persistedChannel) || isExternalRoutingChannel(sessionKeyChannelHint);
  // Inter-session messages must not replace a persisted external `to` with
  // webchat-scoped identifiers (e.g. session keys). Preserve the established
  // external destination so deliveries continue routing to the correct channel.
  // See: https://github.com/openclaw/openclaw/issues/54441
  if (params.isInterSession && hasEstablishedExternalRouteForTo && params.persistedLastTo) {
    return params.persistedLastTo;
  }
  if (
    originatingChannel === INTERNAL_MESSAGE_CHANNEL &&
    !hasEstablishedExternalRouteForTo &&
    (isMainSessionKey(params.sessionKey) || isDirectSessionKey(params.sessionKey))
  ) {
    return params.originatingToRaw || params.toRaw;
  }
  // When the turn originates from an internal/non-deliverable source, do not
  // replace an established external destination with internal routing ids
  // (e.g., session/webchat ids).
  if (!isExternalRoutingChannel(originatingChannel)) {
    const hasExternalFallback =
      isExternalRoutingChannel(persistedChannel) || isExternalRoutingChannel(sessionKeyChannelHint);
    if (hasExternalFallback && params.persistedLastTo) {
      return params.persistedLastTo;
    }
  }

  return params.originatingToRaw || params.toRaw || params.persistedLastTo;
}

export function maybeRetireLegacyMainDeliveryRoute(params: {
  sessionCfg: { dmScope?: string } | undefined;
  sessionKey: string;
  sessionStore: Record<string, SessionEntry>;
  agentId: string;
  mainKey: string;
  isGroup: boolean;
  ctx: MsgContext;
}): LegacyMainDeliveryRetirement | undefined {
  const dmScope = params.sessionCfg?.dmScope ?? "main";
  if (dmScope === "main" || params.isGroup) {
    return undefined;
  }
  const canonicalMainSessionKey = buildAgentMainSessionKey({
    agentId: params.agentId,
    mainKey: params.mainKey,
  });
  if (params.sessionKey === canonicalMainSessionKey) {
    return undefined;
  }
  const legacyMain = params.sessionStore[canonicalMainSessionKey];
  if (!legacyMain) {
    return undefined;
  }
  const legacyRouteKey = deliveryContextKey(deliveryContextFromSession(legacyMain));
  if (!legacyRouteKey) {
    return undefined;
  }
  const activeDirectRouteKey = deliveryContextKey(
    normalizeDeliveryContext({
      channel: params.ctx.OriginatingChannel as string | undefined,
      to: params.ctx.OriginatingTo || params.ctx.To,
      accountId: params.ctx.AccountId,
      threadId: params.ctx.MessageThreadId,
    }),
  );
  if (!activeDirectRouteKey || activeDirectRouteKey !== legacyRouteKey) {
    return undefined;
  }
  if (
    legacyMain.deliveryContext === undefined &&
    legacyMain.lastChannel === undefined &&
    legacyMain.lastTo === undefined &&
    legacyMain.lastAccountId === undefined &&
    legacyMain.lastThreadId === undefined
  ) {
    return undefined;
  }
  return {
    key: canonicalMainSessionKey,
    entry: {
      ...legacyMain,
      deliveryContext: undefined,
      lastChannel: undefined,
      lastTo: undefined,
      lastAccountId: undefined,
      lastThreadId: undefined,
    },
  };
}

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.