generated from nhcarrigan/template
feat: add feature to monitor background agents (#125)
Also includes a fix to persist configuration across reconnects. Reviewed-on: #125 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #125.
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { writable, derived } from "svelte/store";
|
||||
import type { AgentInfo } from "$lib/types/agents";
|
||||
|
||||
// Map of conversation ID -> agents in that conversation
|
||||
const agentsByConversation = writable<Record<string, AgentInfo[]>>({});
|
||||
|
||||
function createAgentStore() {
|
||||
return {
|
||||
subscribe: agentsByConversation.subscribe,
|
||||
|
||||
addAgent(conversationId: string, agent: AgentInfo) {
|
||||
agentsByConversation.update((state) => {
|
||||
const existing = state[conversationId] || [];
|
||||
return {
|
||||
...state,
|
||||
[conversationId]: [...existing, agent],
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
updateAgentId(conversationId: string, toolUseId: string, agentId: 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,
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
[conversationId]: updated,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
endAgent(conversationId: string, toolUseId: string, endedAt: number, isError: boolean) {
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
@@ -435,7 +435,8 @@ function createConversationsStore() {
|
||||
type: TerminalLine["type"],
|
||||
content: string,
|
||||
toolName?: string,
|
||||
cost?: TerminalLine["cost"]
|
||||
cost?: TerminalLine["cost"],
|
||||
parentToolUseId?: string
|
||||
) => {
|
||||
ensureInitialized();
|
||||
const activeId = get(activeConversationId);
|
||||
@@ -448,6 +449,7 @@ function createConversationsStore() {
|
||||
timestamp: new Date(),
|
||||
toolName,
|
||||
cost,
|
||||
parentToolUseId,
|
||||
};
|
||||
|
||||
conversations.update((convs) => {
|
||||
@@ -469,7 +471,8 @@ function createConversationsStore() {
|
||||
type: TerminalLine["type"],
|
||||
content: string,
|
||||
toolName?: string,
|
||||
cost?: TerminalLine["cost"]
|
||||
cost?: TerminalLine["cost"],
|
||||
parentToolUseId?: string
|
||||
) => {
|
||||
ensureInitialized();
|
||||
|
||||
@@ -480,6 +483,7 @@ function createConversationsStore() {
|
||||
timestamp: new Date(),
|
||||
toolName,
|
||||
cost,
|
||||
parentToolUseId,
|
||||
};
|
||||
|
||||
conversations.update((convs) => {
|
||||
|
||||
Reference in New Issue
Block a user