fix: clarify session runtime metadata

This commit is contained in:
Peter Steinberger
2026-04-29 06:07:03 +01:00
parent 4d729d0aa8
commit 8d78451e8b
12 changed files with 348 additions and 31 deletions

View File

@@ -49,6 +49,7 @@ Docs: https://docs.openclaw.ai
- Agents/model selection: resolve slash-form aliases before provider/model parsing and keep alias-resolved primary models subject to transient provider cooldowns, so cron and persisted sessions do not retry cooled-down raw aliases. Fixes #73573 and #73657. Thanks @akai-shuuichi and @hashslingers.
- Agents/Claude CLI: reuse already-cached macOS Keychain credentials for no-prompt Claude credential reads, so doctor/runtime checks do not miss fresh interactive Claude auth. Fixes #73682. Thanks @RyanSandoval.
- Agents/Claude CLI doctor: scope workspace and project-dir checks to agents that actually use the Claude CLI runtime, so non-default Claude agents no longer make the default agent look Claude-backed. Fixes #73903. Thanks @bobfreeman1989.
- Gateway/sessions: expose effective agent runtime metadata on session rows, `sessions.patch`, and local `openclaw sessions --json`, while keeping Claude CLI-backed rows on the canonical model provider so runtime backend and model identity are no longer conflated. Fixes #73090. Thanks @vishutdhar.
- Agents/runtime status: expose effective agent runtime metadata in `agents.list`, Control UI agent panels, and `/agents`, and avoid rendering stale or cumulative CLI token totals as live context usage. Fixes #73660, #73578, and #45268. Thanks @spartman, @DashLabsDev, and @xyooz.
- Agents/transcripts: strip empty assistant text blocks while preserving valid text, images, and signatures, so Anthropic-style providers no longer reject sanitized transcript turns. Fixes #73640. Thanks @jowhee327.
- Providers/Bedrock: omit deprecated `temperature` for Claude Opus 4.7 Bedrock model ids, named and application inference profiles, including dotted `opus-4.7` refs, and classify the nested validation response for failover. Fixes #73663. Thanks @bstanbury.

View File

@@ -387,7 +387,7 @@ enumeration of `src/gateway/server-methods/*.ts`.
</Accordion>
<Accordion title="Session control">
- `sessions.list` returns the current session index.
- `sessions.list` returns the current session index, including per-row `agentRuntime` metadata when an agent runtime backend is configured.
- `sessions.subscribe` and `sessions.unsubscribe` toggle session change event subscriptions for the current WS client.
- `sessions.messages.subscribe` and `sessions.messages.unsubscribe` toggle transcript/message event subscriptions for one session.
- `sessions.preview` returns bounded transcript previews for specific session keys.
@@ -396,7 +396,7 @@ enumeration of `src/gateway/server-methods/*.ts`.
- `sessions.send` sends a message into an existing session.
- `sessions.steer` is the interrupt-and-steer variant for an active session.
- `sessions.abort` aborts active work for a session.
- `sessions.patch` updates session metadata/overrides.
- `sessions.patch` updates session metadata/overrides and reports the resolved canonical model plus effective `agentRuntime`.
- `sessions.reset`, `sessions.delete`, and `sessions.compact` perform session maintenance.
- `sessions.get` returns the full stored session row.
- Chat execution still uses `chat.history`, `chat.send`, `chat.abort`, and `chat.inject`. `chat.history` is display-normalized for UI clients: inline directive tags are stripped from visible text, plain-text tool-call XML payloads (including `<tool_call>...</tool_call>`, `<function_call>...</function_call>`, `<tool_calls>...</tool_calls>`, `<function_calls>...</function_calls>`, and truncated tool-call blocks) and leaked ASCII/full-width model control tokens are stripped, pure silent-token assistant rows such as exact `NO_REPLY` / `no_reply` are omitted, and oversized rows can be replaced with placeholders.

View File

