吸收数字员工技能策略开关
This commit is contained in:
@@ -41,6 +41,7 @@ declare global {
|
||||
getRuntimeRecords?: () => Promise<QimingclawRuntimeRecords>;
|
||||
getUiState?: () => Promise<AdapterState>;
|
||||
getSkillCapabilities?: () => Promise<QimingclawSkillCapabilities>;
|
||||
setSkillEnabled?: (skillId: string, enabled: boolean) => Promise<{ ok?: boolean; skill_id?: string; enabled?: boolean; error?: string }>;
|
||||
getPlanTemplates?: () => Promise<QimingclawPlanTemplateList>;
|
||||
listMemories?: (options?: { query?: string; category?: string; limit?: number }) => Promise<QimingclawMemoryRecord[]>;
|
||||
addMemory?: (input: QimingclawMemoryUpsertInput) => Promise<QimingclawMemoryRecord | null>;
|
||||
@@ -697,7 +698,7 @@ export async function handleQimingclawDigitalApi<T>(
|
||||
if (method === 'POST' && pathname.startsWith('/api/skill/') && pathname.endsWith('/enabled')) {
|
||||
const skillId = decodeURIComponent(pathname.split('/')[3] || '');
|
||||
const body = parseJsonBody<{ enabled?: boolean }>(options.body);
|
||||
return { ok: true, skill_id: skillId, enabled: body.enabled ?? true } as T;
|
||||
return await setBridgeSkillEnabled(skillId, body.enabled ?? true) as T;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -768,7 +769,34 @@ async function readBridgeSkillCapabilities(): Promise<SkillSpec[]> {
|
||||
}
|
||||
|
||||
async function buildSkills(): Promise<SkillSpec[]> {
|
||||
return dedupeSkills([...SKILLS, ...await readBridgeSkillCapabilities()]);
|
||||
const bridgeSkills = await readBridgeSkillCapabilities();
|
||||
return dedupeSkills([...bridgeSkills, ...SKILLS]);
|
||||
}
|
||||
|
||||
async function setBridgeSkillEnabled(
|
||||
skillId: string,
|
||||
enabled: boolean,
|
||||
): Promise<{ ok: boolean; skill_id: string; enabled: boolean; error?: string }> {
|
||||
const bridge = window.QimingClawBridge?.digital;
|
||||
if (!bridge?.setSkillEnabled) {
|
||||
return { ok: false, skill_id: skillId, enabled, error: 'qimingclaw_bridge_unavailable' };
|
||||
}
|
||||
try {
|
||||
const result = await bridge.setSkillEnabled(skillId, enabled);
|
||||
return {
|
||||
ok: result?.ok !== false,
|
||||
skill_id: result?.skill_id ?? skillId,
|
||||
enabled: result?.enabled ?? enabled,
|
||||
...(result?.error ? { error: result.error } : {}),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
ok: false,
|
||||
skill_id: skillId,
|
||||
enabled,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function dedupeSkills(skills: SkillSpec[]): SkillSpec[] {
|
||||
@@ -2808,12 +2836,16 @@ async function handleIngressRoute(
|
||||
const idSource = body.idempotency_key || correlationId;
|
||||
const contextRefs = buildIngressContextRefs(body);
|
||||
const classification = classifyIngressRoute(body, contextRefs, snapshot);
|
||||
const filteredCandidates = await filterRouteCandidateSkills(classification.candidateSkills);
|
||||
const reasonCodes = filteredCandidates.allCandidatesDisabled
|
||||
? [...classification.reasonCodes, 'candidate_skills_disabled']
|
||||
: classification.reasonCodes;
|
||||
const decision: RouteDecisionRecord = {
|
||||
id: `route-decision-${slugifyIdPart(idSource)}`,
|
||||
route_kind: classification.routeKind,
|
||||
intent_kind: classification.intentKind,
|
||||
reason_codes: classification.reasonCodes,
|
||||
candidate_skills: classification.candidateSkills,
|
||||
reason_codes: reasonCodes,
|
||||
candidate_skills: filteredCandidates.candidateSkills,
|
||||
context_refs: contextRefs,
|
||||
created_command_id: null,
|
||||
correlation_id: correlationId,
|
||||
@@ -2950,12 +2982,43 @@ function routeCandidateSkills(routeKind: string, contextRefs: Record<string, unk
|
||||
stringValue(payload.skill_id),
|
||||
]);
|
||||
if (explicit.length > 0) return explicit;
|
||||
if (routeKind === 'PlanControl') return ['qimingclaw-task-agent'];
|
||||
if (routeKind === 'ProjectionQuery') return ['qimingclaw-artifact-report'];
|
||||
if (routeKind === 'PlanControl') return ['qimingclaw-acp-session'];
|
||||
if (routeKind === 'ProjectionQuery') return ['qimingclaw-artifact-reporting'];
|
||||
if (routeKind === 'PlanExecution') return ['qimingclaw-computer-control'];
|
||||
return [];
|
||||
}
|
||||
|
||||
async function filterRouteCandidateSkills(
|
||||
candidateSkills: string[],
|
||||
): Promise<{ candidateSkills: string[]; allCandidatesDisabled: boolean }> {
|
||||
if (candidateSkills.length === 0) {
|
||||
return { candidateSkills, allCandidatesDisabled: false };
|
||||
}
|
||||
const bridge = window.QimingClawBridge?.digital;
|
||||
if (!bridge?.getSkillCapabilities) {
|
||||
return { candidateSkills, allCandidatesDisabled: false };
|
||||
}
|
||||
try {
|
||||
const result = await bridge.getSkillCapabilities();
|
||||
if (!Array.isArray(result?.skills)) {
|
||||
return { candidateSkills, allCandidatesDisabled: false };
|
||||
}
|
||||
const enabledBySkillId = new Map<string, boolean>();
|
||||
for (const skill of result.skills) {
|
||||
const skillId = stringValue(skill.skill_id);
|
||||
if (skillId) enabledBySkillId.set(skillId, skill.enabled !== false);
|
||||
}
|
||||
const enabledCandidates = candidateSkills.filter((skillId) => enabledBySkillId.get(skillId) !== false);
|
||||
return {
|
||||
candidateSkills: enabledCandidates,
|
||||
allCandidatesDisabled: candidateSkills.length > 0 && enabledCandidates.length === 0,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn('[qimingclawAdapter] filter route candidate skills failed:', error);
|
||||
return { candidateSkills, allCandidatesDisabled: false };
|
||||
}
|
||||
}
|
||||
|
||||
function uniqueStrings(values: unknown[]): string[] {
|
||||
return [...new Set(values.map(stringValue).filter(Boolean))];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user