feat: persist Skill Workshop proposal origin

This commit is contained in:
Shakker
2026-05-31 18:46:41 +01:00
committed by Shakker
parent caa08a6dc0
commit 7681b95199
4 changed files with 62 additions and 0 deletions

View File

@@ -568,6 +568,16 @@ const SkillProposalTargetSchema = Type.Object(
{ additionalProperties: false },
);
const SkillProposalOriginSchema = Type.Object(
{
agentId: Type.Optional(NonEmptyString),
sessionKey: Type.Optional(NonEmptyString),
runId: Type.Optional(NonEmptyString),
messageId: Type.Optional(NonEmptyString),
},
{ additionalProperties: false },
);
const SkillProposalRecordSchema = Type.Object(
{
schema: Type.Literal("openclaw.skill-workshop.proposal.v1"),
@@ -579,6 +589,7 @@ const SkillProposalRecordSchema = Type.Object(
createdAt: NonEmptyString,
updatedAt: NonEmptyString,
createdBy: SkillProposalSourceSchema,
origin: Type.Optional(SkillProposalOriginSchema),
proposedVersion: NonEmptyString,
draftFile: Type.Literal("PROPOSAL.md"),
draftHash: NonEmptyString,

View File

@@ -49,6 +49,7 @@ import {
type SkillProposalActionInput,
type SkillProposalApplyResult,
type SkillProposalCreateInput,
type SkillProposalOrigin,
type SkillProposalManifest,
type SkillProposalReadResult,
type SkillProposalRecord,
@@ -161,6 +162,24 @@ function decodeProposalTextFile(buffer: Buffer, label: string): string {
return content;
}
function normalizeProposalOrigin(
origin: SkillProposalOrigin | undefined,
): SkillProposalOrigin | undefined {
const agentId = normalizeOptionalString(origin?.agentId);
const sessionKey = normalizeOptionalString(origin?.sessionKey);
const runId = normalizeOptionalString(origin?.runId);
const messageId = normalizeOptionalString(origin?.messageId);
if (!agentId && !sessionKey && !runId && !messageId) {
return undefined;
}
return {
...(agentId ? { agentId } : {}),
...(sessionKey ? { sessionKey } : {}),
...(runId ? { runId } : {}),
...(messageId ? { messageId } : {}),
};
}
export async function inspectSkillProposal(
proposalId: string,
options: SkillProposalScopeOptions = {},
@@ -242,6 +261,7 @@ export async function proposeCreateSkill(
const id = createSkillProposalId(name);
const goal = normalizeOptionalString(input.goal);
const evidence = normalizeOptionalString(input.evidence);
const origin = normalizeProposalOrigin(input.origin);
const record: SkillProposalRecord = {
schema: SKILL_WORKSHOP_SCHEMA,
id,
@@ -252,6 +272,7 @@ export async function proposeCreateSkill(
createdAt: now,
updatedAt: now,
createdBy: input.createdBy ?? "skill-workshop",
...(origin ? { origin } : {}),
proposedVersion: "v1",
draftFile: "PROPOSAL.md",
draftHash: hashSkillProposalContent(proposalContent),
@@ -313,6 +334,7 @@ export async function proposeUpdateSkill(
const id = createSkillProposalId(targetSkill.skillKey || targetSkill.name);
const goal = normalizeOptionalString(input.goal);
const evidence = normalizeOptionalString(input.evidence);
const origin = normalizeProposalOrigin(input.origin);
const record: SkillProposalRecord = {
schema: SKILL_WORKSHOP_SCHEMA,
id,
@@ -323,6 +345,7 @@ export async function proposeUpdateSkill(
createdAt: now,
updatedAt: now,
createdBy: input.createdBy ?? "skill-workshop",
...(origin ? { origin } : {}),
proposedVersion: "v1",
draftFile: "PROPOSAL.md",
draftHash: hashSkillProposalContent(proposalContent),

View File

@@ -627,6 +627,7 @@ function parseSkillProposalRecord(raw: unknown): SkillProposalRecord | null {
typeof record.updatedAt !== "string" ||
typeof record.draftHash !== "string" ||
record.draftFile !== PROPOSAL_DRAFT_FILE ||
!isValidProposalOrigin(record.origin) ||
!isValidSupportFileList(record.supportFiles) ||
!record.target ||
typeof record.target !== "object" ||
@@ -642,6 +643,23 @@ function parseSkillProposalRecord(raw: unknown): SkillProposalRecord | null {
return record;
}
function isValidProposalOrigin(value: unknown): boolean {
if (value === undefined) {
return true;
}
if (!value || typeof value !== "object" || Array.isArray(value)) {
return false;
}
const origin = value as Record<string, unknown>;
for (const key of ["agentId", "sessionKey", "runId", "messageId"]) {
const item = origin[key];
if (item !== undefined && typeof item !== "string") {
return false;
}
}
return true;
}
function isValidSupportFileList(value: unknown): boolean {
if (value === undefined) {
return true;

View File

@@ -11,6 +11,13 @@ export type SkillProposalStatus = "pending" | "applied" | "rejected" | "quaranti
export type SkillProposalScannerState = "pending" | "clean" | "failed" | "quarantined";
export type SkillProposalSource = "skill-workshop" | "cli" | "gateway";
export type SkillProposalOrigin = {
agentId?: string;
sessionKey?: string;
runId?: string;
messageId?: string;
};
export type SkillProposalScan = {
state: SkillProposalScannerState;
scannedAt: string;
@@ -47,6 +54,7 @@ export type SkillProposalRecord = {
createdAt: string;
updatedAt: string;
createdBy: SkillProposalSource;
origin?: SkillProposalOrigin;
proposedVersion: string;
draftFile: "PROPOSAL.md";
draftHash: string;
@@ -110,6 +118,7 @@ export type SkillProposalCreateInput = {
content: string;
supportFiles?: SkillProposalSupportFileInput[];
createdBy?: SkillProposalSource;
origin?: SkillProposalOrigin;
goal?: string;
evidence?: string;
};
@@ -122,6 +131,7 @@ export type SkillProposalUpdateInput = {
content: string;
supportFiles?: SkillProposalSupportFileInput[];
createdBy?: SkillProposalSource;
origin?: SkillProposalOrigin;
goal?: string;
evidence?: string;
};