@@ -1,4 +1,8 @@
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
import {
inferUniqueProviderFromConfiguredModels,
isCliProvider,
} from "../agents/model-selection.js";
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
@@ -14,7 +18,9 @@ type SessionDisplayDefaults = {
model: string;
};
function parseModelRef(raw: string, defaultProvider: string): { provider: string; model: string } {
type SessionDisplayModelRef = { provider: string; model: string };
function parseModelRef(raw: string, defaultProvider: string): SessionDisplayModelRef {
const trimmed = raw.trim();
if (!trimmed) {
return { provider: defaultProvider, model: DEFAULT_MODEL };
@@ -59,10 +65,7 @@ function normalizeStoredOverrideModel(params: {
};
}
function resolveDefaultModelRef(
cfg: OpenClawConfig,
agentId?: string,
): { provider: string; model: string } {
function resolveDefaultModelRef(cfg: OpenClawConfig, agentId?: string): SessionDisplayModelRef {
const primary =
resolveAgentPrimaryModel(cfg, agentId) ??
resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model) ??
@@ -79,10 +82,48 @@ export function resolveSessionDisplayDefaults(
};
}
function normalizeCliRuntimeDisplayRef(
cfg: OpenClawConfig,
ref: SessionDisplayModelRef,
defaultRef: SessionDisplayModelRef,
): SessionDisplayModelRef {
if (!isCliProvider(ref.provider, cfg)) {
return ref;
}
if (ref.model.includes("/")) {
const parsed = parseModelRef(ref.model, defaultRef.provider);
if (!isCliProvider(parsed.provider, cfg)) {
return parsed;
}
}
const inferredProvider = inferUniqueProviderFromConfiguredModels({
cfg,
model: ref.model,
});
if (inferredProvider && !isCliProvider(inferredProvider, cfg)) {
return { provider: inferredProvider, model: ref.model };
}
const parsed = parseModelRef(ref.model, defaultRef.provider);
if (!isCliProvider(parsed.provider, cfg)) {
return parsed;
}
return {
provider: defaultRef.provider || ref.provider,
model: parsed.model || ref.model,
};
}
export function resolveSessionDisplayModel(
cfg: OpenClawConfig,
row: SessionDisplayModelRow,
): string {
return resolveSessionDisplayModelRef(cfg, row).model;
}
export function resolveSessionDisplayModelRef(
cfg: OpenClawConfig,
row: SessionDisplayModelRow,
): SessionDisplayModelRef {
const agentId = row.key.startsWith("agent:") ? row.key.split(":")[1] : undefined;
const defaultRef = resolveDefaultModelRef(cfg, agentId);
const normalizedOverride = normalizeStoredOverrideModel({
@@ -94,10 +135,14 @@ export function resolveSessionDisplayModel(
return parseModelRef(
normalizedOverride.modelOverride,
normalizedOverride.providerOverride ?? defaultRef.provider,
).model;
);
}
if (row.model) {
return parseModelRef(row.model, row.modelProvider ?? defaultRef.provider).model;
return normalizeCliRuntimeDisplayRef(
cfg,
parseModelRef(row.model, row.modelProvider ?? defaultRef.provider),
defaultRef,
);
}
return defaultRef.model;
return defaultRef;
}

View File

@@ -1,5 +1,11 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { mockSessionsConfig, runSessionsJson, writeStore } from "./sessions.test-helpers.js";
import {
mockSessionsConfig,
resetMockSessionsConfig,
runSessionsJson,
setMockSessionsConfig,
writeStore,
} from "./sessions.test-helpers.js";
mockSessionsConfig();
@@ -8,7 +14,9 @@ import { sessionsCommand } from "./sessions.js";
type SessionsJsonPayload = {
sessions?: Array<{
key: string;
modelProvider?: string | null;
model?: string | null;
agentRuntime?: { id: string; fallback?: string; source: string };
}>;
};
@@ -38,6 +46,7 @@ describe("sessionsCommand model resolution", () => {
});
afterEach(() => {
resetMockSessionsConfig();
vi.useRealTimers();
});
@@ -60,4 +69,69 @@ describe("sessionsCommand model resolution", () => {
);
expect(model).toBe("gpt-5.4");
});
it("separates Claude CLI runtime from canonical model provider in JSON output", async () => {
setMockSessionsConfig(() => ({
agents: {
defaults: {
agentRuntime: { id: "claude-cli", fallback: "none" },
model: { primary: "anthropic/claude-opus-4-7" },
models: { "anthropic/claude-opus-4-7": {} },
contextTokens: 200_000,
},
},
}));
const store = writeStore(
{
"agent:main:main": {
sessionId: "main-session",
updatedAt: Date.now() - 60_000,
modelProvider: "claude-cli",
model: "claude-opus-4-7",
},
},
"sessions-claude-runtime",
);
const payload = await runSessionsJson<SessionsJsonPayload>(sessionsCommand, store);
const session = payload.sessions?.find((row) => row.key === "agent:main:main");
expect(session?.modelProvider).toBe("anthropic");
expect(session?.model).toBe("claude-opus-4-7");
expect(session?.agentRuntime).toEqual({
id: "claude-cli",
fallback: "none",
source: "defaults",
});
});
it("infers canonical provider for bare CLI models before default-provider fallback", async () => {
setMockSessionsConfig(() => ({
agents: {
defaults: {
agentRuntime: { id: "claude-cli", fallback: "none" },
model: { primary: "openai/gpt-5.4" },
models: { "anthropic/claude-opus-4-7": {} },
contextTokens: 200_000,
},
},
}));
const store = writeStore(
{
"agent:main:main": {
sessionId: "main-session",
updatedAt: Date.now() - 60_000,
modelProvider: "claude-cli",
model: "claude-opus-4-7",
},
},
"sessions-claude-runtime-openai-default",
);
const payload = await runSessionsJson<SessionsJsonPayload>(sessionsCommand, store);
const session = payload.sessions?.find((row) => row.key === "agent:main:main");
expect(session?.modelProvider).toBe("anthropic");
expect(session?.model).toBe("claude-opus-4-7");
});
});

View File

@@ -5,7 +5,7 @@ import path from "node:path";
import { vi } from "vitest";
import type { RuntimeEnv } from "../runtime.js";
const sessionsConfigState = vi.hoisted(() => ({
const sessionsConfigState = vi.hoisted<{ loadConfig: () => Record<string, unknown> }>(() => ({
loadConfig: () => ({
agents: {
defaults: {
@@ -17,6 +17,8 @@ const sessionsConfigState = vi.hoisted(() => ({
}),
}));
const defaultSessionsConfigLoader = sessionsConfigState.loadConfig;
vi.mock("../config/config.js", () => ({
getRuntimeConfig: () => sessionsConfigState.loadConfig(),
loadConfig: () => sessionsConfigState.loadConfig(),
@@ -28,6 +30,14 @@ export function mockSessionsConfig() {
// warnings before importing `sessions.ts`.
}
export function setMockSessionsConfig(loader: () => Record<string, unknown>) {
sessionsConfigState.loadConfig = loader;
}
export function resetMockSessionsConfig() {
sessionsConfigState.loadConfig = defaultSessionsConfigLoader;
}
export function makeRuntime(params?: { throwOnError?: boolean }): {
runtime: RuntimeEnv;
logs: string[];

View File

@@ -1,3 +1,4 @@
import { resolveAgentRuntimeMetadata } from "../agents/agent-runtime-metadata.js";
import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
import { getRuntimeConfig } from "../config/config.js";
import { loadSessionStore, resolveSessionTotalTokens } from "../config/sessions.js";
@@ -7,6 +8,7 @@ import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js";
import { isRich, theme } from "../terminal/theme.js";
import { resolveSessionStoreTargetsOrExit } from "./session-store-targets.js";
import {
resolveSessionDisplayModelRef,
resolveSessionDisplayDefaults,
resolveSessionDisplayModel,
} from "./sessions-display-model.js";
@@ -25,6 +27,7 @@ import {
type SessionRow = SessionDisplayRow & {
agentId: string;
kind: "direct" | "group" | "global" | "unknown";
agentRuntime: ReturnType<typeof resolveAgentRuntimeMetadata>;
};
const AGENT_PAD = 10;
@@ -146,12 +149,14 @@ export async function sessionsCommand(
const rows = targets
.flatMap((target) => {
const store = loadSessionStore(target.storePath);
return toSessionDisplayRows(store).map((row) =>
Object.assign({}, row, {
agentId: parseAgentSessionKey(row.key)?.agentId ?? target.agentId,
return toSessionDisplayRows(store).map((row) => {
const agentId = parseAgentSessionKey(row.key)?.agentId ?? target.agentId;
return Object.assign({}, row, {
agentId,
agentRuntime: resolveAgentRuntimeMetadata(cfg, agentId),
kind: classifySessionKey(row.key, store[row.key]),
}),
);
});
});
})
.filter((row) => {
if (activeMinutes === undefined) {
@@ -180,7 +185,7 @@ export async function sessionsCommand(
activeMinutes: activeMinutes ?? null,
sessions: await Promise.all(
rows.map(async (r) => {
const model = resolveSessionDisplayModel(cfg, r);
const modelRef = resolveSessionDisplayModelRef(cfg, r);
return {
...r,
totalTokens: resolveSessionTotalTokens(r) ?? null,
@@ -189,10 +194,11 @@ export async function sessionsCommand(
contextTokens:
r.contextTokens ??
configuredContextTokens ??
(await lookupContextTokensForDisplay(model)) ??
(await lookupContextTokensForDisplay(modelRef.model)) ??
configContextTokens ??
null,
model,
modelProvider: modelRef.provider,
model: modelRef.model,
};
}),
),

View File

@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
import { resolveAgentRuntimeMetadata } from "../../agents/agent-runtime-metadata.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import {
abortEmbeddedPiRun,
@@ -79,6 +80,7 @@ import {
resolveDeletedAgentIdFromSessionKey,
resolveFreshestSessionEntryFromStoreKeys,
resolveGatewaySessionStoreTarget,
resolveSessionDisplayModelIdentityRef,
resolveSessionModelRef,
resolveSessionTranscriptCandidates,
type SessionsPatchResult,
@@ -1364,14 +1366,22 @@ export const sessionsHandlers: GatewayRequestHandlers = {
const parsed = parseAgentSessionKey(target.canonicalKey ?? key);
const agentId = normalizeAgentId(parsed?.agentId ?? resolveDefaultAgentId(cfg));
const resolved = resolveSessionModelRef(cfg, applied.entry, agentId);
const resolvedDisplayModel = resolveSessionDisplayModelIdentityRef({
cfg,
agentId,
provider: resolved.provider,
model: resolved.model,
});
const agentRuntime = resolveAgentRuntimeMetadata(cfg, agentId);
const result: SessionsPatchResult = {
ok: true,
path: storePath,
key: target.canonicalKey,
entry: applied.entry,
resolved: {
modelProvider: resolved.provider,
model: resolved.model,
modelProvider: resolvedDisplayModel.provider,
model: resolvedDisplayModel.model,
agentRuntime,
},
};
respond(true, result, undefined);

View File

@@ -1391,7 +1391,11 @@ describe("gateway server sessions", () => {
model?: string;
modelProvider?: string;
};
resolved?: { model?: string; modelProvider?: string };
resolved?: {
model?: string;
modelProvider?: string;
agentRuntime?: { id: string; fallback?: string; source: string };
};
}>("sessions.patch", {
key: "agent:main:main",
model: "openai/gpt-test-a",
@@ -1403,9 +1407,18 @@ describe("gateway server sessions", () => {
expect(modelPatched.payload?.entry.modelProvider).toBeUndefined();
expect(modelPatched.payload?.resolved?.modelProvider).toBe("openai");
expect(modelPatched.payload?.resolved?.model).toBe("gpt-test-a");
expect(modelPatched.payload?.resolved?.agentRuntime).toEqual({
id: "pi",
source: "implicit",
});
const listAfterModelPatch = await directSessionReq<{
sessions: Array<{ key: string; modelProvider?: string; model?: string }>;
sessions: Array<{
key: string;
modelProvider?: string;
model?: string;
agentRuntime?: { id: string; fallback?: string; source: string };
}>;
}>("sessions.list", {});
expect(listAfterModelPatch.ok).toBe(true);
const mainAfterModelPatch = listAfterModelPatch.payload?.sessions.find(
@@ -1413,6 +1426,7 @@ describe("gateway server sessions", () => {
);
expect(mainAfterModelPatch?.modelProvider).toBe("openai");
expect(mainAfterModelPatch?.model).toBe("gpt-test-a");
expect(mainAfterModelPatch?.agentRuntime).toEqual({ id: "pi", source: "implicit" });
const compacted = await directSessionReq<{ ok: true; compacted: boolean }>("sessions.compact", {
key: "agent:main:main",
@@ -3723,7 +3737,11 @@ describe("gateway server sessions", () => {
const patched = await rpcReq<{
entry: { label?: string };
key: string;
resolved: { modelProvider: string; model: string };
resolved: {
modelProvider: string;
model: string;
agentRuntime: { id: string; fallback?: string; source: string };
};
}>(ws, "sessions.patch", {
key: "agent:main:main",
label: "cfg-isolation",
@@ -3733,6 +3751,7 @@ describe("gateway server sessions", () => {
expect(patched.payload?.resolved).toEqual({
modelProvider: "anthropic",
model: "claude-opus-4-6",
agentRuntime: { id: "pi", source: "implicit" },
});
expect(patched.payload?.entry.label).toBe("cfg-isolation");

View File

@@ -23,6 +23,7 @@ import {
resolveDeletedAgentIdFromSessionKey,
resolveGatewayModelSupportsImages,
resolveGatewaySessionStoreTarget,
resolveSessionDisplayModelIdentityRef,
resolveSessionModelIdentityRef,
resolveSessionModelRef,
resolveSessionStoreKey,
@@ -57,12 +58,14 @@ function createSingleAgentAvatarConfig(workspace: string): OpenClawConfig {
function createModelDefaultsConfig(params: {
primary: string;
models?: Record<string, Record<string, never>>;
agentRuntime?: { id: string; fallback?: "pi" | "none" };
}): OpenClawConfig {
return {
agents: {
defaults: {
model: { primary: params.primary },
models: params.models,
agentRuntime: params.agentRuntime,
},
},
} as OpenClawConfig;
@@ -1106,6 +1109,62 @@ describe("listSessionsFromStore selected model display", () => {
expect(result.sessions[0]?.modelProvider).toBe("anthropic");
expect(result.sessions[0]?.model).toBe("claude-opus-4-6");
});
test("separates Claude CLI runtime metadata from canonical model identity", () => {
const cfg = createModelDefaultsConfig({
primary: "anthropic/claude-opus-4-7",
agentRuntime: { id: "claude-cli", fallback: "none" },
});
const result = listSessionsFromStore({
cfg,
storePath: "/tmp/sessions.json",
store: {
"agent:main:main": {
sessionId: "sess-main",
updatedAt: Date.now(),
modelProvider: "claude-cli",
model: "claude-opus-4-7",
} as SessionEntry,
},
opts: {},
});
expect(result.sessions[0]?.modelProvider).toBe("anthropic");
expect(result.sessions[0]?.model).toBe("claude-opus-4-7");
expect(result.sessions[0]?.agentRuntime).toEqual({
id: "claude-cli",
fallback: "none",
source: "defaults",
});
});
test("infers canonical provider for bare CLI models before default-provider fallback", () => {
const cfg = createModelDefaultsConfig({
primary: "openai/gpt-5.4",
models: {
"anthropic/claude-opus-4-7": {},
},
agentRuntime: { id: "claude-cli", fallback: "none" },
});
const result = listSessionsFromStore({
cfg,
storePath: "/tmp/sessions.json",
store: {
"agent:main:main": {
sessionId: "sess-main",
updatedAt: Date.now(),
modelProvider: "claude-cli",
model: "claude-opus-4-7",
} as SessionEntry,
},
opts: {},
});
expect(result.sessions[0]?.modelProvider).toBe("anthropic");
expect(result.sessions[0]?.model).toBe("claude-opus-4-7");
});
});
describe("resolveSessionModelIdentityRef", () => {
@@ -1238,6 +1297,43 @@ describe("resolveSessionModelIdentityRef", () => {
});
});
describe("resolveSessionDisplayModelIdentityRef", () => {
test("canonicalizes CLI runtime provider to the selected model provider", () => {
const cfg = createModelDefaultsConfig({
primary: "anthropic/claude-opus-4-7",
agentRuntime: { id: "claude-cli", fallback: "none" },
});
expect(
resolveSessionDisplayModelIdentityRef({
cfg,
agentId: "main",
provider: "claude-cli",
model: "claude-opus-4-7",
}),
).toEqual({ provider: "anthropic", model: "claude-opus-4-7" });
});
test("prefers configured provider inference over default-provider parsing for bare CLI models", () => {
const cfg = createModelDefaultsConfig({
primary: "openai/gpt-5.4",
models: {
"anthropic/claude-opus-4-7": {},
},
agentRuntime: { id: "claude-cli", fallback: "none" },
});
expect(
resolveSessionDisplayModelIdentityRef({
cfg,
agentId: "main",
provider: "claude-cli",
model: "claude-opus-4-7",
}),
).toEqual({ provider: "anthropic", model: "claude-opus-4-7" });
});
});
describe("deriveSessionTitle", () => {
test("returns undefined for undefined entry", () => {
expect(deriveSessionTitle(undefined)).toBeUndefined();

View File

@@ -18,6 +18,7 @@ import {
} from "../agents/model-catalog.js";
import {
inferUniqueProviderFromConfiguredModels,
isCliProvider,
normalizeStoredOverrideModel,
parseModelRef,
resolveConfiguredModelRef,
@@ -1249,6 +1250,45 @@ export function resolveSessionModelIdentityRef(
return { provider: resolved.provider, model: resolved.model };
}
export function resolveSessionDisplayModelIdentityRef(params: {
cfg: OpenClawConfig;
agentId: string;
provider?: string;
model?: string;
}): { provider?: string; model?: string } {
const provider = normalizeOptionalString(params.provider);
const model = normalizeOptionalString(params.model);
if (!provider || !model || !isCliProvider(provider, params.cfg)) {
return { provider, model };
}
const defaultRef = resolveDefaultModelForAgent({ cfg: params.cfg, agentId: params.agentId });
if (model.includes("/")) {
const parsedModel = parseModelRef(model, defaultRef.provider);
if (parsedModel && !isCliProvider(parsedModel.provider, params.cfg)) {
return parsedModel;
}
}
const inferredProvider = inferUniqueProviderFromConfiguredModels({
cfg: params.cfg,
model,
});
if (inferredProvider && !isCliProvider(inferredProvider, params.cfg)) {
return { provider: inferredProvider, model };
}
const parsedModel = parseModelRef(model, defaultRef.provider);
if (parsedModel && !isCliProvider(parsedModel.provider, params.cfg)) {
return parsedModel;
}
return {
provider: defaultRef.provider || provider,
model,
};
}
export function buildGatewaySessionRow(params: {
cfg: OpenClawConfig;
storePath: string;
@@ -1395,11 +1435,22 @@ export function buildGatewaySessionRow(params: {
: transcriptUsage?.totalTokensFresh === true;
const childSessions = resolveChildSessionKeys(key, store, now);
const latestCompactionCheckpoint = resolveLatestCompactionCheckpoint(entry);
const agentRuntime = resolveAgentRuntimeMetadata(cfg, sessionAgentId);
const selectedOrRuntimeModelProvider = selectedModel?.provider ?? modelProvider;
const selectedOrRuntimeModel = selectedModel?.model ?? model;
const rowModelIdentity = resolveSessionDisplayModelIdentityRef({
cfg,
agentId: sessionAgentId,
provider: selectedOrRuntimeModelProvider,
model: selectedOrRuntimeModel,
});
const rowModelProvider = rowModelIdentity.provider;
const rowModel = rowModelIdentity.model;
const estimatedCostUsd =
resolveEstimatedSessionCostUsd({
cfg,
provider: modelProvider,
model,
provider: rowModelProvider,
model: rowModel,
entry,
}) ?? resolveNonNegativeNumber(transcriptUsage?.estimatedCostUsd);
const contextTokens =
@@ -1408,8 +1459,8 @@ export function buildGatewaySessionRow(params: {
resolvePositiveNumber(
resolveContextTokensForModel({
cfg,
provider: modelProvider,
model,
provider: rowModelProvider,
model: rowModel,
// Gateway/session listing is read-only; don't start async model discovery.
allowAsyncLoad: false,
}),
@@ -1432,8 +1483,6 @@ export function buildGatewaySessionRow(params: {
}
}
const rowModelProvider = selectedModel?.provider ?? modelProvider;
const rowModel = selectedModel?.model ?? model;
const thinkingProvider = rowModelProvider ?? DEFAULT_PROVIDER;
const thinkingModel = rowModel ?? DEFAULT_MODEL;
const thinkingLevels = listThinkingLevelOptions(
@@ -1500,6 +1549,7 @@ export function buildGatewaySessionRow(params: {
responseUsage: entry?.responseUsage,
modelProvider: rowModelProvider,
model: rowModel,
agentRuntime,
contextTokens,
deliveryContext: deliveryFields.deliveryContext,
lastChannel: deliveryFields.lastChannel ?? entry?.lastChannel,

View File

@@ -2,6 +2,7 @@ import type { ChatType } from "../channels/chat-type.js";
import type { SessionCompactionCheckpoint, SessionEntry } from "../config/sessions/types.js";
import type { PluginSessionExtensionProjection } from "../plugins/host-hooks.js";
import type {
GatewayAgentRuntime,
GatewayAgentRow as SharedGatewayAgentRow,
SessionsListResultBase,
SessionsPatchResultBase,
@@ -75,6 +76,7 @@ export type GatewaySessionRow = {
responseUsage?: "on" | "off" | "tokens" | "full";
modelProvider?: string;
model?: string;
agentRuntime?: GatewayAgentRuntime;
contextTokens?: number;
deliveryContext?: DeliveryContext;
lastChannel?: SessionEntry["lastChannel"];
@@ -111,5 +113,6 @@ export type SessionsPatchResult = SessionsPatchResultBase<SessionEntry> & {
resolved?: {
modelProvider?: string;
model?: string;
agentRuntime?: GatewayAgentRuntime;
};
};

View File

@@ -2,6 +2,7 @@ export type UpdateAvailable = import("../../../src/infra/update-startup.js").Upd
import type { CronJobBase } from "../../../src/cron/types-shared.js";
import type { ConfigUiHints } from "../../../src/shared/config-ui-hints-types.js";
import type {
GatewayAgentRuntime,
GatewayAgentRow as SharedGatewayAgentRow,
SessionsListResultBase,
SessionsPatchResultBase,
@@ -443,6 +444,7 @@ export type GatewaySessionRow = {
childSessions?: string[];
model?: string;
modelProvider?: string;
agentRuntime?: GatewayAgentRuntime;
contextTokens?: number;
compactionCheckpointCount?: number;
latestCompactionCheckpoint?: SessionCompactionCheckpoint;
@@ -497,6 +499,7 @@ export type SessionsPatchResult = SessionsPatchResultBase<{
resolved?: {
modelProvider?: string;
model?: string;
agentRuntime?: GatewayAgentRuntime;
};
};