refactor: add dynamic input height, agent sorting, and last activity tracking
This commit is contained in:
@@ -1,79 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "b203dc68-03e0-4971-a3d7-a4822c940b50",
|
||||
"title": "Agent b203dc68",
|
||||
"sessionId": "07f421be-98c1-4f23-9604-8f00274956c7",
|
||||
"id": "5d28d04a-1b29-43fd-b27e-bcaa32ee453d",
|
||||
"title": "Add Three Global Footer Buttons",
|
||||
"sessionId": "147f5a79-e310-4bfd-86fa-0c6a011cb7c7",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": "07f421be-98c1-4f23-9604-8f00274956c7"
|
||||
"sessionId": "147f5a79-e310-4bfd-86fa-0c6a011cb7c7"
|
||||
},
|
||||
"createdAt": "2025-10-24T15:53:55.340Z",
|
||||
"createdAt": "2025-10-25T19:50:58.711Z",
|
||||
"lastActivityAt": "2025-10-25T19:50:58.711Z",
|
||||
"cwd": "/Users/moboudra/dev/voice-dev"
|
||||
},
|
||||
{
|
||||
"id": "d0633729-2f74-426a-a3a8-05e8c7206703",
|
||||
"title": "Assist with Voice Development",
|
||||
"sessionId": "79c9a8e0-8a24-4851-baad-62747b815442",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": "79c9a8e0-8a24-4851-baad-62747b815442"
|
||||
},
|
||||
"createdAt": "2025-10-24T15:57:04.442Z",
|
||||
"cwd": "/Users/moboudra/dev/voice-dev"
|
||||
},
|
||||
{
|
||||
"id": "50128b81-017c-4ab5-8901-3d3f6b65898c",
|
||||
"title": "Agent 50128b81",
|
||||
"sessionId": "019a1c46-f0ea-76f4-8c1d-6c5d568f7b05",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": null
|
||||
},
|
||||
"createdAt": "2025-10-25T10:48:01.416Z",
|
||||
"cwd": "/Users/moboudra/dev/faro/main"
|
||||
},
|
||||
{
|
||||
"id": "cf327185-2307-4744-b2d0-c50f8bfa8f55",
|
||||
"title": "Agent cf327185",
|
||||
"sessionId": "019a1b66-dbf2-76b0-b578-60abfbd65d22",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": null
|
||||
},
|
||||
"createdAt": "2025-10-25T12:37:28.278Z",
|
||||
"cwd": "/Users/moboudra/dev/faro/main"
|
||||
},
|
||||
{
|
||||
"id": "9af00580-418b-4053-a942-a8d168ab5703",
|
||||
"title": "Initial User Greeting",
|
||||
"sessionId": "7adb8247-5ffe-44a0-ae8b-177a5611fe6c",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": "7adb8247-5ffe-44a0-ae8b-177a5611fe6c"
|
||||
},
|
||||
"createdAt": "2025-10-25T12:44:52.183Z",
|
||||
"cwd": "/Users/moboudra/dev/faro/main"
|
||||
},
|
||||
{
|
||||
"id": "415c2eb6-6649-4f3f-aee5-acc3d6b2cfd4",
|
||||
"title": "Agent 415c2eb6",
|
||||
"sessionId": "019a1c5a-0929-76de-ad58-1b80fa056209",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": null
|
||||
},
|
||||
"createdAt": "2025-10-25T17:10:52.912Z",
|
||||
"cwd": "/Users/moboudra/dev/faro/main"
|
||||
},
|
||||
{
|
||||
"id": "cac8f9e6-3b3b-4916-8e04-2b2ea8053f22",
|
||||
"title": "Review Open Pull Requests",
|
||||
"sessionId": "5f3938aa-bd06-47f2-b09e-b6bae76f7ffc",
|
||||
"options": {
|
||||
"type": "claude",
|
||||
"sessionId": "5f3938aa-bd06-47f2-b09e-b6bae76f7ffc"
|
||||
},
|
||||
"createdAt": "2025-10-25T17:43:48.309Z",
|
||||
"cwd": "/Users/moboudra/dev/faro/main"
|
||||
}
|
||||
]
|
||||
@@ -42,6 +42,7 @@ interface ManagedAgent {
|
||||
id: string;
|
||||
cwd: string;
|
||||
createdAt: Date;
|
||||
lastActivityAt: Date;
|
||||
title: string;
|
||||
options: AgentOptions;
|
||||
subscribers: Set<AgentUpdateCallback>;
|
||||
@@ -166,10 +167,15 @@ export class AgentManager {
|
||||
console.log(
|
||||
`[AgentManager] Loading agent ${persistedAgent.id} as uninitialized`
|
||||
);
|
||||
const createdAt = new Date(persistedAgent.createdAt);
|
||||
const lastActivityAt = persistedAgent.lastActivityAt
|
||||
? new Date(persistedAgent.lastActivityAt)
|
||||
: createdAt;
|
||||
const agent: ManagedAgent = {
|
||||
id: persistedAgent.id,
|
||||
cwd: expandTilde(persistedAgent.cwd),
|
||||
createdAt: new Date(persistedAgent.createdAt),
|
||||
createdAt,
|
||||
lastActivityAt,
|
||||
title: persistedAgent.title,
|
||||
options: persistedAgent.options,
|
||||
subscribers: new Set(),
|
||||
@@ -225,6 +231,7 @@ export class AgentManager {
|
||||
id: agentId,
|
||||
cwd,
|
||||
createdAt,
|
||||
lastActivityAt: createdAt,
|
||||
title: "",
|
||||
options: agentOptions,
|
||||
subscribers: new Set(),
|
||||
@@ -250,6 +257,7 @@ export class AgentManager {
|
||||
sessionId: null,
|
||||
options: agent.options,
|
||||
createdAt: createdAt.toISOString(),
|
||||
lastActivityAt: agent.lastActivityAt.toISOString(),
|
||||
cwd: agent.cwd,
|
||||
});
|
||||
|
||||
@@ -553,6 +561,7 @@ export class AgentManager {
|
||||
id: agent.id,
|
||||
status,
|
||||
createdAt: agent.createdAt,
|
||||
lastActivityAt: agent.lastActivityAt,
|
||||
type: "claude" as const,
|
||||
sessionId,
|
||||
error: error ?? null,
|
||||
@@ -617,6 +626,7 @@ export class AgentManager {
|
||||
id: agent.id,
|
||||
status,
|
||||
createdAt: agent.createdAt,
|
||||
lastActivityAt: agent.lastActivityAt,
|
||||
type: "claude",
|
||||
sessionId: runtime?.sessionId ?? null,
|
||||
error: error ?? null,
|
||||
@@ -1000,6 +1010,7 @@ export class AgentManager {
|
||||
sessionId: runtime.sessionId,
|
||||
options: agent.options,
|
||||
createdAt: agent.createdAt.toISOString(),
|
||||
lastActivityAt: agent.lastActivityAt.toISOString(),
|
||||
cwd: agent.cwd,
|
||||
});
|
||||
|
||||
@@ -1056,6 +1067,9 @@ export class AgentManager {
|
||||
const agent = this.agents.get(agentId);
|
||||
if (!agent) return;
|
||||
|
||||
// Update last activity timestamp
|
||||
agent.lastActivityAt = new Date();
|
||||
|
||||
// Augment update with stable message IDs for deduplication
|
||||
let enrichedUpdate: EnrichedSessionNotification;
|
||||
|
||||
@@ -1286,17 +1300,6 @@ export class AgentManager {
|
||||
console.log(
|
||||
`[Agent ${agentId}] Permission request emitted: ${requestId}`
|
||||
);
|
||||
|
||||
// Set a timeout to auto-reject after 5 minutes if no response
|
||||
setTimeout(() => {
|
||||
if (agent.pendingPermissions.has(requestId)) {
|
||||
console.warn(
|
||||
`[Agent ${agentId}] Permission request ${requestId} timed out`
|
||||
);
|
||||
agent.pendingPermissions.delete(requestId);
|
||||
reject(new Error("Permission request timed out"));
|
||||
}
|
||||
}, 5 * 60 * 1000); // 5 minutes
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1459,6 +1462,7 @@ export class AgentManager {
|
||||
sessionId: runtime.sessionId,
|
||||
options: agent.options,
|
||||
createdAt: agent.createdAt.toISOString(),
|
||||
lastActivityAt: agent.lastActivityAt.toISOString(),
|
||||
cwd: agent.cwd,
|
||||
});
|
||||
console.log(`[Agent ${agent.id}] State persisted`);
|
||||
|
||||
@@ -25,6 +25,7 @@ export const PersistedAgentSchema = z.object({
|
||||
sessionId: z.string().nullable(), // ACP protocol session ID (null for uninitialized agents)
|
||||
options: AgentOptionsSchema, // Required field with discriminated union
|
||||
createdAt: z.string(),
|
||||
lastActivityAt: z.string().optional(), // Migration: optional for backward compatibility
|
||||
cwd: z.string(),
|
||||
});
|
||||
|
||||
@@ -52,6 +53,11 @@ export class AgentPersistence {
|
||||
try {
|
||||
const validated = PersistedAgentSchema.parse(agent);
|
||||
|
||||
// Migration: if lastActivityAt is missing, default to createdAt
|
||||
if (!validated.lastActivityAt) {
|
||||
validated.lastActivityAt = validated.createdAt;
|
||||
}
|
||||
|
||||
if (
|
||||
validated.options.type === "claude" &&
|
||||
validated.options.sessionId !== null
|
||||
|
||||
@@ -116,6 +116,7 @@ export interface AgentInfo {
|
||||
id: string;
|
||||
status: AgentStatus;
|
||||
createdAt: Date;
|
||||
lastActivityAt: Date;
|
||||
type: "claude";
|
||||
sessionId: string | null;
|
||||
error: string | null;
|
||||
|
||||
@@ -10,6 +10,7 @@ const AgentInfoSchema = z.object({
|
||||
id: z.string(),
|
||||
status: z.string(),
|
||||
createdAt: z.date(),
|
||||
lastActivityAt: z.date(),
|
||||
type: z.literal("claude"),
|
||||
sessionId: z.string().nullable(),
|
||||
error: z.string().nullable(),
|
||||
|
||||
Reference in New Issue
Block a user