Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  sessions.ts

  Sprache: JAVA
 

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

import { toNumber } from "../format.ts";
import type { GatewayBrowserClient } from "../gateway.ts";
import type {
  GatewaySessionRow,
  SessionCompactionCheckpoint,
  SessionsCompactionBranchResult,
  SessionsCompactionListResult,
  SessionsCompactionRestoreResult,
  SessionsListResult,
} from "../types.ts";
import {
  formatMissingOperatorReadScopeMessage,
  isMissingOperatorReadScopeError,
} from "./scope-errors.ts";

export type SessionsState = {
  client: GatewayBrowserClient | null;
  connected: boolean;
  sessionsLoading: boolean;
  sessionsResult: SessionsListResult | null;
  sessionsError: string | null;
  sessionsFilterActive: string;
  sessionsFilterLimit: string;
  sessionsIncludeGlobal: boolean;
  sessionsIncludeUnknown: boolean;
  sessionsExpandedCheckpointKey: string | null;
  sessionsCheckpointItemsByKey: Record<string, SessionCompactionCheckpoint[]>;
  sessionsCheckpointLoadingKey: string | null;
  sessionsCheckpointBusyKey: string | null;
  sessionsCheckpointErrorByKey: Record<string, string>;
};

type LoadSessionsOverrides = {
  activeMinutes?: number;
  limit?: number;
  includeGlobal?: boolean;
  includeUnknown?: boolean;
};

type SessionsLoadControl = {
  loading: boolean;
  pending: { overrides?: LoadSessionsOverrides } | null;
  ownsStateLoading: boolean;
};

const sessionsLoadControls = new WeakMap<object, SessionsLoadControl>();

const SESSION_EVENT_ROW_FIELDS = [
  "abortedLastRun",
  "childSessions",
  "compactionCheckpointCount",
  "contextTokens",
  "displayName",
  "endedAt",
  "elevatedLevel",
  "fastMode",
  "inputTokens",
  "kind",
  "label",
  "latestCompactionCheckpoint",
  "model",
  "modelProvider",
  "outputTokens",
  "reasoningLevel",
  "runtimeMs",
  "sessionId",
  "spawnedBy",
  "startedAt",
  "status",
  "subject",
  "surface",
  "systemSent",
  "thinkingDefault",
  "thinkingLevel",
  "thinkingOptions",
  "totalTokens",
  "totalTokensFresh",
  "updatedAt",
  "verboseLevel",
] as const satisfies readonly (keyof GatewaySessionRow)[];

function getSessionsLoadControl(state: SessionsState): SessionsLoadControl {
  const key = state as object;
  let control = sessionsLoadControls.get(key);
  if (!control) {
    control = { loading: false, ownsStateLoading: false, pending: null };
    sessionsLoadControls.set(key, control);
  }
  return control;
}

function takePendingSessionsLoad(
  control: SessionsLoadControl,
): { overrides?: LoadSessionsOverrides } | null {
  const pending = control.pending;
  control.pending = null;
  return pending;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value && typeof value === "object");
}

function hasOwn(record: Record<string, unknown>, key: string): boolean {
  return Object.prototype.hasOwnProperty.call(record, key);
}

function normalizeSessionKind(value: unknown): GatewaySessionRow["kind"] | undefined {
  return value === "direct" || value === "group" || value === "global" || value === "unknown"
    ? value
    : undefined;
}

function checkpointSummarySignature(
  row:
    | {
        compactionCheckpointCount?: number;
        latestCompactionCheckpoint?: { checkpointId?: string; createdAt?: number } | null;
      }
    | undefined,
): string {
  return `${row?.compactionCheckpointCount ?? 0}:${
    row?.latestCompactionCheckpoint?.checkpointId ?? ""
  }:${row?.latestCompactionCheckpoint?.createdAt ?? 0}`;
}

