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,
|
||||
PersistedAgentDescriptor,
|
||||
} from "./agent-sdk-types.js";
|
||||
import { buildArchivedAgentRecord, type ArchivedStoredAgentRecord } from "./agent-archive.js";
|
||||
import type { StoredAgentRecord, AgentStorage } from "./agent-storage.js";
|
||||
import {
|
||||
InMemoryAgentTimelineStore,
|
||||
@@ -66,7 +67,6 @@ const STORED_AGENT_CAPABILITIES: AgentCapabilityFlags = {
|
||||
};
|
||||
|
||||
type TimeoutResult = "completed" | "timed_out";
|
||||
type ArchivedStoredAgentRecord = StoredAgentRecord & { archivedAt: string };
|
||||
|
||||
interface TimeoutOptions {
|
||||
operation: Promise<void>;
|
||||
@@ -1071,19 +1071,7 @@ export class AgentManager {
|
||||
private async markRecordArchived(record: StoredAgentRecord): Promise<ArchivedStoredAgentRecord> {
|
||||
const registry = this.requireRegistry();
|
||||
const archivedAt = new Date().toISOString();
|
||||
const normalizedStatus =
|
||||
record.lastStatus === "running" || record.lastStatus === "initializing"
|
||||
? "idle"
|
||||
: record.lastStatus;
|
||||
const archivedRecord: ArchivedStoredAgentRecord = {
|
||||
...record,
|
||||
archivedAt,
|
||||
updatedAt: archivedAt,
|
||||
lastStatus: normalizedStatus,
|
||||
requiresAttention: false,
|
||||
attentionReason: null,
|
||||
attentionTimestamp: null,
|
||||
};
|
||||
const archivedRecord = buildArchivedAgentRecord(record, { archivedAt, updatedAt: archivedAt });
|
||||
|
||||
await registry.upsert(archivedRecord);
|
||||
|
||||
@@ -1259,19 +1247,7 @@ export class AgentManager {
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
}
|
||||
|
||||
const normalizedStatus =
|
||||
record.lastStatus === "running" || record.lastStatus === "initializing"
|
||||
? "idle"
|
||||
: record.lastStatus;
|
||||
|
||||
const nextRecord: StoredAgentRecord = {
|
||||
...record,
|
||||
archivedAt,
|
||||
lastStatus: normalizedStatus,
|
||||
requiresAttention: false,
|
||||
attentionReason: null,
|
||||
attentionTimestamp: null,
|
||||
};
|
||||
const nextRecord = buildArchivedAgentRecord(record, { archivedAt });
|
||||
await registry.upsert(nextRecord);
|
||||
|
||||
await this.archiveNativeSessionBestEffort(record.provider, record.persistence);
|
||||
|
||||
Reference in New Issue
Block a user