generated from nhcarrigan/template
452fe185df
## Summary This PR brings Hikari Desktop up to full compatibility with Claude Code CLI versions v2.1.68 through v2.1.74, implementing all changelog items audited in issues #200–#218. ## Changes ### Bug Fixes - Remove deprecated Claude Opus 4.0 and 4.1 models from the model selector - Auto-migrate users pinned to deprecated models to Opus 4.6 ### New Features - Add cron tool support (`CronCreate`, `CronDelete`, `CronList`) with character state mapping and `CLAUDE_CODE_DISABLE_CRON` settings toggle - Handle `EnterWorktree` and `ExitWorktree` tools in character state mapping and tool display - Add CLI update check with npm registry indicator in the version bar - Add `agent_type` field and support the Agent tool rename from CLI v2.1.69 - Consume `worktree` field from status line hook events - Display per-agent model override in the agent monitor tree - Expose Claude Code CLI built-in slash commands (`/simplify`, `/loop`, `/batch`, `/memory`, `/context`) in the command menu with CLI badges - Add `includeGitInstructions` toggle in settings - Add `ENABLE_CLAUDEAI_MCP_SERVERS` opt-out setting - Linkify MCP binary file paths (PDFs, audio, Office docs) in markdown output - Add auto-memory panel, `/memory` slash command shortcut, and unified toast notification system - Toast notifications for `WorktreeCreate` and `WorktreeRemove` hook events - Sort session resume list by most recent activity, with most recent user message as preview - Convert WSL Linux paths to Windows UNC paths when opening binary files via `open_binary_file` command - Expose `autoMemoryDirectory` setting in ConfigSidebar (Agent Settings section) - Add `/context` as a CLI built-in in the slash command menu - Expose `modelOverrides` setting as a JSON textarea in ConfigSidebar (for AWS Bedrock, Google Vertex, etc.) > **Note:** The CLI update check commit does not have a corresponding issue — it was a bonus addition during the audit sprint. ## Closes Closes #200 Closes #201 Closes #202 Closes #205 Closes #206 Closes #207 Closes #208 Closes #209 Closes #210 Closes #211 Closes #212 Closes #213 Closes #214 Closes #215 Closes #216 Closes #217 Closes #218 Reviewed-on: #221 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import { writable, derived } from "svelte/store";
|
|
import type { AgentInfo } from "$lib/types/agents";
|
|
import { assignCharacter } from "$lib/utils/agentCharacters";
|
|
|
|
// Map of conversation ID -> agents in that conversation
|
|
const agentsByConversation = writable<Record<string, AgentInfo[]>>({});
|
|
|
|
function createAgentStore() {
|
|
return {
|
|
subscribe: agentsByConversation.subscribe,
|
|
|
|
addAgent(conversationId: string, agent: Omit<AgentInfo, "characterName" | "characterAvatar">) {
|
|
agentsByConversation.update((state) => {
|
|
const existing = state[conversationId] || [];
|
|
const activeNames = existing.map((a) => a.characterName);
|
|
const character = assignCharacter(activeNames);
|
|
return {
|
|
...state,
|
|
[conversationId]: [
|
|
...existing,
|
|
{ ...agent, characterName: character.name, characterAvatar: character.avatar },
|
|
],
|
|
};
|
|
});
|
|
},
|
|
|
|
updateAgentId(conversationId: string, toolUseId: string, agentId: string, agentType?: string) {
|
|
agentsByConversation.update((state) => {
|
|
const agents = state[conversationId];
|
|
if (!agents) return state;
|
|
|
|
const agentIndex = agents.findIndex((a) => a.toolUseId === toolUseId);
|
|
if (agentIndex === -1) return state;
|
|
|
|
const updated = [...agents];
|
|
updated[agentIndex] = {
|
|
...updated[agentIndex],
|
|
agentId,
|
|
...(agentType !== undefined ? { agentType } : {}),
|
|
};
|
|
|
|
return {
|
|
...state,
|
|
[conversationId]: updated,
|
|
};
|
|
});
|
|
},
|
|
|
|
endAgent(
|
|
conversationId: string,
|
|
toolUseId: string,
|
|
endedAt: number,
|
|
isError: boolean,
|
|
lastAssistantMessage?: string
|
|
) {
|
|
agentsByConversation.update((state) => {
|
|
const agents = state[conversationId];
|
|
if (!agents) return state;
|
|
|
|
const agentIndex = agents.findIndex((a) => a.toolUseId === toolUseId);
|
|
if (agentIndex === -1) return state;
|
|
|
|
const updated = [...agents];
|
|
const agent = updated[agentIndex];
|
|
const durationMs = endedAt - agent.startedAt;
|
|
|
|
updated[agentIndex] = {
|
|
...agent,
|
|
endedAt,
|
|
status: isError ? "errored" : "completed",
|
|
durationMs,
|
|
lastAssistantMessage,
|
|
};
|
|
|
|
return {
|
|
...state,
|
|
[conversationId]: updated,
|
|
};
|
|
});
|
|
},
|
|
|
|
markAllErrored(conversationId: string) {
|
|
agentsByConversation.update((state) => {
|
|
const agents = state[conversationId];
|
|
if (!agents) return state;
|
|
|
|
const now = Date.now();
|
|
const updated = agents.map((agent) =>
|
|
agent.status === "running"
|
|
? { ...agent, endedAt: now, status: "errored" as const }
|
|
: agent
|
|
);
|
|
|
|
return {
|
|
...state,
|
|
[conversationId]: updated,
|
|
};
|
|
});
|
|
},
|
|
|
|
clearCompleted(conversationId: string) {
|
|
agentsByConversation.update((state) => {
|
|
const agents = state[conversationId];
|
|
if (!agents) return state;
|
|
|
|
return {
|
|
...state,
|
|
[conversationId]: agents.filter((a) => a.status === "running"),
|
|
};
|
|
});
|
|
},
|
|
|
|
clearConversation(conversationId: string) {
|
|
agentsByConversation.update((state) => {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- Unused destructured value
|
|
const { [conversationId]: _, ...rest } = state;
|
|
return rest;
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
export const agentStore = createAgentStore();
|
|
|
|
export function getAgentsForConversation(conversationId: string) {
|
|
return derived(agentsByConversation, ($state) => $state[conversationId] || []);
|
|
}
|
|
|
|
export const runningAgentCount = derived(agentsByConversation, ($state) => {
|
|
let count = 0;
|
|
for (const agents of Object.values($state)) {
|
|
count += agents.filter((a) => a.status === "running").length;
|
|
}
|
|
return count;
|
|
});
|