9381a6a9f7
* fix(scripts): launch dev-isolated backend via __backend subcommand The kandev binary became a bundle launcher with the restart supervisor (ADR 0019) and exits without KANDEV_BUNDLE_DIR when invoked bare; the isolated dev instance must run the backend entrypoint directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(mcp): session spawning, sibling messaging, and renameable tabs - spawn_session_kandev spawns an additional agent session on an existing task (default: the caller's own) via LaunchSession(IntentStart), with optional agent_profile_id (defaults to the spawner's profile) and a session name; the initial prompt carries spawner attribution and reply instructions. - message_task_kandev accepts an optional session_id to target a specific session, enabling same-task sibling messaging. Explicit targets are pinned through dispatch so the idle-path primary-session re-resolution cannot reroute a sibling message back to its sender (regression-tested). - task_sessions gains a name column (migration ordered after the agent_execution_id-drop rebuild), a session.rename WS action, and inline tab rename UI; custom names take precedence over derived agent/model labels across the tab, agents dropdown, mobile list, chat header, and dockview panel titles. - Inter-session messages stamp sender_session_name into metadata so the sender badge shows which session sent the message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): address review findings on session spawn/rename/messaging - include name in task-session DTOs (list + full, no omitempty) so persisted tab names survive list hydration and clears propagate - sanitize sender task/session ids embedded in <kandev-system> blocks - reject cross-workspace spawn targets; require sender_task_id - truncate session names by runes, not bytes - distinguish not-found from internal errors in message target lookup - guard TabRenameInput blur-commit after Enter/Escape resolution - cap optimistic rename length client-side and toast on rename failure - add tests: spawn launch-failure and cross-workspace paths, renameSession API client Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): round-two review fixes for spawn/rename lint and hardening - extract spawn validation/authorization helpers (cyclop/nestif limits) - make stripSystemTag replace until stable to block nested tag evasion - load session before rename so a durable rename can never skip its broadcast; surface lookup failures to the caller instead - drop redundant sessionLauncher nil check (guarded registration) - add maxLength to the rename input matching the server cap - add tests: nested sanitizer evasion, spawn rename-failure tolerance Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import {
|
|
displayModelName,
|
|
isModelConfigOption,
|
|
type DynamicConfigOption,
|
|
type ModelSelectorOption,
|
|
} from "@/components/model-config-selector";
|
|
|
|
type ResolveSessionTabTitleArgs = {
|
|
/** User-supplied session name; wins over every derived title when set. */
|
|
customName?: string | null;
|
|
agentLabel: string | null;
|
|
activeModelId: string | null;
|
|
currentModelId: string | null;
|
|
snapshotModel: string | null;
|
|
modelOptions: ModelSelectorOption[];
|
|
configOptions: DynamicConfigOption[];
|
|
};
|
|
|
|
function optionName(option: DynamicConfigOption, value: string): string {
|
|
return option.options?.find((item) => item.value === value)?.name ?? value;
|
|
}
|
|
|
|
function resolveModelTitle(
|
|
args: ResolveSessionTabTitleArgs,
|
|
modelId: string | null,
|
|
): string | null {
|
|
if (!modelId) return null;
|
|
|
|
const modelConfig = args.configOptions.find(isModelConfigOption);
|
|
let modelLabel = displayModelName(args.modelOptions, modelId);
|
|
if (modelConfig) {
|
|
// Use caller-supplied modelId, not modelConfig.currentValue, so live
|
|
// active/current model switches are reflected immediately in the tab title.
|
|
modelLabel = optionName(modelConfig, modelId);
|
|
}
|
|
const extras = args.configOptions
|
|
.filter((option) => !isModelConfigOption(option))
|
|
.map((option) => optionName(option, option.currentValue))
|
|
.filter(Boolean);
|
|
return [modelLabel, ...extras].join(" / ");
|
|
}
|
|
|
|
export function resolveSessionTabTitle(args: ResolveSessionTabTitleArgs): string | null {
|
|
if (args.customName) return args.customName;
|
|
const liveModelId = args.activeModelId || args.currentModelId;
|
|
return (
|
|
args.agentLabel ??
|
|
resolveModelTitle(args, liveModelId) ??
|
|
resolveModelTitle(args, args.snapshotModel)
|
|
);
|
|
}
|