Extract agent archive projection (#877)
This commit is contained in:
@@ -0,0 +1,70 @@
|
|||||||
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
import { buildArchivedAgentRecord } from "./agent-archive.js";
|
||||||
|
import type { StoredAgentRecord } from "./agent-storage.js";
|
||||||
|
|
||||||
|
const BASE_RECORD: StoredAgentRecord = {
|
||||||
|
id: "agent-1",
|
||||||
|
provider: "codex",
|
||||||
|
cwd: "/workspace/project",
|
||||||
|
createdAt: "2025-01-01T00:00:00.000Z",
|
||||||
|
updatedAt: "2025-01-02T00:00:00.000Z",
|
||||||
|
labels: {},
|
||||||
|
lastStatus: "idle",
|
||||||
|
config: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
test("archives a stored agent without changing terminal statuses", () => {
|
||||||
|
const statuses: Array<StoredAgentRecord["lastStatus"]> = ["idle", "error", "closed"];
|
||||||
|
|
||||||
|
for (const status of statuses) {
|
||||||
|
const archived = buildArchivedAgentRecord(
|
||||||
|
{ ...BASE_RECORD, lastStatus: status },
|
||||||
|
{ archivedAt: "2025-01-03T00:00:00.000Z" },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(archived.lastStatus).toBe(status);
|
||||||
|
expect(archived.archivedAt).toBe("2025-01-03T00:00:00.000Z");
|
||||||
|
expect(archived.updatedAt).toBe(BASE_RECORD.updatedAt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("archives busy stored agents as idle", () => {
|
||||||
|
const statuses: Array<StoredAgentRecord["lastStatus"]> = ["initializing", "running"];
|
||||||
|
|
||||||
|
for (const status of statuses) {
|
||||||
|
const archived = buildArchivedAgentRecord(
|
||||||
|
{ ...BASE_RECORD, lastStatus: status },
|
||||||
|
{ archivedAt: "2025-01-03T00:00:00.000Z" },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(archived.lastStatus).toBe("idle");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("clears persisted attention when archiving", () => {
|
||||||
|
const archived = buildArchivedAgentRecord(
|
||||||
|
{
|
||||||
|
...BASE_RECORD,
|
||||||
|
requiresAttention: true,
|
||||||
|
attentionReason: "finished",
|
||||||
|
attentionTimestamp: "2025-01-02T12:00:00.000Z",
|
||||||
|
},
|
||||||
|
{ archivedAt: "2025-01-03T00:00:00.000Z" },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(archived).toMatchObject({
|
||||||
|
requiresAttention: false,
|
||||||
|
attentionReason: null,
|
||||||
|
attentionTimestamp: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can stamp updatedAt to the archive timestamp", () => {
|
||||||
|
const archived = buildArchivedAgentRecord(BASE_RECORD, {
|
||||||
|
archivedAt: "2025-01-03T00:00:00.000Z",
|
||||||
|
updatedAt: "2025-01-03T00:00:00.000Z",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(archived.updatedAt).toBe("2025-01-03T00:00:00.000Z");
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import type { StoredAgentRecord } from "./agent-storage.js";
|
||||||
|
|
||||||
|
export type ArchivedStoredAgentRecord = StoredAgentRecord & { archivedAt: string };
|
||||||
|
|
||||||
|
interface BuildArchivedAgentRecordOptions {
|
||||||
|
archivedAt?: string;
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildArchivedAgentRecord(
|
||||||
|
record: StoredAgentRecord,
|
||||||
|
options?: BuildArchivedAgentRecordOptions,
|
||||||
|
): ArchivedStoredAgentRecord {
|
||||||
|
const archivedAt = options?.archivedAt ?? new Date().toISOString();
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
archivedAt,
|
||||||
|
updatedAt: options?.updatedAt ?? record.updatedAt,
|
||||||
|
lastStatus: normalizeArchivedStatus(record.lastStatus),
|
||||||
|
requiresAttention: false,
|
||||||
|
attentionReason: null,
|
||||||
|
attentionTimestamp: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeArchivedStatus(
|
||||||
|
status: StoredAgentRecord["lastStatus"],
|
||||||
|
): StoredAgentRecord["lastStatus"] {
|
||||||
|
return status === "running" || status === "initializing" ? "idle" : status;
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ import type {
|
|||||||
ListPersistedAgentsOptions,
|
ListPersistedAgentsOptions,
|
||||||
PersistedAgentDescriptor,
|
PersistedAgentDescriptor,
|
||||||
} from "./agent-sdk-types.js";
|
} from "./agent-sdk-types.js";
|
||||||
|
import { buildArchivedAgentRecord, type ArchivedStoredAgentRecord } from "./agent-archive.js";
|
||||||
import type { StoredAgentRecord, AgentStorage } from "./agent-storage.js";
|
import type { StoredAgentRecord, AgentStorage } from "./agent-storage.js";
|
||||||
import {
|
import {
|
||||||
InMemoryAgentTimelineStore,
|
InMemoryAgentTimelineStore,
|
||||||
@@ -66,7 +67,6 @@ const STORED_AGENT_CAPABILITIES: AgentCapabilityFlags = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type TimeoutResult = "completed" | "timed_out";
|
type TimeoutResult = "completed" | "timed_out";
|
||||||
type ArchivedStoredAgentRecord = StoredAgentRecord & { archivedAt: string };
|
|
||||||
|
|
||||||
interface TimeoutOptions {
|
interface TimeoutOptions {
|
||||||
operation: Promise<void>;
|
operation: Promise<void>;
|
||||||
@@ -1071,19 +1071,7 @@ export class AgentManager {
|
|||||||
private async markRecordArchived(record: StoredAgentRecord): Promise<ArchivedStoredAgentRecord> {
|
private async markRecordArchived(record: StoredAgentRecord): Promise<ArchivedStoredAgentRecord> {
|
||||||
const registry = this.requireRegistry();
|
const registry = this.requireRegistry();
|
||||||
const archivedAt = new Date().toISOString();
|
const archivedAt = new Date().toISOString();
|
||||||
const normalizedStatus =
|
const archivedRecord = buildArchivedAgentRecord(record, { archivedAt, updatedAt: archivedAt });
|
||||||
record.lastStatus === "running" || record.lastStatus === "initializing"
|
|
||||||
? "idle"
|
|
||||||
: record.lastStatus;
|
|
||||||
const archivedRecord: ArchivedStoredAgentRecord = {
|
|
||||||
...record,
|
|
||||||
archivedAt,
|
|
||||||
updatedAt: archivedAt,
|
|
||||||
lastStatus: normalizedStatus,
|
|
||||||
requiresAttention: false,
|
|
||||||
attentionReason: null,
|
|
||||||
attentionTimestamp: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
await registry.upsert(archivedRecord);
|
await registry.upsert(archivedRecord);
|
||||||
|
|
||||||
@@ -1259,19 +1247,7 @@ export class AgentManager {
|
|||||||
throw new Error(`Agent not found: ${agentId}`);
|
throw new Error(`Agent not found: ${agentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizedStatus =
|
const nextRecord = buildArchivedAgentRecord(record, { archivedAt });
|
||||||
record.lastStatus === "running" || record.lastStatus === "initializing"
|
|
||||||
? "idle"
|
|
||||||
: record.lastStatus;
|
|
||||||
|
|
||||||
const nextRecord: StoredAgentRecord = {
|
|
||||||
...record,
|
|
||||||
archivedAt,
|
|
||||||
lastStatus: normalizedStatus,
|
|
||||||
requiresAttention: false,
|
|
||||||
attentionReason: null,
|
|
||||||
attentionTimestamp: null,
|
|
||||||
};
|
|
||||||
await registry.upsert(nextRecord);
|
await registry.upsert(nextRecord);
|
||||||
|
|
||||||
await this.archiveNativeSessionBestEffort(record.provider, record.persistence);
|
await this.archiveNativeSessionBestEffort(record.provider, record.persistence);
|
||||||
|
|||||||
Reference in New Issue
Block a user