function invalidateCheckpointCacheForKey(state: SessionsState, key: string) {
  if (
    !(key in state.sessionsCheckpointItemsByKey) &&
    !(key in state.sessionsCheckpointErrorByKey)
  ) {
    return;
  }
  const nextItems = { ...state.sessionsCheckpointItemsByKey };
  const nextErrors = { ...state.sessionsCheckpointErrorByKey };
  delete nextItems[key];
  delete nextErrors[key];
  state.sessionsCheckpointItemsByKey = nextItems;
  state.sessionsCheckpointErrorByKey = nextErrors;
}

async function fetchSessionCompactionCheckpoints(state: SessionsState, key: string) {
  state.sessionsCheckpointLoadingKey = key;
  state.sessionsCheckpointErrorByKey = {
    ...state.sessionsCheckpointErrorByKey,
    [key]: "",
  };
  try {
    const result = await state.client?.request<SessionsCompactionListResult>(
      "sessions.compaction.list",
      { key },
    );
    if (result) {
      state.sessionsCheckpointItemsByKey = {
        ...state.sessionsCheckpointItemsByKey,
        [key]: result.checkpoints ?? [],
      };
    }
  } catch (err) {
    state.sessionsCheckpointErrorByKey = {
      ...state.sessionsCheckpointErrorByKey,
      [key]: String(err),
    };
  } finally {
    if (state.sessionsCheckpointLoadingKey === key) {
      state.sessionsCheckpointLoadingKey = null;
    }
  }
}

async function withSessionsLoading(
  state: SessionsState,
  run: () => Promise<void>,
): Promise<boolean> {
  if (state.sessionsLoading) {
    return false;
  }
  const control = getSessionsLoadControl(state);
  state.sessionsLoading = true;
  state.sessionsError = null;
  let drainedPendingRefresh = false;
  try {
    await run();
  } finally {
    state.sessionsLoading = false;
    const pending = takePendingSessionsLoad(control);
    if (pending && state.client && state.connected) {
      await loadSessions(state, pending.overrides);
      drainedPendingRefresh = true;
    }
  }
  return drainedPendingRefresh;
}

async function runCompactionMutation<T>(
  state: SessionsState,
  key: string,
  checkpointId: string,
  method: "sessions.compaction.branch" | "sessions.compaction.restore",
  confirmMessage: string,
): Promise<T | null> {
  if (!state.client || !state.connected || !window.confirm(confirmMessage)) {
    return null;
  }
  const client = state.client;
  state.sessionsCheckpointBusyKey = checkpointId;
  try {
    const result = await client.request<T>(method, { key, checkpointId });
    await loadSessions(state);
    return result;
  } catch (err) {
    state.sessionsError = String(err);
    return null;
  } finally {
    if (state.sessionsCheckpointBusyKey === checkpointId) {
      state.sessionsCheckpointBusyKey = null;
    }
  }
}

export function applySessionsChangedEvent(state: SessionsState, payload: unknown): boolean {
  if (!isRecord(payload) || !state.sessionsResult) {
    return false;
  }
  const eventSession = isRecord(payload.session) ? payload.session : null;
  const source = eventSession ?? payload;
  const key =
    (typeof source.key === "string" && source.key.trim()) ||
    (typeof payload.sessionKey === "string" && payload.sessionKey.trim()) ||
    (typeof payload.key === "string" && payload.key.trim()) ||
    "";
  if (!key) {
    return false;
  }

  const previousRows = state.sessionsResult.sessions;
  const existingIndex = previousRows.findIndex((row) => row.key === key);
  const existing = existingIndex >= 0 ? previousRows[existingIndex] : undefined;
  const previousCheckpointSignature = checkpointSummarySignature(existing);
  const fallbackKind = normalizeSessionKind(source.kind) ?? existing?.kind ?? "unknown";
  const nextRow: GatewaySessionRow = {
    ...(existing ?? { key, kind: fallbackKind, updatedAt: null }),
    key,
    kind: fallbackKind,
  };
  const mutableNext = nextRow as unknown as Record<string, unknown>;
  for (const field of SESSION_EVENT_ROW_FIELDS) {
    if (!hasOwn(source, field)) {
      continue;
    }
    const value = source[field];
    if (value === undefined) {
      delete mutableNext[field];
    } else {
      mutableNext[field] = value;
    }
  }
  if (nextRow.totalTokensFresh === false && !hasOwn(source, "totalTokens")) {
    delete nextRow.totalTokens;
  }

  const sessions =
    existingIndex >= 0
      ? previousRows.map((row, index) => (index === existingIndex ? nextRow : row))
      : [nextRow, ...previousRows];
  const eventTs = typeof payload.ts === "number" && Number.isFinite(payload.ts) ? payload.ts : null;
  state.sessionsResult = {
    ...state.sessionsResult,
    ts: eventTs == null ? state.sessionsResult.ts : Math.max(state.sessionsResult.ts, eventTs),
    count: existingIndex >= 0 ? state.sessionsResult.count : state.sessionsResult.count + 1,
    sessions,
  };

  if (previousCheckpointSignature !== checkpointSummarySignature(nextRow)) {
    invalidateCheckpointCacheForKey(state, key);
  }
  return true;
}

