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

Quelle  registry.ts

  Sprache: JAVA
 

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

import type { OpenClawConfig } from "../config/types.js";
import { defaultSlotIdForKey } from "../plugins/slots.js";
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
import { sanitizeForLog } from "../terminal/ansi.js";
import type { ContextEngine } from "./types.js";

/**
 * A factory that creates a ContextEngine instance.
 * Supports async creation for engines that need DB connections etc.
 */
export type ContextEngineFactory = () => ContextEngine | Promise<ContextEngine>;
export type ContextEngineRegistrationResult = { ok: true } | { ok: false; existingOwner: string };

type RegisterContextEngineForOwnerOptions = {
  allowSameOwnerRefresh?: boolean;
};

const LEGACY_SESSION_KEY_COMPAT = Symbol.for("openclaw.contextEngine.sessionKeyCompat");
const SESSION_KEY_COMPAT_METHODS = [
  "bootstrap",
  "maintain",
  "ingest",
  "ingestBatch",
  "afterTurn",
  "assemble",
  "compact",
] as const;
const LEGACY_COMPAT_PARAMS = ["sessionKey", "prompt"] as const;
const LEGACY_COMPAT_METHOD_KEYS = {
  bootstrap: ["sessionKey"],
  maintain: ["sessionKey"],
  ingest: ["sessionKey"],
  ingestBatch: ["sessionKey"],
  afterTurn: ["sessionKey"],
  assemble: ["sessionKey", "prompt"],
  compact: ["sessionKey"],
} as const;

type SessionKeyCompatMethodName = (typeof SESSION_KEY_COMPAT_METHODS)[number];
type SessionKeyCompatParams = {
  sessionKey?: string;
  prompt?: string;
};
type LegacyCompatKey = (typeof LEGACY_COMPAT_PARAMS)[number];
type LegacyCompatParamMap = Partial<Record<LegacyCompatKey, unknown>>;

function isSessionKeyCompatMethodName(value: PropertyKey): value is SessionKeyCompatMethodName {
  return (
    typeof value === "string" && (SESSION_KEY_COMPAT_METHODS as readonly string[]).includes(value)
  );
}

function hasOwnLegacyCompatKey<K extends LegacyCompatKey>(
  params: unknown,
  key: K,
): params is SessionKeyCompatParams & Required<Pick<LegacyCompatParamMap, K>> {
  return (
    params !== null &&
    typeof params === "object" &&
    Object.prototype.hasOwnProperty.call(params, key)
  );
}

function withoutLegacyCompatKeys<T extends SessionKeyCompatParams>(
  params: T,
  keys: Iterable<LegacyCompatKey>,
): T {
  const legacyParams = { ...params };
  for (const key of keys) {
    delete legacyParams[key];
  }
  return legacyParams;
}

function issueRejectsLegacyCompatKeyStrictly(issue: unknown, key: LegacyCompatKey): boolean {
  if (!issue || typeof issue !== "object") {
    return false;
  }

  const issueRecord = issue as {
    code?: unknown;
    keys?: unknown;
    message?: unknown;
  };
  if (
    issueRecord.code === "unrecognized_keys" &&
    Array.isArray(issueRecord.keys) &&
    issueRecord.keys.some((issueKey) => issueKey === key)
  ) {
    return true;
  }

  return isLegacyCompatErrorForKey(issueRecord.message, key);
}

function* iterateErrorChain(error: unknown) {
  let current = error;
  const seen = new Set<unknown>();
  while (current !== undefined && current !== null && !seen.has(current)) {
    yield current;
    seen.add(current);
    if (typeof current !== "object") {
      break;
    }
    current = (current as { cause?: unknown }).cause;
  }
}

