feat: 为 gitbundle 装配 required skills (#138)
* Add gitbundle required skills validation * fix: 限定 required skill blocked result 覆盖 --------- Co-authored-by: AgentRun Codex <agentrun@example.invalid> Co-authored-by: Codex <codex@pikas.tech>
This commit is contained in:
@@ -8,6 +8,7 @@ export type FailureKind =
|
||||
| "secret-unavailable"
|
||||
| "prompt-unavailable"
|
||||
| "prompt-too-large"
|
||||
| "required-skill-unavailable"
|
||||
| "skill-unavailable"
|
||||
| "runner-lease-conflict"
|
||||
| "backend-failed"
|
||||
@@ -81,6 +82,9 @@ export interface ResourceBundleRef extends JsonRecord {
|
||||
inject?: "thread-start";
|
||||
required?: boolean;
|
||||
}>;
|
||||
requiredSkills?: Array<{
|
||||
name: string;
|
||||
}>;
|
||||
submodules?: false;
|
||||
lfs?: false;
|
||||
credentialRef?: SecretRef;
|
||||
|
||||
@@ -91,6 +91,7 @@ export function validateResourceBundleRef(value: unknown): ResourceBundleRef | n
|
||||
rejectLegacyResourceBundleFields(record);
|
||||
const result: ResourceBundleRef = { kind: "gitbundle", repoUrl, ...(commitId ? { commitId } : {}), ...(ref ? { ref } : {}), bundles: validateResourceGitBundles(record.bundles, repoUrl, commitId, ref) };
|
||||
if (record.promptRefs !== undefined) result.promptRefs = validateResourcePromptRefs(record.promptRefs);
|
||||
if (record.requiredSkills !== undefined) result.requiredSkills = validateResourceRequiredSkills(record.requiredSkills);
|
||||
if (record.submodules !== undefined && record.submodules !== false) throw new AgentRunError("schema-invalid", "resourceBundleRef.submodules can only be false in v0.1", { httpStatus: 400 });
|
||||
if (record.lfs !== undefined && record.lfs !== false) throw new AgentRunError("schema-invalid", "resourceBundleRef.lfs can only be false in v0.1", { httpStatus: 400 });
|
||||
if (record.submodules === false) result.submodules = false;
|
||||
@@ -160,6 +161,22 @@ function validateResourcePromptRefs(value: unknown): NonNullable<ResourceBundleR
|
||||
});
|
||||
}
|
||||
|
||||
function validateResourceRequiredSkills(value: unknown): NonNullable<ResourceBundleRef["requiredSkills"]> {
|
||||
if (!Array.isArray(value)) throw new AgentRunError("schema-invalid", "resourceBundleRef.requiredSkills must be an array", { httpStatus: 400 });
|
||||
if (value.length > 32) throw new AgentRunError("schema-invalid", "resourceBundleRef.requiredSkills must contain at most 32 entries", { httpStatus: 400 });
|
||||
const seen = new Set<string>();
|
||||
return value.map((entry, index) => {
|
||||
const record = asRecord(entry, `resourceBundleRef.requiredSkills[${index}]`);
|
||||
const allowedKeys = new Set(["name"]);
|
||||
const extraKeys = Object.keys(record).filter((key) => !allowedKeys.has(key));
|
||||
if (extraKeys.length > 0) throw new AgentRunError("schema-invalid", `resourceBundleRef.requiredSkills[${index}] only supports name`, { httpStatus: 400, details: { rejectedKeys: extraKeys.sort(), valuesPrinted: false } });
|
||||
const name = validateResourceName(requiredString(record, "name"), `resourceBundleRef.requiredSkills[${index}].name`);
|
||||
if (seen.has(name)) throw new AgentRunError("schema-invalid", `resourceBundleRef.requiredSkills name ${name} is duplicated`, { httpStatus: 400 });
|
||||
seen.add(name);
|
||||
return { name };
|
||||
});
|
||||
}
|
||||
|
||||
function validateResourceName(name: string, fieldName: string): string {
|
||||
if (!/^[a-z][a-z0-9._-]{0,62}$/u.test(name)) throw new AgentRunError("schema-invalid", `${fieldName} must be a lowercase resource name`, { httpStatus: 400 });
|
||||
return name;
|
||||
|
||||
Reference in New Issue
Block a user