openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
103 lines (102 loc) • 4.45 kB
JavaScript
import { o as resolveFreshSessionTotalTokens } from "./types-BGDhC4_W.mjs";
import crypto from "node:crypto";
//#region src/config/sessions/goals-transitions.ts
var SessionGoalTransitionError = class extends Error {
constructor(..._args) {
super(..._args);
this.name = "SessionGoalTransitionError";
}
};
function normalizeTokenCount(value) {
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
}
function resolveEntryFreshTotalTokens(entry) {
return normalizeTokenCount(resolveFreshSessionTotalTokens(entry));
}
function normalizeTokenBudget(value) {
const normalized = normalizeTokenCount(value);
return normalized && normalized > 0 ? normalized : void 0;
}
function accountSessionGoalUsage(entry, now, options) {
const goal = entry.goal;
if (!goal) return;
const totalTokens = resolveEntryFreshTotalTokens(entry);
const hasFreshStart = goal.tokenStartFresh !== false;
const shouldHoldStaleStart = !hasFreshStart && options?.adoptFreshBaseline === false;
const shouldAdoptFreshStart = !shouldHoldStaleStart && totalTokens !== void 0 && !hasFreshStart;
const tokenStart = shouldAdoptFreshStart ? totalTokens : normalizeTokenCount(goal.tokenStart) ?? totalTokens ?? 0;
const tokensUsed = totalTokens === void 0 || shouldAdoptFreshStart || shouldHoldStaleStart ? goal.tokensUsed : Math.max(goal.tokensUsed, Math.max(0, totalTokens - tokenStart));
const next = {
...goal,
tokenStart,
tokenStartFresh: hasFreshStart || shouldAdoptFreshStart,
tokensUsed
};
if (next.status === "active" && next.tokenBudget !== void 0 && tokensUsed >= next.tokenBudget) {
next.status = "budget_limited";
next.budgetLimitedAt = now;
next.updatedAt = now;
}
return next;
}
function buildCreatedSessionGoal(entry, options, now) {
const objective = options.objective;
if (!objective.trim()) throw new SessionGoalTransitionError("objective required");
if (entry.goal) throw new SessionGoalTransitionError("goal already exists");
const tokenBudget = normalizeTokenBudget(options.tokenBudget);
const tokenStart = resolveEntryFreshTotalTokens(entry);
return {
schemaVersion: 1,
id: crypto.randomUUID(),
objective,
status: "active",
createdAt: now,
updatedAt: now,
tokenStart: tokenStart ?? 0,
tokenStartFresh: tokenStart !== void 0,
tokensUsed: 0,
...tokenBudget ? { tokenBudget } : {},
continuationTurns: 0
};
}
function buildUpdatedSessionGoalStatus(entry, options, now) {
const accounted = accountSessionGoalUsage(entry, now);
if (!accounted) throw new SessionGoalTransitionError("goal not found");
if (accounted.status === "complete" && accounted.status !== options.status) throw new SessionGoalTransitionError(`goal is already ${accounted.status}`);
const resetsBudgetWindow = options.status === "active" && (accounted.status === "budget_limited" || accounted.status === "usage_limited" || accounted.tokenBudget !== void 0 && accounted.tokensUsed >= accounted.tokenBudget);
const freshTokenStart = resetsBudgetWindow ? resolveEntryFreshTotalTokens(entry) : void 0;
const next = {
...accounted,
status: options.status,
updatedAt: now,
...options.note ? { lastStatusNote: options.note } : {},
...options.status === "paused" ? { pausedAt: now } : {},
...options.status === "blocked" ? { blockedAt: now } : {},
...options.status === "complete" ? { completedAt: accounted.completedAt ?? now } : {}
};
if (resetsBudgetWindow) {
next.tokenStart = freshTokenStart ?? 0;
next.tokenStartFresh = freshTokenStart !== void 0;
next.tokensUsed = 0;
delete next.budgetLimitedAt;
delete next.usageLimitedAt;
}
if (next.status === "active" && next.tokenBudget !== void 0 && next.tokensUsed >= next.tokenBudget) {
next.status = "budget_limited";
next.budgetLimitedAt = now;
}
return next;
}
function buildUpdatedSessionGoalObjective(entry, objective, now) {
if (!objective.trim()) throw new SessionGoalTransitionError("objective required");
const accounted = accountSessionGoalUsage(entry, now);
if (!accounted) throw new SessionGoalTransitionError("goal not found");
if (accounted.status === "complete") throw new SessionGoalTransitionError(`goal is already ${accounted.status}`);
return {
...accounted,
objective,
updatedAt: now
};
}
//#endregion
export { buildUpdatedSessionGoalStatus as a, buildUpdatedSessionGoalObjective as i, accountSessionGoalUsage as n, buildCreatedSessionGoal as r, SessionGoalTransitionError as t };