export async function subscribeSessions(state: SessionsState) {
  if (!state.client || !state.connected) {
    return;
  }
  try {
    await state.client.request("sessions.subscribe", {});
  } catch (err) {
    state.sessionsError = String(err);
  }
}

export async function loadSessions(state: SessionsState, overrides?: LoadSessionsOverrides) {
  if (!state.client || !state.connected) {
    return;
  }
  const control = getSessionsLoadControl(state);
  if (control.loading) {
    control.pending = { overrides };
    return;
  }
  if (state.sessionsLoading) {
    control.pending = { overrides };
    return;
  }
  const client = state.client;
  control.loading = true;
  control.ownsStateLoading = true;
  state.sessionsLoading = true;
  state.sessionsError = null;
  let currentOverrides: LoadSessionsOverrides | undefined = overrides;
  try {
    for (;;) {
      control.pending = null;
      await loadSessionsOnce(state, client, currentOverrides);
      const pending = takePendingSessionsLoad(control);
      if (!pending || !state.client || !state.connected) {
        break;
      }
      currentOverrides = pending.overrides;
    }
  } finally {
    control.loading = false;
    control.pending = null;
    if (control.ownsStateLoading) {
      state.sessionsLoading = false;
      control.ownsStateLoading = false;
    }
  }
}

async function loadSessionsOnce(
  state: SessionsState,
  client: NonNullable<SessionsState["client"]>,
  overrides?: LoadSessionsOverrides,
) {
  await (async () => {
    const previousRows = new Map(
      (state.sessionsResult?.sessions ?? []).map((row) => [row.key, row] as const),
    );
    const includeGlobal = overrides?.includeGlobal ?? state.sessionsIncludeGlobal;
    const includeUnknown = overrides?.includeUnknown ?? state.sessionsIncludeUnknown;
    const activeMinutes = overrides?.activeMinutes ?? toNumber(state.sessionsFilterActive, 0);
    const limit = overrides?.limit ?? toNumber(state.sessionsFilterLimit, 0);
    const params: Record<string, unknown> = {
      includeGlobal,
      includeUnknown,
    };
    if (activeMinutes > 0) {
      params.activeMinutes = activeMinutes;
    }
    if (limit > 0) {
      params.limit = limit;
    }
    const res = await client.request<SessionsListResult | undefined>("sessions.list", params);
    if (res) {
      state.sessionsResult = res;
      const nextKeys = new Set(res.sessions.map((row) => row.key));
      for (const key of Object.keys(state.sessionsCheckpointItemsByKey)) {
        if (!nextKeys.has(key)) {
          invalidateCheckpointCacheForKey(state, key);
        }
      }
      let expandedNeedsRefetch = false;
      for (const row of res.sessions) {
        const previous = previousRows.get(row.key);
        if (checkpointSummarySignature(previous) !== checkpointSummarySignature(row)) {
          invalidateCheckpointCacheForKey(state, row.key);
          if (state.sessionsExpandedCheckpointKey === row.key) {
            expandedNeedsRefetch = true;
          }
        }
      }
      const expandedKey = state.sessionsExpandedCheckpointKey;
      if (
        expandedKey &&
        nextKeys.has(expandedKey) &&
        (expandedNeedsRefetch || !state.sessionsCheckpointItemsByKey[expandedKey])
      ) {
        await fetchSessionCompactionCheckpoints(state, expandedKey);
      }
    }
  })().catch((err: unknown) => {
    if (!isMissingOperatorReadScopeError(err)) {
      state.sessionsError = String(err);
      return;
    }
    state.sessionsResult = null;
    state.sessionsError = formatMissingOperatorReadScopeMessage("sessions");
  });
}