const LEGACY_UNKNOWN_FIELD_PATTERNS: Record<LegacyCompatKey, readonly RegExp[]> = {
  sessionKey: [
    /\bunrecognized key(?:\(s\)|s)? in object:.*['"`]sessionKey['"`]/i,
    /\badditional propert(?:y|ies)\b.*['"`]sessionKey['"`]/i,
    /\bmust not have additional propert(?:y|ies)\b.*['"`]sessionKey['"`]/i,
    /\b(?:unexpected|extraneous)\s+(?:property|properties|field|fields|key|keys)\b.*['"`]sessionKey['"`]/i,
    /\b(?:unknown|invalid)\s+(?:property|properties|field|fields|key|keys)\b.*['"`]sessionKey['"`]/i,
    /['"`]sessionKey['"`].*\b(?:was|is)\s+not allowed\b/i,
    /"code"\s*:\s*"unrecognized_keys"[^]*"sessionKey"/i,
  ],
  prompt: [
    /\bunrecognized key(?:\(s\)|s)? in object:.*['"`]prompt['"`]/i,
    /\badditional propert(?:y|ies)\b.*['"`]prompt['"`]/i,
    /\bmust not have additional propert(?:y|ies)\b.*['"`]prompt['"`]/i,
    /\b(?:unexpected|extraneous)\s+(?:property|properties|field|fields|key|keys)\b.*['"`]prompt['"`]/i,
    /\b(?:unknown|invalid)\s+(?:property|properties|field|fields|key|keys)\b.*['"`]prompt['"`]/i,
    /['"`]prompt['"`].*\b(?:was|is)\s+not allowed\b/i,
    /"code"\s*:\s*"unrecognized_keys"[^]*"prompt"/i,
  ],
} as const;

function isLegacyCompatUnknownFieldValidationMessage(
  message: string,
  key: LegacyCompatKey,
): boolean {
  return LEGACY_UNKNOWN_FIELD_PATTERNS[key].some((pattern) => pattern.test(message));
}

function isLegacyCompatErrorForKey(error: unknown, key: LegacyCompatKey): boolean {
  for (const candidate of iterateErrorChain(error)) {
    if (Array.isArray(candidate)) {
      if (candidate.some((entry) => issueRejectsLegacyCompatKeyStrictly(entry, key))) {
        return true;
      }
      continue;
    }

    if (typeof candidate === "string") {
      if (isLegacyCompatUnknownFieldValidationMessage(candidate, key)) {
        return true;
      }
      continue;
    }

    if (!candidate || typeof candidate !== "object") {
      continue;
    }

    const issueContainer = candidate as {
      message?: unknown;
      issues?: unknown;
      errors?: unknown;
    };

    if (
      Array.isArray(issueContainer.issues) &&
      issueContainer.issues.some((issue) => issueRejectsLegacyCompatKeyStrictly(issue, key))
    ) {
      return true;
    }

    if (
      Array.isArray(issueContainer.errors) &&
      issueContainer.errors.some((issue) => issueRejectsLegacyCompatKeyStrictly(issue, key))
    ) {
      return true;
    }

    if (
      typeof issueContainer.message === "string" &&
      isLegacyCompatUnknownFieldValidationMessage(issueContainer.message, key)
    ) {
      return true;
    }
  }

  return false;
}

function detectRejectedLegacyCompatKeys(
  error: unknown,
  allowedKeys: readonly LegacyCompatKey[],
): Set<LegacyCompatKey> {
  const rejectedKeys = new Set<LegacyCompatKey>();
  for (const key of allowedKeys) {
    if (isLegacyCompatErrorForKey(error, key)) {
      rejectedKeys.add(key);
    }
  }
  return rejectedKeys;
}

async function invokeWithLegacyCompat<TResult, TParams extends SessionKeyCompatParams>(
  method: (params: TParams) => Promise<TResult> | TResult,
  params: TParams,
  allowedKeys: readonly LegacyCompatKey[],
  opts?: {
    onLegacyModeDetected?: () => void;
    onLegacyKeysDetected?: (keys: Set<LegacyCompatKey>) => void;
    rejectedKeys?: ReadonlySet<LegacyCompatKey>;
  },
): Promise<TResult> {
  const activeRejectedKeys = new Set(opts?.rejectedKeys ?? []);
  const availableKeys = allowedKeys.filter((key) => hasOwnLegacyCompatKey(params, key));
  if (availableKeys.length === 0) {
    return await method(params);
  }

  let currentParams =
    activeRejectedKeys.size > 0 ? withoutLegacyCompatKeys(params, activeRejectedKeys) : params;

  try {
    return await method(currentParams);
  } catch (error) {
    let currentError = error;
    while (true) {
      const rejectedKeys = detectRejectedLegacyCompatKeys(currentError, availableKeys);
      let learnedNewKey = false;
      for (const key of rejectedKeys) {
        if (!activeRejectedKeys.has(key)) {
          activeRejectedKeys.add(key);
          learnedNewKey = true;
        }
      }

      if (!learnedNewKey) {
        throw currentError;
      }

      opts?.onLegacyModeDetected?.();
      opts?.onLegacyKeysDetected?.(rejectedKeys);
      currentParams = withoutLegacyCompatKeys(params, activeRejectedKeys);

      try {
        return await method(currentParams);
      } catch (retryError) {
        currentError = retryError;
      }
    }
  }
}

function wrapContextEngineWithSessionKeyCompat(engine: ContextEngine): ContextEngine {
  const marked = engine as ContextEngine & {
    [LEGACY_SESSION_KEY_COMPAT]?: boolean;
  };
  if (marked[LEGACY_SESSION_KEY_COMPAT]) {
    return engine;
  }

  let isLegacy = false;
  const rejectedKeys = new Set<LegacyCompatKey>();
  const proxy: ContextEngine = new Proxy(engine, {
    get(target, property, receiver) {
      if (property === LEGACY_SESSION_KEY_COMPAT) {
        return true;
      }

      const value = Reflect.get(target, property, receiver);
      if (typeof value !== "function") {
        return value;
      }

      if (!isSessionKeyCompatMethodName(property)) {
        return value.bind(target);
      }

      return (params: SessionKeyCompatParams) => {
        const method = value.bind(target) as (params: SessionKeyCompatParams) => unknown;
        const allowedKeys = LEGACY_COMPAT_METHOD_KEYS[property];
        if (
          isLegacy &&
          allowedKeys.some((key) => rejectedKeys.has(key) && hasOwnLegacyCompatKey(params, key))
        ) {
          return method(withoutLegacyCompatKeys(params, rejectedKeys));
        }
        return invokeWithLegacyCompat(method, params, allowedKeys, {
          onLegacyModeDetected: () => {
            isLegacy = true;
          },
          onLegacyKeysDetected: (keys) => {
            for (const key of keys) {
              rejectedKeys.add(key);
            }
          },
          rejectedKeys,
        });
      };
    },
  });
  return proxy;
}

// ---------------------------------------------------------------------------
// Registry (module-level singleton)
// ---------------------------------------------------------------------------

const CONTEXT_ENGINE_REGISTRY_STATE = Symbol.for("openclaw.contextEngineRegistryState");
const CORE_CONTEXT_ENGINE_OWNER = "core";
const PUBLIC_CONTEXT_ENGINE_OWNER = "public-sdk";

type ContextEngineRegistryState = {
  engines: Map<
    string,
    {
      factory: ContextEngineFactory;
      owner: string;
    }
  >;
};

// Keep context-engine registrations process-global so duplicated dist chunks
// still share one registry map at runtime.
const contextEngineRegistryState = resolveGlobalSingleton<ContextEngineRegistryState>(
  CONTEXT_ENGINE_REGISTRY_STATE,
  () => ({
    engines: new Map(),
  }),
);

function getContextEngineRegistryState(): ContextEngineRegistryState {
  return contextEngineRegistryState;
}

function requireContextEngineOwner(owner: string): string {
  const normalizedOwner = owner.trim();
  if (!normalizedOwner) {
    throw new Error(
      `registerContextEngineForOwner: owner must be a non-empty string, got ${JSON.stringify(owner)}`,
    );
  }
  return normalizedOwner;
}

/**
 * Register a context engine implementation under an explicit trusted owner.
 */
export function registerContextEngineForOwner(
  id: string,
  factory: ContextEngineFactory,
  owner: string,
  opts?: RegisterContextEngineForOwnerOptions,
): ContextEngineRegistrationResult {
  const normalizedOwner = requireContextEngineOwner(owner);
  const registry = getContextEngineRegistryState().engines;
  const existing = registry.get(id);
  if (
    id === defaultSlotIdForKey("contextEngine") &&
    normalizedOwner !== CORE_CONTEXT_ENGINE_OWNER
  ) {
    return { ok: false, existingOwner: CORE_CONTEXT_ENGINE_OWNER };
  }
  if (existing && existing.owner !== normalizedOwner) {
    return { ok: false, existingOwner: existing.owner };
  }
  if (existing && opts?.allowSameOwnerRefresh !== true) {
    return { ok: false, existingOwner: existing.owner };
  }
  registry.set(id, { factory, owner: normalizedOwner });
  return { ok: true };
}

/**
 * Public SDK entry point for third-party registrations.
 *
 * This path is intentionally unprivileged: it cannot claim core-owned ids and
 * it cannot safely refresh an existing registration because the caller's
 * identity is not authenticated.
 */
export function registerContextEngine(
  id: string,
  factory: ContextEngineFactory,
): ContextEngineRegistrationResult {
  return registerContextEngineForOwner(id, factory, PUBLIC_CONTEXT_ENGINE_OWNER);
}

/**
 * Return the factory for a registered engine, or undefined.
 */
export function getContextEngineFactory(id: string): ContextEngineFactory | undefined {
  return getContextEngineRegistryState().engines.get(id)?.factory;
}

/**
 * List all registered engine ids.
 */
export function listContextEngineIds(): string[] {
  return [...getContextEngineRegistryState().engines.keys()];
}

export function clearContextEnginesForOwner(owner: string): void {
  const normalizedOwner = requireContextEngineOwner(owner);
  const registry = getContextEngineRegistryState().engines;
  for (const [id, entry] of registry.entries()) {
    if (entry.owner === normalizedOwner) {
      registry.delete(id);
    }
  }
}

function describeResolvedContextEngineContractError(
  engineId: string,
  engine: unknown,
): string | null {
  if (!engine || typeof engine !== "object") {
    return `Context engine "${engineId}" factory returned ${JSON.stringify(engine)} instead of a ContextEngine object.`;
  }

  const candidate = engine as Record<string, unknown>;
  const issues: string[] = [];
  const info = candidate.info;
  if (!info || typeof info !== "object") {
    issues.push("missing info");
  } else {
    const infoRecord = info as Record<string, unknown>;
    // Engines own their internal info.id; it is metadata, not a handle into the
    // registry. The registered id (plugin slot id) and the engine's own id are
    // allowed to differ, so we only require that info.id is a non-empty string
    // for display/logging purposes and do not enforce equality with engineId.
    const infoId = typeof infoRecord.id === "string" ? infoRecord.id.trim() : "";
    if (!infoId) {
      issues.push("missing info.id");
    }
    if (typeof infoRecord.name !== "string" || !infoRecord.name.trim()) {
      issues.push("missing info.name");
    }
  }

  if (typeof candidate.ingest !== "function") {
    issues.push("missing ingest()");
  }
  if (typeof candidate.assemble !== "function") {
    issues.push("missing assemble()");
  }
  if (typeof candidate.compact !== "function") {
    issues.push("missing compact()");
  }

  if (issues.length === 0) {
    return null;
  }

  return `Context engine "${engineId}" factory returned an invalid ContextEngine: ${issues.join(", ")}.`;
}

// ---------------------------------------------------------------------------
// Resolution
// ---------------------------------------------------------------------------

/**
 * Resolve which ContextEngine to use based on plugin slot configuration.
 *
 * Resolution order:
 *   1. `config.plugins.slots.contextEngine` (explicit slot override)
 *   2. Default slot value ("legacy")
 *
 * Non-default engines that fail (unregistered, factory throw, or contract
 * violation) are logged and silently replaced by the default engine.
 * Throws only when the default engine itself cannot be resolved.
 */
export async function resolveContextEngine(config?: OpenClawConfig): Promise<ContextEngine> {
  const slotValue = config?.plugins?.slots?.contextEngine;
  const engineId =
    typeof slotValue === "string" && slotValue.trim()
      ? slotValue.trim()
      : defaultSlotIdForKey("contextEngine");

  const defaultEngineId = defaultSlotIdForKey("contextEngine");
  const isDefaultEngine = engineId === defaultEngineId;

  const entry = getContextEngineRegistryState().engines.get(engineId);
  if (!entry) {
    if (isDefaultEngine) {
      throw new Error(
        `Context engine "${engineId}" is not registered. ` +
          `Available engines: ${listContextEngineIds().join(", ") || "(none)"}`,
      );
    }
    console.error(
      `[context-engine] Context engine "${sanitizeForLog(engineId)}" is not registered; ` +
        `falling back to default engine "${defaultEngineId}".`,
    );
    return resolveDefaultContextEngine(defaultEngineId);
  }

  let engine: ContextEngine;
  try {
    engine = await entry.factory();
  } catch (factoryError) {
    if (isDefaultEngine) {
      throw factoryError;
    }
    console.error(
      `[context-engine] Context engine "${sanitizeForLog(engineId)}" factory threw during resolution: ` +
        `${sanitizeForLog(factoryError instanceof Error ? factoryError.message : String(factoryError))}; ` +
        `falling back to default engine "${defaultEngineId}".`,
    );
    return resolveDefaultContextEngine(defaultEngineId);
  }

  let contractError: string | null;
  try {
    contractError = describeResolvedContextEngineContractError(engineId, engine);
  } catch (validationError) {
    if (isDefaultEngine) {
      throw validationError;
    }
    console.error(
      `[context-engine] Context engine "${sanitizeForLog(engineId)}" contract validation threw: ` +
        `${sanitizeForLog(validationError instanceof Error ? validationError.message : String(validationError))}; ` +
        `falling back to default engine "${defaultEngineId}".`,
    );
    return resolveDefaultContextEngine(defaultEngineId);
  }
  if (contractError) {
    if (isDefaultEngine) {
      throw new Error(contractError);
    }
    // contractError includes engineId from plugin config; sanitizeForLog covers it
    console.error(
      `[context-engine] ${sanitizeForLog(contractError)}; falling back to default engine "${defaultEngineId}".`,
    );
    return resolveDefaultContextEngine(defaultEngineId);
  }

  return wrapContextEngineWithSessionKeyCompat(engine);
}

/**
 * Resolve the default context engine as a last-resort fallback.
 *
 * This helper is intentionally strict: if the default engine itself fails,
 * there is no further fallback and the error must propagate.
 */
async function resolveDefaultContextEngine(defaultEngineId: string): Promise<ContextEngine> {
  const defaultEntry = getContextEngineRegistryState().engines.get(defaultEngineId);
  if (!defaultEntry) {
    throw new Error(
      `[context-engine] fallback failed: default engine "${defaultEngineId}" is not registered. ` +
        `Available engines: ${listContextEngineIds().join(", ") || "(none)"}`,
    );
  }
  const engine = await defaultEntry.factory();
  const contractError = describeResolvedContextEngineContractError(defaultEngineId, engine);
  if (contractError) {
    throw new Error(`[context-engine] ${contractError}`);
  }
  return wrapContextEngineWithSessionKeyCompat(engine);
}

¤ Dauer der Verarbeitung: 0.23 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.