Files
openclaw/src/gateway/session-model-patch-origin.ts
Peter Steinberger e905a682f7 feat(agents): session self-service tools
New grouped sessions tool (patch: label/pin/archive/model/thinking; group
CRUD; owner-gated; no reset/delete/compact). sessions_spawn gains visible +
worktree via sessions.create with admission reservation and abort-confirmed
rollback; a started run with no run id is aborted and deleted rather than
left orphaned. subagents tool reads the unified task_runs ledger tree-scoped
(subagent/acp/media/cron) and cancels detached runs, enforcing the
controlScope gate for leaf callers. session_status gains a cost line.
Agent-origin model patches record a fallback marker; runs failing on
auth/billing/model_not_found revert to the last working model (transient
errors never revert), and an independent thinkingLevel change realigns the
marker so revert cannot clobber it.

Refs #107237
2026-07-14 18:12:22 -07:00

62 lines
2.1 KiB
TypeScript

import { AsyncLocalStorage } from "node:async_hooks";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "@openclaw/normalization-core/string-coerce";
import { resolveProviderIdForAuth } from "../agents/provider-auth-aliases.js";
import { resolveSessionModelRef } from "../agents/session-model-ref.js";
import type { SessionEntry } from "../config/sessions.js";
import { createAgentPatchedSessionModelFallback } from "../config/sessions/session-model-fallback.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
const agentSessionModelPatch = new AsyncLocalStorage<boolean>();
export function withAgentSessionModelPatchOrigin<T>(run: () => T): T {
return agentSessionModelPatch.run(true, run);
}
export function isAgentSessionModelPatchOrigin(): boolean {
return agentSessionModelPatch.getStore() === true;
}
export function shouldPreserveSessionAuthProfileOverride(params: {
cfg: OpenClawConfig;
entry: SessionEntry;
currentProvider: string;
provider: string;
}): boolean {
const profileOverride = normalizeOptionalString(params.entry.authProfileOverride);
const provider = normalizeOptionalLowercaseString(params.provider);
if (!profileOverride || !provider) {
return false;
}
const resolvesToTargetProvider = (rawProvider: string | undefined): boolean => {
const candidate = normalizeOptionalLowercaseString(rawProvider);
return Boolean(
candidate &&
resolveProviderIdForAuth(candidate, { config: params.cfg }) ===
resolveProviderIdForAuth(provider, { config: params.cfg }),
);
};
const delimiterIndex = profileOverride.indexOf(":");
if (delimiterIndex < 0) {
return resolvesToTargetProvider(params.currentProvider);
}
return resolvesToTargetProvider(profileOverride.slice(0, delimiterIndex));
}
export function snapshotAgentModelFallback(
cfg: OpenClawConfig,
entry: SessionEntry,
agentId: string,
now: number,
): NonNullable<SessionEntry["modelFallback"]> {
const prior = resolveSessionModelRef(cfg, entry, agentId);
return createAgentPatchedSessionModelFallback({
model: prior.model,
provider: prior.provider,
entry,
ts: now,
});
}