export async function patchSession(
  state: SessionsState,
  key: string,
  patch: {
    label?: string | null;
    thinkingLevel?: string | null;
    fastMode?: boolean | null;
    verboseLevel?: string | null;
    reasoningLevel?: string | null;
  },
) {
  if (!state.client || !state.connected) {
    return;
  }
  const params: Record<string, unknown> = { key };
  for (const field of [
    "label",
    "thinkingLevel",
    "fastMode",
    "verboseLevel",
    "reasoningLevel",
  ] as const) {
    if (field in patch) {
      params[field] = patch[field];
    }
  }
  try {
    await state.client.request("sessions.patch", params);
    await loadSessions(state);
  } catch (err) {
    state.sessionsError = String(err);
  }
}

export async function deleteSessionsAndRefresh(
  state: SessionsState,
  keys: string[],
): Promise<string[]> {
  if (!state.client || !state.connected || keys.length === 0) {
    return [];
  }
  const client = state.client;
  if (state.sessionsLoading) {
    return [];
  }
  const confirmed = window.confirm(
    `Delete ${keys.length} ${keys.length === 1 ? "session" : "sessions"}?\n\nThis will delete the session entries and archive their transcripts.`,
  );
  if (!confirmed) {
    return [];
  }
  const deleted: string[] = [];
  const deleteErrors: string[] = [];
  const refreshedDuringDelete = await withSessionsLoading(state, async () => {
    for (const key of keys) {
      try {
        await client.request("sessions.delete", { key, deleteTranscript: true });
        deleted.push(key);
      } catch (err) {
        deleteErrors.push(String(err));
      }
    }
  });
  if (deleted.length > 0 && !refreshedDuringDelete) {
    await loadSessions(state);
  }
  if (deleteErrors.length > 0) {
    state.sessionsError = deleteErrors.join("; ");
  }
  return deleted;
}

export async function toggleSessionCompactionCheckpoints(state: SessionsState, key: string) {
  const trimmedKey = key.trim();
  if (!trimmedKey) {
    return;
  }
  if (state.sessionsExpandedCheckpointKey === trimmedKey) {
    state.sessionsExpandedCheckpointKey = null;
    return;
  }
  state.sessionsExpandedCheckpointKey = trimmedKey;
  if (state.sessionsCheckpointItemsByKey[trimmedKey]) {
    return;
  }
  await fetchSessionCompactionCheckpoints(state, trimmedKey);
}

export async function branchSessionFromCheckpoint(
  state: SessionsState,
  key: string,
  checkpointId: string,
): Promise<string | null> {
  const result = await runCompactionMutation<SessionsCompactionBranchResult>(
    state,
    key,
    checkpointId,
    "sessions.compaction.branch",
    "Create a new child session from this pre-compaction checkpoint?",
  );
  return result?.key ?? null;
}

export async function restoreSessionFromCheckpoint(
  state: SessionsState,
  key: string,
  checkpointId: string,
) {
  await runCompactionMutation<SessionsCompactionRestoreResult>(
    state,
    key,
    checkpointId,
    "sessions.compaction.restore",
    "Restore this session to the selected pre-compaction checkpoint?\n\nThis replaces the current active transcript for the session key.",
  );
}

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






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge