Files
openclaw/extensions/anthropic/session-catalog-runtime.ts
Jason (Json) b32f59c8c1 feat(ui): create catalog sessions from the sidebar (#105810)
* feat(ui): create sessions from catalog headers

* fix(ui): deduplicate catalog-owned sessions

* fix(ui): preserve catalog session routing state

* fix(ui): keep per-agent session creation in switcher

* fix(anthropic): normalize runtime config snapshot

* test(plugin-sdk): account for model runtime export

* test(plugin-sdk): classify current deprecated surface

* test(ui): follow agent chip menu trigger

* fix(ui): bind catalog drafts to validated agent

* fix(ui): resolve catalog targets from gateway

* refactor(ui): split sidebar session helpers

* chore(ci): complete LOC baseline migration

* fix(ui): extract sidebar session catalog rendering

* fix(anthropic): use provider-relative catalog model id

* chore(ci): ratchet sidebar LOC baselines

* test(ui): cover catalog draft retargeting

* chore(plugin-sdk): refresh API baseline

* style: format catalog session changes

* fix(ui): retry catalog drafts after reconnect

* fix(ui): back off catalog target retries

* fix(ui): keep unresolved catalog drafts locked

* fix(ui): gate catalog creation in core

* fix(ui): keep catalog renderer private

* style(ui): format catalog session target

* chore(plugin-sdk): refresh API baseline

* style(ui): format catalog group state

* Merge latest origin/main CI fixes

* fix(ui): preserve catalog session agent selection

* fix(ui): preserve catalog session agent

* chore(plugin-sdk): refresh API baseline

* fix(ui): type catalog route data

* chore(plugin-sdk): refresh API baseline

* refactor(sessions): centralize transcript path archival

* fix(ui): target expanded agent catalog sessions

* fix(ui): scope catalog actions to expanded agent

* docs(ui): clarify catalog loader fallback

* fix(ui): scope catalog pagination to agent

* test(ui): type sidebar lifecycle refresh

* fix(anthropic): preserve node catalog bindings
2026-07-13 09:25:22 -06:00

103 lines
3.8 KiB
TypeScript

import {
listAgentIds,
resolveAllowedModelRef,
resolveDefaultAgentId,
resolveDefaultModelForAgent,
} from "openclaw/plugin-sdk/agent-runtime";
import { resolveEffectiveAgentRuntime } from "openclaw/plugin-sdk/command-auth-native";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
CLAUDE_CLI_BACKEND_ID,
CLAUDE_CLI_CANONICAL_DEFAULT_MODEL_ID,
CLAUDE_CLI_CANONICAL_DEFAULT_MODEL_REF,
} from "./cli-constants.js";
import { adoptedSourceKey, CLAUDE_LOCAL_SESSION_HOST_ID } from "./session-catalog-adoption.js";
export function currentClaudeSessionCatalogConfig(api: OpenClawPluginApi): OpenClawConfig {
return (api.runtime.config?.current?.() ?? api.config ?? {}) as OpenClawConfig;
}
function boundClaudeSource(
pluginId: string,
entry: {
cliSessionBindings?: unknown;
execHost?: string;
execNode?: string;
pluginOwnerId?: string;
modelSelectionLocked?: boolean;
pluginExtensions?: unknown;
},
): { hostId: string; threadId: string } | undefined {
const anthropic = isRecord(entry.pluginExtensions) ? entry.pluginExtensions.anthropic : undefined;
const marker = isRecord(anthropic) ? anthropic.sessionCatalog : undefined;
const hostId =
isRecord(marker) && typeof marker.sourceHostId === "string"
? marker.sourceHostId
: entry.execHost === "node" && typeof entry.execNode === "string" && entry.execNode.trim()
? `node:${entry.execNode.trim()}`
: CLAUDE_LOCAL_SESSION_HOST_ID;
const bindings = isRecord(entry.cliSessionBindings) ? entry.cliSessionBindings : undefined;
const binding = bindings?.[CLAUDE_CLI_BACKEND_ID];
if (isRecord(binding) && typeof binding.sessionId === "string" && binding.sessionId) {
return { hostId, threadId: binding.sessionId };
}
if (entry.pluginOwnerId !== pluginId || entry.modelSelectionLocked !== true) {
return undefined;
}
return isRecord(marker) && typeof marker.sourceThreadId === "string"
? { hostId, threadId: marker.sourceThreadId }
: undefined;
}
export function listBoundClaudeSessions(api: OpenClawPluginApi): Map<string, string> {
const config = currentClaudeSessionCatalogConfig(api);
const defaultAgentId = resolveDefaultAgentId(config);
const agentIds = [
defaultAgentId,
...listAgentIds(config).filter((agentId) => agentId !== defaultAgentId),
];
const bound = new Map<string, string>();
for (const { sessionKey, entry } of agentIds.flatMap((agentId) =>
api.runtime.agent.session.listSessionEntries({ agentId }),
)) {
const source = boundClaudeSource(api.id, entry);
if (source) {
bound.set(adoptedSourceKey(source.hostId, source.threadId), sessionKey);
}
}
return bound;
}
export function resolveClaudeCatalogCreateSession(
api: OpenClawPluginApi,
requestedAgentId?: string,
): { model: string; agentRuntime: string } | undefined {
const config = currentClaudeSessionCatalogConfig(api);
const agentId = requestedAgentId ?? resolveDefaultAgentId(config);
const agentRuntime = resolveEffectiveAgentRuntime({
cfg: config,
provider: "anthropic",
modelId: CLAUDE_CLI_CANONICAL_DEFAULT_MODEL_ID,
agentId,
});
if (agentRuntime !== CLAUDE_CLI_BACKEND_ID) {
return undefined;
}
const defaultModel = resolveDefaultModelForAgent({ cfg: config, agentId });
const allowed = resolveAllowedModelRef({
cfg: config,
catalog: [],
raw: CLAUDE_CLI_CANONICAL_DEFAULT_MODEL_REF,
defaultProvider: defaultModel.provider,
defaultModel: defaultModel.model,
});
return "error" in allowed
? undefined
: {
model: CLAUDE_CLI_CANONICAL_DEFAULT_MODEL_REF,
agentRuntime: CLAUDE_CLI_BACKEND_ID,
};
}