mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
refactor(workboard): consolidate card normalization helpers (#129236)
This commit is contained in:
committed by
GitHub
parent
f830b8c159
commit
b1e27db51c
@@ -1437,7 +1437,7 @@ extensions/workboard/src/sqlite-store.ts 44
|
||||
extensions/workboard/src/store-automation.ts 1
|
||||
extensions/workboard/src/store-card-helpers.ts 1
|
||||
extensions/workboard/src/store-core.ts 5
|
||||
extensions/workboard/src/store-normalizers.ts 33
|
||||
extensions/workboard/src/store-normalizers.ts 11
|
||||
extensions/workboard/src/store-workflow.ts 2
|
||||
extensions/workboard/src/store.ts 5
|
||||
extensions/workboard/src/tools.ts 11
|
||||
|
||||
@@ -131,12 +131,25 @@ export function appendEvent(
|
||||
].slice(-MAX_CARD_EVENTS);
|
||||
}
|
||||
|
||||
function latestMetadataIdChanged(
|
||||
existing: readonly { id: string }[] | undefined,
|
||||
next: readonly { id: string }[] | undefined,
|
||||
function metadataEntriesChanged(
|
||||
existing: WorkboardCard,
|
||||
next: WorkboardCard,
|
||||
key:
|
||||
| "comments"
|
||||
| "links"
|
||||
| "proof"
|
||||
| "artifacts"
|
||||
| "attachments"
|
||||
| "workerLogs"
|
||||
| "notifications",
|
||||
): boolean {
|
||||
const latestId = next?.at(-1)?.id;
|
||||
return Boolean(latestId && latestId !== existing?.at(-1)?.id);
|
||||
const previous = existing.metadata?.[key];
|
||||
const current = next.metadata?.[key];
|
||||
const latestId = current?.at(-1)?.id;
|
||||
return (
|
||||
(previous?.length ?? 0) !== (current?.length ?? 0) ||
|
||||
Boolean(latestId && latestId !== previous?.at(-1)?.id)
|
||||
);
|
||||
}
|
||||
|
||||
export function lifecycleStatusSourceUpdatedAtFromPatch(metadata: unknown): number | undefined {
|
||||
@@ -258,34 +271,17 @@ export function updateEvent(
|
||||
...(cardRunId(next) ? { runId: cardRunId(next) } : {}),
|
||||
};
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.comments?.length ?? 0) !== (next.metadata?.comments?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.comments, next.metadata?.comments)
|
||||
) {
|
||||
return { kind: "comment_added" };
|
||||
for (const [key, kind] of [
|
||||
["comments", "comment_added"],
|
||||
["links", "link_added"],
|
||||
["proof", "proof_added"],
|
||||
["artifacts", "artifact_added"],
|
||||
] as const) {
|
||||
if (metadataEntriesChanged(existing, next, key)) {
|
||||
return { kind };
|
||||
}
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.links?.length ?? 0) !== (next.metadata?.links?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.links, next.metadata?.links)
|
||||
) {
|
||||
return { kind: "link_added" };
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.proof?.length ?? 0) !== (next.metadata?.proof?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.proof, next.metadata?.proof)
|
||||
) {
|
||||
return { kind: "proof_added" };
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.artifacts?.length ?? 0) !== (next.metadata?.artifacts?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.artifacts, next.metadata?.artifacts)
|
||||
) {
|
||||
return { kind: "artifact_added" };
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.attachments?.length ?? 0) !== (next.metadata?.attachments?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.attachments, next.metadata?.attachments)
|
||||
) {
|
||||
if (metadataEntriesChanged(existing, next, "attachments")) {
|
||||
return (next.metadata?.attachments?.length ?? 0) > (existing.metadata?.attachments?.length ?? 0)
|
||||
? { kind: "attachment_added" }
|
||||
: { kind: "edited" };
|
||||
@@ -293,20 +289,13 @@ export function updateEvent(
|
||||
if (existing.metadata?.workerProtocol?.state !== next.metadata?.workerProtocol?.state) {
|
||||
return { kind: "orchestration" };
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.workerLogs?.length ?? 0) !== (next.metadata?.workerLogs?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.workerLogs, next.metadata?.workerLogs)
|
||||
) {
|
||||
if (metadataEntriesChanged(existing, next, "workerLogs")) {
|
||||
return { kind: "orchestration" };
|
||||
}
|
||||
if ((existing.metadata?.diagnostics?.length ?? 0) !== (next.metadata?.diagnostics?.length ?? 0)) {
|
||||
return { kind: "diagnostic" };
|
||||
}
|
||||
if (
|
||||
(existing.metadata?.notifications?.length ?? 0) !==
|
||||
(next.metadata?.notifications?.length ?? 0) ||
|
||||
latestMetadataIdChanged(existing.metadata?.notifications, next.metadata?.notifications)
|
||||
) {
|
||||
if (metadataEntriesChanged(existing, next, "notifications")) {
|
||||
return { kind: "notification" };
|
||||
}
|
||||
if (
|
||||
@@ -552,6 +541,19 @@ function cardResultSummary(card: WorkboardCard): string | undefined {
|
||||
);
|
||||
}
|
||||
|
||||
function appendWorkerContextSection<T>(
|
||||
lines: string[],
|
||||
heading: string,
|
||||
entries: readonly T[] | undefined,
|
||||
format: (entry: T) => string,
|
||||
maxEntries = 8,
|
||||
): void {
|
||||
const recent = entries?.slice(-maxEntries) ?? [];
|
||||
if (recent.length) {
|
||||
lines.push("", `## ${heading}`, ...recent.map(format));
|
||||
}
|
||||
}
|
||||
|
||||
export function buildWorkerContext(
|
||||
card: WorkboardCard,
|
||||
cards: readonly WorkboardCard[] = [],
|
||||
@@ -567,85 +569,69 @@ export function buildWorkerContext(
|
||||
if (card.notes) {
|
||||
lines.push("", "## Notes", capText(card.notes, 4000) ?? "");
|
||||
}
|
||||
const attempts = card.metadata?.attempts?.slice(-8) ?? [];
|
||||
if (attempts.length) {
|
||||
lines.push("", "## Recent attempts");
|
||||
for (const attempt of attempts) {
|
||||
lines.push(
|
||||
`- ${attempt.status} ${attempt.model ?? ""} ${attempt.error ? `error=${capText(attempt.error, 240)}` : ""}`.trim(),
|
||||
);
|
||||
}
|
||||
}
|
||||
const comments = card.metadata?.comments?.slice(-12) ?? [];
|
||||
if (comments.length) {
|
||||
lines.push("", "## Recent comments");
|
||||
for (const comment of comments) {
|
||||
lines.push(`- ${capText(comment.body, 400)}`);
|
||||
}
|
||||
}
|
||||
const proof = card.metadata?.proof?.slice(-8) ?? [];
|
||||
if (proof.length) {
|
||||
lines.push("", "## Proof");
|
||||
for (const entry of proof) {
|
||||
lines.push(
|
||||
`- ${entry.status}: ${capText(entry.label ?? entry.command ?? entry.url ?? entry.note, 400)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const artifacts = card.metadata?.artifacts?.slice(-8) ?? [];
|
||||
if (artifacts.length) {
|
||||
lines.push("", "## Artifacts");
|
||||
for (const artifact of artifacts) {
|
||||
lines.push(`- ${capText(artifact.label ?? artifact.url ?? artifact.path, 400)}`);
|
||||
}
|
||||
}
|
||||
const attachments = card.metadata?.attachments?.slice(-8) ?? [];
|
||||
if (attachments.length) {
|
||||
lines.push("", "## Attachments");
|
||||
for (const attachment of attachments) {
|
||||
const detail = [
|
||||
attachment.fileName,
|
||||
`${attachment.byteSize} bytes`,
|
||||
attachment.mimeType,
|
||||
attachment.note,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · ");
|
||||
lines.push(`- ${capText(detail, 500)}`);
|
||||
}
|
||||
}
|
||||
appendWorkerContextSection(lines, "Recent attempts", card.metadata?.attempts, (attempt) =>
|
||||
`- ${attempt.status} ${attempt.model ?? ""} ${attempt.error ? `error=${capText(attempt.error, 240)}` : ""}`.trim(),
|
||||
);
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Recent comments",
|
||||
card.metadata?.comments,
|
||||
(comment) => `- ${capText(comment.body, 400)}`,
|
||||
12,
|
||||
);
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Proof",
|
||||
card.metadata?.proof,
|
||||
(entry) =>
|
||||
`- ${entry.status}: ${capText(entry.label ?? entry.command ?? entry.url ?? entry.note, 400)}`,
|
||||
);
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Artifacts",
|
||||
card.metadata?.artifacts,
|
||||
(artifact) => `- ${capText(artifact.label ?? artifact.url ?? artifact.path, 400)}`,
|
||||
);
|
||||
appendWorkerContextSection(lines, "Attachments", card.metadata?.attachments, (attachment) => {
|
||||
const detail = [
|
||||
attachment.fileName,
|
||||
`${attachment.byteSize} bytes`,
|
||||
attachment.mimeType,
|
||||
attachment.note,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · ");
|
||||
return `- ${capText(detail, 500)}`;
|
||||
});
|
||||
if (card.metadata?.workerProtocol) {
|
||||
const protocol = card.metadata.workerProtocol;
|
||||
lines.push("", "## Worker protocol");
|
||||
lines.push(`${protocol.state}: ${capText(protocol.detail, 500) ?? "no detail"}`);
|
||||
}
|
||||
const workerLogs = card.metadata?.workerLogs?.slice(-8) ?? [];
|
||||
if (workerLogs.length) {
|
||||
lines.push("", "## Worker logs");
|
||||
for (const log of workerLogs) {
|
||||
lines.push(`- ${log.level}: ${capText(log.message, 500)}`);
|
||||
}
|
||||
}
|
||||
const links = card.metadata?.links?.slice(-8) ?? [];
|
||||
if (links.length) {
|
||||
lines.push("", "## Links");
|
||||
for (const link of links) {
|
||||
lines.push(`- ${link.type}: ${link.title ?? link.url ?? link.targetCardId ?? ""}`);
|
||||
}
|
||||
}
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Worker logs",
|
||||
card.metadata?.workerLogs,
|
||||
(log) => `- ${log.level}: ${capText(log.message, 500)}`,
|
||||
);
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Links",
|
||||
card.metadata?.links,
|
||||
(link) => `- ${link.type}: ${link.title ?? link.url ?? link.targetCardId ?? ""}`,
|
||||
);
|
||||
const cardsById = new Map(cards.map((entry) => [entry.id, entry]));
|
||||
const parentResults = cardParentIds(card)
|
||||
.map((parentId) => cardsById.get(parentId))
|
||||
.filter((parent): parent is WorkboardCard => parent !== undefined && parent.status === "done")
|
||||
.slice(-6);
|
||||
if (parentResults.length) {
|
||||
lines.push("", "## Parent results");
|
||||
for (const parent of parentResults) {
|
||||
lines.push(
|
||||
`- ${parent.id} ${parent.title}: ${capText(cardResultSummary(parent), 500) ?? "done"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Parent results",
|
||||
parentResults,
|
||||
(parent) =>
|
||||
`- ${parent.id} ${parent.title}: ${capText(cardResultSummary(parent), 500) ?? "done"}`,
|
||||
);
|
||||
const recentAgentWork =
|
||||
card.agentId && cards.length
|
||||
? cards
|
||||
@@ -659,14 +645,12 @@ export function buildWorkerContext(
|
||||
.toSorted((a, b) => b.updatedAt - a.updatedAt)
|
||||
.slice(0, 5)
|
||||
: [];
|
||||
if (recentAgentWork.length) {
|
||||
lines.push("", `## Recent done work by ${card.agentId}`);
|
||||
for (const entry of recentAgentWork) {
|
||||
lines.push(
|
||||
`- ${entry.id} ${entry.title}: ${capText(cardResultSummary(entry), 300) ?? "done"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
`Recent done work by ${card.agentId}`,
|
||||
recentAgentWork,
|
||||
(entry) => `- ${entry.id} ${entry.title}: ${capText(cardResultSummary(entry), 300) ?? "done"}`,
|
||||
);
|
||||
const automation = card.metadata?.automation;
|
||||
if (automation) {
|
||||
lines.push("", "## Automation");
|
||||
@@ -689,12 +673,13 @@ export function buildWorkerContext(
|
||||
}
|
||||
}
|
||||
const diagnostics = computeCardDiagnostics(card, Date.now());
|
||||
if (diagnostics.length) {
|
||||
lines.push("", "## Active diagnostics");
|
||||
for (const entry of diagnostics) {
|
||||
lines.push(`- ${entry.severity}: ${entry.title}`);
|
||||
}
|
||||
}
|
||||
appendWorkerContextSection(
|
||||
lines,
|
||||
"Active diagnostics",
|
||||
diagnostics,
|
||||
(entry) => `- ${entry.severity}: ${entry.title}`,
|
||||
diagnostics.length,
|
||||
);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
WORKBOARD_TEMPLATE_IDS,
|
||||
type WorkboardArtifact,
|
||||
type WorkboardAttachment,
|
||||
type WorkboardAttemptStatus,
|
||||
type WorkboardAutomation,
|
||||
type WorkboardBoardMetadata,
|
||||
type WorkboardClaim,
|
||||
@@ -23,12 +22,9 @@ import {
|
||||
type WorkboardDiagnostic,
|
||||
type WorkboardDiagnosticAction,
|
||||
type WorkboardDiagnosticKind,
|
||||
type WorkboardDiagnosticSeverity,
|
||||
type WorkboardEvent,
|
||||
type WorkboardEventKind,
|
||||
type WorkboardExecution,
|
||||
type WorkboardExecutionMode,
|
||||
type WorkboardExecutionStatus,
|
||||
type WorkboardLink,
|
||||
type WorkboardLinkType,
|
||||
type WorkboardLaunchState,
|
||||
@@ -39,7 +35,6 @@ import {
|
||||
type WorkboardOrchestrationSettings,
|
||||
type WorkboardPriority,
|
||||
type WorkboardProof,
|
||||
type WorkboardProofStatus,
|
||||
type WorkboardRunAttempt,
|
||||
type WorkboardStatus,
|
||||
type WorkboardTemplateId,
|
||||
@@ -550,69 +545,20 @@ export function deriveChildIdempotencyKey(
|
||||
return key.length <= 160 ? key : undefined;
|
||||
}
|
||||
|
||||
function normalizeExecutionMode(
|
||||
function normalizeEnumValue<T extends string, TFallback extends T | undefined>(
|
||||
value: unknown,
|
||||
fallback: WorkboardExecutionMode,
|
||||
): WorkboardExecutionMode {
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
WORKBOARD_EXECUTION_MODES.includes(value as WorkboardExecutionMode)
|
||||
) {
|
||||
return value as WorkboardExecutionMode;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function normalizeExecutionStatus(
|
||||
value: unknown,
|
||||
fallback: WorkboardExecutionStatus,
|
||||
): WorkboardExecutionStatus {
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
WORKBOARD_EXECUTION_STATUSES.includes(value as WorkboardExecutionStatus)
|
||||
) {
|
||||
return value as WorkboardExecutionStatus;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function normalizeAttemptStatus(
|
||||
value: unknown,
|
||||
fallback: WorkboardAttemptStatus,
|
||||
): WorkboardAttemptStatus {
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
WORKBOARD_ATTEMPT_STATUSES.includes(value as WorkboardAttemptStatus)
|
||||
) {
|
||||
return value as WorkboardAttemptStatus;
|
||||
}
|
||||
return fallback;
|
||||
allowed: readonly T[],
|
||||
fallback: TFallback,
|
||||
): T | TFallback {
|
||||
return typeof value === "string" && allowed.includes(value as T) ? (value as T) : fallback;
|
||||
}
|
||||
|
||||
export function normalizeLinkType(value: unknown, fallback: WorkboardLinkType): WorkboardLinkType {
|
||||
if (typeof value === "string" && WORKBOARD_LINK_TYPES.includes(value as WorkboardLinkType)) {
|
||||
return value as WorkboardLinkType;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function normalizeProofStatus(
|
||||
value: unknown,
|
||||
fallback: WorkboardProofStatus,
|
||||
): WorkboardProofStatus {
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
WORKBOARD_PROOF_STATUSES.includes(value as WorkboardProofStatus)
|
||||
) {
|
||||
return value as WorkboardProofStatus;
|
||||
}
|
||||
return fallback;
|
||||
return normalizeEnumValue(value, WORKBOARD_LINK_TYPES, fallback);
|
||||
}
|
||||
|
||||
export function normalizeTemplateId(value: unknown): WorkboardTemplateId | undefined {
|
||||
return typeof value === "string" && WORKBOARD_TEMPLATE_IDS.includes(value as WorkboardTemplateId)
|
||||
? (value as WorkboardTemplateId)
|
||||
: undefined;
|
||||
return normalizeEnumValue(value, WORKBOARD_TEMPLATE_IDS, undefined);
|
||||
}
|
||||
|
||||
export function normalizeTimestamp(value: unknown, fallback: number): number {
|
||||
@@ -627,23 +573,13 @@ function normalizeEvent(value: unknown): WorkboardEvent | null {
|
||||
}
|
||||
const record = value;
|
||||
const id = normalizeOptionalString(record.id);
|
||||
const kind = WORKBOARD_EVENT_KINDS.includes(record.kind as WorkboardEventKind)
|
||||
? (record.kind as WorkboardEventKind)
|
||||
: null;
|
||||
const kind = normalizeEnumValue(record.kind, WORKBOARD_EVENT_KINDS, undefined);
|
||||
const at = normalizeTimestamp(record.at, 0);
|
||||
if (!id || !kind || !at) {
|
||||
return null;
|
||||
}
|
||||
const fromStatus =
|
||||
typeof record.fromStatus === "string" &&
|
||||
WORKBOARD_STATUSES.includes(record.fromStatus as WorkboardStatus)
|
||||
? (record.fromStatus as WorkboardStatus)
|
||||
: undefined;
|
||||
const toStatus =
|
||||
typeof record.toStatus === "string" &&
|
||||
WORKBOARD_STATUSES.includes(record.toStatus as WorkboardStatus)
|
||||
? (record.toStatus as WorkboardStatus)
|
||||
: undefined;
|
||||
const fromStatus = normalizeEnumValue(record.fromStatus, WORKBOARD_STATUSES, undefined);
|
||||
const toStatus = normalizeEnumValue(record.toStatus, WORKBOARD_STATUSES, undefined);
|
||||
const sessionKey = normalizeOptionalString(record.sessionKey);
|
||||
const runId = normalizeOptionalString(record.runId);
|
||||
return {
|
||||
@@ -685,7 +621,7 @@ function normalizeAttempt(value: unknown): WorkboardRunAttempt | null {
|
||||
const model = normalizeBoundedString(record.model, undefined, 160, "attempt model");
|
||||
return {
|
||||
id,
|
||||
status: normalizeAttemptStatus(record.status, "running"),
|
||||
status: normalizeEnumValue(record.status, WORKBOARD_ATTEMPT_STATUSES, "running"),
|
||||
startedAt,
|
||||
...(endedAt ? { endedAt } : {}),
|
||||
...(engine ? { engine } : {}),
|
||||
@@ -761,7 +697,7 @@ function normalizeProof(value: unknown): WorkboardProof | null {
|
||||
const note = normalizeBoundedString(record.note, undefined, 2000, "proof note");
|
||||
return {
|
||||
id,
|
||||
status: normalizeProofStatus(record.status, "unknown"),
|
||||
status: normalizeEnumValue(record.status, WORKBOARD_PROOF_STATUSES, "unknown"),
|
||||
createdAt,
|
||||
...(label ? { label } : {}),
|
||||
...(command ? { command } : {}),
|
||||
@@ -858,14 +794,11 @@ function normalizeWorkerProtocol(
|
||||
return fallback;
|
||||
}
|
||||
const record = value;
|
||||
const state =
|
||||
record.state === "idle" ||
|
||||
record.state === "running" ||
|
||||
record.state === "completed" ||
|
||||
record.state === "blocked" ||
|
||||
record.state === "violated"
|
||||
? record.state
|
||||
: fallback?.state;
|
||||
const state = normalizeEnumValue(
|
||||
record.state,
|
||||
["idle", "running", "completed", "blocked", "violated"],
|
||||
fallback?.state,
|
||||
);
|
||||
if (!state) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -970,14 +903,8 @@ function normalizeDiagnostic(value: unknown): WorkboardDiagnostic | null {
|
||||
return null;
|
||||
}
|
||||
const record = value;
|
||||
const kind = WORKBOARD_DIAGNOSTIC_KINDS.includes(record.kind as WorkboardDiagnosticKind)
|
||||
? (record.kind as WorkboardDiagnosticKind)
|
||||
: undefined;
|
||||
const severity = WORKBOARD_DIAGNOSTIC_SEVERITIES.includes(
|
||||
record.severity as WorkboardDiagnosticSeverity,
|
||||
)
|
||||
? (record.severity as WorkboardDiagnosticSeverity)
|
||||
: "warning";
|
||||
const kind = normalizeEnumValue(record.kind, WORKBOARD_DIAGNOSTIC_KINDS, undefined);
|
||||
const severity = normalizeEnumValue(record.severity, WORKBOARD_DIAGNOSTIC_SEVERITIES, "warning");
|
||||
const title = normalizeBoundedString(record.title, undefined, 160, "diagnostic title");
|
||||
const detail = normalizeBoundedString(record.detail, undefined, 800, "diagnostic detail");
|
||||
const firstSeenAt = normalizeTimestamp(record.firstSeenAt, Date.now());
|
||||
@@ -1011,9 +938,7 @@ function normalizeNotification(value: unknown): WorkboardNotification | null {
|
||||
}
|
||||
const record = value;
|
||||
const id = normalizeOptionalString(record.id) ?? randomUUID();
|
||||
const kind = WORKBOARD_NOTIFICATION_KINDS.includes(record.kind as WorkboardNotificationKind)
|
||||
? (record.kind as WorkboardNotificationKind)
|
||||
: undefined;
|
||||
const kind = normalizeEnumValue(record.kind, WORKBOARD_NOTIFICATION_KINDS, undefined);
|
||||
const createdAt = normalizeTimestamp(record.createdAt, Date.now());
|
||||
const sequence = normalizeTimestamp(record.sequence, 0) || undefined;
|
||||
const message = normalizeBoundedString(record.message, undefined, 240, "notification message");
|
||||
@@ -1040,7 +965,7 @@ export function normalizeProofInput(input: WorkboardProofInput, now: number): Wo
|
||||
const note = normalizeBoundedString(input.note, undefined, 2000, "proof note");
|
||||
return {
|
||||
id: randomUUID(),
|
||||
status: normalizeProofStatus(input.status, "unknown"),
|
||||
status: normalizeEnumValue(input.status, WORKBOARD_PROOF_STATUSES, "unknown"),
|
||||
createdAt: now,
|
||||
...(label ? { label } : {}),
|
||||
...(command ? { command } : {}),
|
||||
@@ -1087,6 +1012,20 @@ export function appendCompletionProof(
|
||||
return entries.slice(-MAX_CARD_PROOF);
|
||||
}
|
||||
|
||||
function normalizeList<T>(
|
||||
value: unknown,
|
||||
normalize: (entry: unknown) => T | null,
|
||||
limit: number,
|
||||
fallback?: T[],
|
||||
): T[] | undefined {
|
||||
return Array.isArray(value)
|
||||
? value
|
||||
.map(normalize)
|
||||
.filter((entry): entry is T => entry !== null)
|
||||
.slice(-limit)
|
||||
: fallback;
|
||||
}
|
||||
|
||||
export function normalizeMetadata(
|
||||
value: unknown,
|
||||
fallback: WorkboardMetadata = {},
|
||||
@@ -1131,43 +1070,38 @@ export function normalizeMetadata(
|
||||
})()
|
||||
: links.slice(-MAX_CARD_LINKS);
|
||||
const normalized = {
|
||||
attempts: Array.isArray(record.attempts)
|
||||
? record.attempts
|
||||
.map(normalizeAttempt)
|
||||
.filter((attempt): attempt is WorkboardRunAttempt => attempt !== null)
|
||||
.slice(-MAX_CARD_ATTEMPTS)
|
||||
: fallback.attempts,
|
||||
comments: Array.isArray(record.comments)
|
||||
? record.comments
|
||||
.map(normalizeComment)
|
||||
.filter((comment): comment is WorkboardComment => comment !== null)
|
||||
.slice(-MAX_CARD_COMMENTS)
|
||||
: fallback.comments,
|
||||
attempts: normalizeList(
|
||||
record.attempts,
|
||||
normalizeAttempt,
|
||||
MAX_CARD_ATTEMPTS,
|
||||
fallback.attempts,
|
||||
),
|
||||
comments: normalizeList(
|
||||
record.comments,
|
||||
normalizeComment,
|
||||
MAX_CARD_COMMENTS,
|
||||
fallback.comments,
|
||||
),
|
||||
links: normalizedLinks,
|
||||
proof: Array.isArray(record.proof)
|
||||
? record.proof
|
||||
.map(normalizeProof)
|
||||
.filter((proof): proof is WorkboardProof => proof !== null)
|
||||
.slice(-MAX_CARD_PROOF)
|
||||
: fallback.proof,
|
||||
artifacts: Array.isArray(record.artifacts)
|
||||
? record.artifacts
|
||||
.map(normalizeArtifact)
|
||||
.filter((artifact): artifact is WorkboardArtifact => artifact !== null)
|
||||
.slice(-MAX_CARD_ARTIFACTS)
|
||||
: fallback.artifacts,
|
||||
attachments: Array.isArray(record.attachments)
|
||||
? record.attachments
|
||||
.map(normalizeAttachment)
|
||||
.filter((attachment): attachment is WorkboardAttachment => attachment !== null)
|
||||
.slice(-MAX_CARD_ATTACHMENTS)
|
||||
: fallback.attachments,
|
||||
workerLogs: Array.isArray(record.workerLogs)
|
||||
? record.workerLogs
|
||||
.map(normalizeWorkerLog)
|
||||
.filter((log): log is WorkboardWorkerLog => log !== null)
|
||||
.slice(-MAX_CARD_WORKER_LOGS)
|
||||
: fallback.workerLogs,
|
||||
proof: normalizeList(record.proof, normalizeProof, MAX_CARD_PROOF, fallback.proof),
|
||||
artifacts: normalizeList(
|
||||
record.artifacts,
|
||||
normalizeArtifact,
|
||||
MAX_CARD_ARTIFACTS,
|
||||
fallback.artifacts,
|
||||
),
|
||||
attachments: normalizeList(
|
||||
record.attachments,
|
||||
normalizeAttachment,
|
||||
MAX_CARD_ATTACHMENTS,
|
||||
fallback.attachments,
|
||||
),
|
||||
workerLogs: normalizeList(
|
||||
record.workerLogs,
|
||||
normalizeWorkerLog,
|
||||
MAX_CARD_WORKER_LOGS,
|
||||
fallback.workerLogs,
|
||||
),
|
||||
workerProtocol: Object.hasOwn(record, "workerProtocol")
|
||||
? normalizeWorkerProtocol(record.workerProtocol, fallback.workerProtocol)
|
||||
: fallback.workerProtocol,
|
||||
@@ -1181,20 +1115,18 @@ export function normalizeMetadata(
|
||||
? normalizeClaim(record.claim, fallback.claim)
|
||||
: undefined
|
||||
: fallback.claim,
|
||||
diagnostics: Array.isArray(record.diagnostics)
|
||||
? record.diagnostics
|
||||
.map(normalizeDiagnostic)
|
||||
.filter(
|
||||
(diagnosticLocal): diagnosticLocal is WorkboardDiagnostic => diagnosticLocal !== null,
|
||||
)
|
||||
.slice(-MAX_CARD_DIAGNOSTICS)
|
||||
: fallback.diagnostics,
|
||||
notifications: Array.isArray(record.notifications)
|
||||
? record.notifications
|
||||
.map(normalizeNotification)
|
||||
.filter((notification): notification is WorkboardNotification => notification !== null)
|
||||
.slice(-MAX_CARD_NOTIFICATIONS)
|
||||
: fallback.notifications,
|
||||
diagnostics: normalizeList(
|
||||
record.diagnostics,
|
||||
normalizeDiagnostic,
|
||||
MAX_CARD_DIAGNOSTICS,
|
||||
fallback.diagnostics,
|
||||
),
|
||||
notifications: normalizeList(
|
||||
record.notifications,
|
||||
normalizeNotification,
|
||||
MAX_CARD_NOTIFICATIONS,
|
||||
fallback.notifications,
|
||||
),
|
||||
templateId: normalizeTemplateId(record.templateId) ?? fallback.templateId,
|
||||
archivedAt: hasArchivedAt
|
||||
? normalizeTimestamp(record.archivedAt, 0) || undefined
|
||||
@@ -1242,8 +1174,8 @@ export function normalizeExecution(value: unknown): WorkboardExecution | undefin
|
||||
return {
|
||||
id,
|
||||
kind: "agent-session",
|
||||
mode: normalizeExecutionMode(record.mode, "autonomous"),
|
||||
status: normalizeExecutionStatus(record.status, "idle"),
|
||||
mode: normalizeEnumValue(record.mode, WORKBOARD_EXECUTION_MODES, "autonomous"),
|
||||
status: normalizeEnumValue(record.status, WORKBOARD_EXECUTION_STATUSES, "idle"),
|
||||
startedAt,
|
||||
updatedAt,
|
||||
...(engine ? { engine } : {}),
|
||||
@@ -1269,17 +1201,10 @@ export function syncExecutionSessionKey(
|
||||
|
||||
function removeUndefinedExecutionFields(execution: WorkboardExecution): WorkboardExecution {
|
||||
const next = { ...execution };
|
||||
if (next.engine === undefined) {
|
||||
delete next.engine;
|
||||
}
|
||||
if (next.model === undefined) {
|
||||
delete next.model;
|
||||
}
|
||||
if (next.sessionKey === undefined) {
|
||||
delete next.sessionKey;
|
||||
}
|
||||
if (next.runId === undefined) {
|
||||
delete next.runId;
|
||||
for (const key of ["engine", "model", "sessionKey", "runId"] as const) {
|
||||
if (next[key] === undefined) {
|
||||
delete next[key];
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user